pass the window using a CommandParameter from a ContextMenu - c#

The goal is to hide/show a window from the task tray using Hardcoded WPF NotifyTrayIcon in a MVVM solution. The problem is the CommandParameter always seems to be null, which then of course cause the code to crash. I've tried a number of different bindings including:
CommandParameter="{Binding Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}}"
CommandParameter="{Binding ElementName=window, Mode=OneWay}"
etc but everything passes back a null, any suggestions?
Thanks

If you have only one Window then you could simply use Application.Current.MainWindow.
If you have more then you may search for the good one in the Application.Current.Windows collection.

Related

DataGridColumn - How to Bind the Visibility to DataGrid Width

i try to show the user some not so important columns of my datagrid, only when enough space(width of the whole program) available is. so i wanna bind the visibility of the column to the actual with of the datagrid with a converter.
all of my converters, who i wrote works well, BUT in this case happens nothing, means there is no call of the converter. i checked that out by my debug log.
<mui:DataGridTextColumn Header="Kilometer"
MinWidth="20"
Width="SizeToCells"
Binding="{Binding Path=Kilometer, TargetNullValue='-', FallbackValue='-'}"
Visibility="{Binding Path=ActualWidth, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}, TargetNullValue=Collapsed, FallbackValue=Collapsed, Converter={StaticResource IntAsLimit2VisibilityCollapsed}, ConverterParameter=1300}"
/>
made i something wrong? is it maybe in this combination not possible?
As stated here, DataGrid columns don't lie in Visual tree of DataGrid, and we can't get to outer DataContext using RelativeSource.
We can only use x:Reference to an external FrameworkElement.
Also, here is a similar question with a possible solution (you don't need to set a width and will probably avoid a circular reference).

Context menu binding never gets executed

I have ListView control in my view with it's own viewmodel A. I have made a seperate UserControl to use as ListViewItem, because it's styling takes a lot of space. In this ListViewItem I have a button, which is binded to viewmodel A and it works fine.
As the context menu has it's own visual tree and cannot bind via ancestor, I have used binding proxy, to solve this issue. I have tweaked it a little so it worked for my particular case, because if it just used {Binding} it would bind to item's model, not listview's viewmodel.
<helpers:BindingProxy x:Key="proxy" Data="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}}"/>
To check if the binding is correct I've used a converter as a way just to have a breakpoint to check source. Everything was good and I was getting my viewmodel right there.
Now, when I try to bind to this in my context menu
<UserControl.ContextMenu>
<ContextMenu>
<MenuItem Header="Open"
Command="{Binding DataContext.OpenChatCommand, Source={StaticResource proxy}, Converter={StaticResource DataBindingDebugConverter}}"
CommandParameter="{Binding}"/>
</ContextMenu>
</UserControl.ContextMenu>
The command never gets called. I added converter to see if something is wrong, but it turns out, I never get to my converter, which in turn means this code never gets executed.
Anyone with any ideas why this is happening and how to solve this is welcome.
I think it's the compiler malfunctioning though
I just did a brief readup on that "binding proxy" you mentioned, but as far as I know, DataGridTextColumn is in the same Visual Tree as its DataGrid, just that its DataContext is bound to its data.
For ContextMenu, it's totally different. This one really has a separate tree from its parent. There is no point in using a proxy object in resources, because it is from a different visual tree. When you use StaticResource, WPF will search upwards through its visual tree, level by level, inside those elements' Resource property (which is a ResourceDictionary).
One way is to make that proxy into a singleton, and use Source={x:Static helpers:BindingProxy.Instance}. Of course using this means that your proxy can only be used by a single View, or else something unexpected would happen.
The other way is to make use of PlacementTarget property of the ContextMenu.
<ContextMenu DataContext="{Binding Path=PlacementTarget.DataContext,
RelativeSource={RelativeSource Self}}">
This is the preferred way, but you need to make sure the parent's DataContext is really the VM that you need.
Edit
There is no super elegant way to do it the MVVM way. The best way is probably through the use of Tag property.
<ContextMenu DataContext="{Binding Path=PlacementTarget.Tag,
RelativeSource={RelativeSource Self}}">
ListView Control:
<MyControl:MyListViewItem .... Tag="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type MyControl:MyListViewView}}}"}" ...>

Pass Element as CommandParameter from root of ElementTree

I searched a lot through Google and StackOverflow, but nothing answered my problem.
I have two Xaml Files:
MainWindow.xaml
<Window x:Name="mainWindow">
<Window.DataContext>
<!-- Instantiate ViewModel of the MainWindow -->
<vm:MainWindowViewModel x:Name="viewModel"/>
</Window.DataContext>
<!-- Create the Menu of the MainWindow -->
<custom:MainMenu Grid.Row="0"/>
<ad:DockingManager x:Name="dockingManager">
<!-- ... -->
</Window>
And the MainMenu.xaml
<UserControl>
<Menu>
<MenuItem Header="{t:Translate MENU_LAYOUT_SAVE}" Command="{Binding SaveLayoutCommand}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=Window}}"/>
<MenuItem Header="{t:Translate MENU_LAYOUT_LOAD}" Command="{Binding LoadLayoutCommand}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=Window}}"/>
</Menu>
</UserControl>
And here my Problem occurs. Instead of passing the Mainwindow-object I want to pass the DockingManager x:Name="dockingManager" from MainWindow. But if i try to reference the object by its name it fails...
I tried the following Bindings:
CommandParameter="{Binding ElementName=dockingManager}"
CommandParameter="{Binding ElementName=dockingManager, RelativeSource={RelativeSource AncestorType=Window}}"
So how can I find and reference an Object (dockingManager) from the ElementTree within xaml. I want to avoid using extra code in Code-behind.
Try CommandParameter="{Binding ElementName=dockingManager, Path=.}".
EDIT:
The previous answer would not work. Here's a working idea...
In the Window.xaml:
<custom:MainMenu Grid.Row="0" Tag="{Binding ElementName=dockingManager}" />
In the MainMenu.xaml:
<UserControl x:Name="UcMainMenu" />
...
<MenuItem Header="{t:Translate MENU_LAYOUT_SAVE}" Command="{Binding SaveLayoutCommand}" CommandParameter="{Binding ElementName=UcMainMenu, Path=Tag}"/>
You can use:
CommandParameter="{x:Reference Name=yourElementName}"
Since you are using MVVM here is what you should do to come up with a slightly different solution:
Get rid of the CommandParameter
The command will trigger a callback in the MainWindowViewModel instance
This callback will change some state/properties in the MainWindowViewModel instance
The DockingManager instance reacts to that adjusted state of the MainWindowViewModel instance through bindings
The way you are doing it now is way too complicated. In addition to that, you are wildly mixing patterns here. MVVM tries to separate the business logic from the actual elements. You are using elements of MVVM with Smart UI/Code Behind techniques.
Also, consider using individual view models for individual controls. The main menu control is separate and the docking manager is, too. Why? Because you want to break everything into smaller pieces, but more importantly, because you might have reusability in mind. With the main menu trying to access a docking manager inside a Window that is not possible.

Is possible to make a WPF RelativeSource binding with AncestorType pointing to a base type?

I want to bind a property to the parent container view having a ViewModel in its DataContext.
This code works perfectly well when the parent is a direct instance of ConcreteClassView:
Property="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ty:ConcreteClassView}}, Path=DataContext.Name}"
However, the parent is not found when trying to locate it via a base class or a interface.
Sample:
PropertyB="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ty:BaseClassView}}, Path=DataContext.Name}"
PropertyB="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ty:INamedElementView}}, Path=DataContext.Name}"
Giving that:
class ConcreteClassView : BaseClassView, INamedElementView { }
Ok, Let's assume that FindAncestor, AncestorType needs the concrete type to work.
But there is any workaround to locate ancestors just based on base classes or implementing a given Interface?
Thxs.
FindAncestor, AncestorType do work with base classes, so your assumption is wrong.
Here is proof: This works
<HeaderedContentControl Tag="ABC">
<TextBlock Text="{Binding Tag, RelativeSource={RelativeSource AncestorType=ContentControl}}" />
</HeaderedContentControl>
It works also with interface (Button implements ICommandSource):
<Button Tag="ABC">
<TextBlock Text="{Binding Tag, RelativeSource={RelativeSource AncestorType=ICommandSource}}" />
</Button>
(Tested in .NET 4.5)
So why your code does not work?
There may be another element derived from ty:BaseClassView in your visual tree between the binding target and the element you are looking for.
This doesn't work:
<HeaderedContentControl Tag="ABC">
<Label>
<TextBlock Text="{Binding Tag, RelativeSource={RelativeSource AncestorType=ContentControl}}" />
</Label>
</HeaderedContentControl>
Label is also inherited from ContentControl, so Binding Source is Label in this case
Visual Tree may be disconnected. For example Popup control is part of Logical Tree, but it has its own visual tree, so you can't use RelativeSource FindAncestor inside popup to look for parents outside popup. Please note, that elements are removed from visual tree also when you set Visibility="Collapsed"
How to debug?
You can use converter to debug your binding. Just specify RelativeSource and some fake converter and leave the path empty. You can then place breakpoint to your converter, where value is be your binding source.
Use loaded event of your element with binding to write all visual parents to Debug window
EDIT: Now in Visual Studio 2015, you can use Live Visual Tree explorer, to inspect the visual tree at runtime, (similarly like Browsers' developers tools can inspect dom elements). Using this tool you should be able to find bug in your application within few seconds.
https://msdn.microsoft.com/en-us/library/mt270227.aspx

How to add UserControl to Canvas of MainView from multiple ViewModels in WPF MVVM

I have a MainWindowView(Window) with a Canvas in which I add my Views(UserControls).
The Canvas in the MainWindow is a Custom Canvas derived from Canvas so that Views inside this can be moved here and there, and can bringtofront or sendback.
I add Views to MainWindowView's Canvas by Binding a Command to a Button. So when I click a Button, a View gets added in the Canvas.
But, my problem is, I want to add another View to the same Canvas of MainViewModel from the ViewModel of my Views which are already in the Canvas of MainViewModel.
Since the ObservableCollection, which I used to bind Canvas, is in MainViewModel, I can add View from the MainViewModel only.
When I try to use the MainViewModel from other ViewModel, I have to create a new object of it, which makes the old View in the Canvas being replaced by the new one.
Is there a solution for this. If not what's the use of using MVVM framework.
Please help...
Use Calibrum Micro, which will help you in this
Am I getting this right : Your controls' DataContext is a Different one than that of the Window and you need to access it from there?
Basically that could have been avoided by design (use Dependency Injection to get the MainViewModel instance into the Command), but in fact there is a WPF/MVVM friendly way of solving this:
Use Commands to Add Controls to the MainViewModels ObservableCollection
<Button Command="{Binding Path=CreateViewCommand}" CommandParameter="{Binding}" />
From your Control (what you called View), you must use Ancestor Binding:
<Button Command="{Binding Path=DataContext.CreateViewCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"
CommandParameter="{Binding Path=DataContext, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"/>
In your Command, you can cast the parameter to its original Type (MainViewModel) and work with it as you wish.

Categories

Resources