WPF parent child relation in binding - c#

I am working on an application which is totally based on MVVM. I am facing a binding problem.
<ListView ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}},Path=DataContext.CurrentSecurityList}">
<ListView.ContextMenu>
<ContextMenu>
<MenuItem Header="Remove" Command="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type UserControl}},Path=DataContext.RemoveSecurity}"/>
</ContextMenu>
</ListView.ContextMenu>
ListView binding is working absolutely fine in this code but the problem comes when it comes to MenuItem Command Binding. Can someone explain what i am doing wrong over here.

Put a Tag in ListView to connect its ancestor to its ContextMenu:
<ListView ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}},Path=DataContext.CurrentSecurityList}"
Tag="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}">
<ListView.ContextMenu>
<ContextMenu>
<MenuItem Header="Remove" Command="{Binding PlacementTarget.Tag.DataContext.RemoveSecurity, RelativeSource={RelativeSource
AncestorType=ContextMenu}}"/>
</ContextMenu>
</ListView.ContextMenu>
</ListView>

ContextMenu works on different visual tree so it is not possible to bind it like that. You need to find ContextMenu ancestor and refer to its PlacementTarget.DataContext to retrieve your command. Try something like this:
<MenuItem Header="Remove" Command="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type ContextMenu}},Path=PlacementTarget.DataContext.RemoveSecurity}"/>

Related

Execute the Command in Listview MenuItem MVVM

Why does the Command in my Listview Menu Item is not executing?
This is the code on my Listview
<ListView ItemsSource="{Binding ListDataCorrection}" >
<ListView.View>
<GridView>
<GridViewColumn Header="Validate">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button Content="Update" Margin="5" Cursor="Hand" Command="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ListView}}, Path=DataContext.ValidateCommand}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
<ListView.ContextMenu>
<ContextMenu>
<MenuItem Header="Remove" Command="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ListView}}, Path=DataContext.ValidateAllCommand}">
</MenuItem>
</ContextMenu>
</ListView.ContextMenu>
</ListView>
But the weird thing is the ValidateCommand inside the Gridview is executed.
While the Command in the MenuItem is not.
What's is wrong with my Binding?
And i also checked if the Command name is correct. If not i should receive an error saying that the command is not found in the ViewModel
Thank you.
I'm having this problem too sometimes with MenuItems inside a ContextMenu. I guess that the ContextMenu can't find the correct DataContext.
My soltuion for this is a BindingProxy-class which looks like:
public class BindingProxy : Freezable
{
public static readonly DependencyProperty DataProperty = DependencyProperty.Register(
"Data", typeof (object), typeof (BindingProxy), new UIPropertyMetadata(null));
/// <summary>
/// This Property holds the DataContext
/// </summary>
public object Data
{
get { return GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
}
In the resources of your view (UserControl or Window) you have to add the proxy like:
<codeBase:BindingProxy x:Key="proxy" Data="{Binding}"/>
And in your MenuItem you can use it with:
<MenuItem Header="Remove" Command="{Binding Source={StaticResource proxy}, Path=Data.ValidateAllCommand}"/>
A Menu (as well as a Popup for example) is not part of the visual-tree, as it is created on demand. Since its is not part of the visual-tree, it will not inherit its parents DataContext. However you can still bind to your ListView using the PlacementTarget-property in your binding:
<ListView.ContextMenu>
<ContextMenu>
<MenuItem Header="Remove"
Command="{Binding Path=PlacementTarget.DataContext.ValidateAllCommand}">
</MenuItem>
</ContextMenu>
</ListView.ContextMenu>
Why does the Command in my Listview Menu Item is not executing?
Because the ListView is not a visual ancestor of the MenuItem so the RelativeSource of the binding isn't found.
If you change the AncestorType to ContextMenu and bind to its PlacementTarget, it should work:
<ContextMenu>
<MenuItem Header="Remove" Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}},
Path=PlacementTarget.DataContext.ValidateAllCommand}">
</MenuItem>
</ContextMenu>

Binding in ResourceDictionary not working

I have a ContextMenu in a ResourceDictionary. The ContextMenu should hide or show depending on the value of a view-model property, but it does not work.
This is my XAML code (ControlBase derives from UserControl):
<control1:ControlBase>
<UserControl.Resources>
<ResourceDictionary>
<HierarchicalDataTemplate ItemsSource="{Binding InfraNetworkItems}">
<StackPanel>
<StackPanel.ContextMenu>
<ContextMenu DataContext="{Binding PlacementTarget.DataContext,
RelativeSource={RelativeSource Self}}">
<MenuItem Header="Delete"
Visibility="{Binding
DataContext.MyViewModel.DeleteEnabled,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=control1:ControlBase},
Converter={StaticResource
BooleanVisibilityConverter}}" />
</ContextMenu>
</StackPanel.ContextMenu>
</StackPanel>
</HierarchicalDataTemplate>
</ResourceDictionary>
</UserControl.Resources>
</control1:ControlBase>
DeleteEnabled is a bool property on the view-model.
My previous attempts of solving the problem are based on this assumptions:
The ContextMenu is inside a HierarchicalDataTemplate which has the ItemsSource set. My property is not a member of this ItemSource, it belongs to the view-model. Therefore I've tried this line of code, but without any effect:
Visibility="{Binding DataContext.MyViewModel.DeleteEnabled,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=control1:ControlBase},
Converter={StaticResource BooleanVisibilityConverter}}"
But if I copy the DeleteEnabled property from the view-model to the ItemSource object, it works:
Visibility="{Binding DeleteEnabled, Converter={StaticResource BooleanVisibilityConverter}}"
what is the DataContext of your view? If it's an instance of MyViewModel you have to change the path of your Binding.
Please try this one:
<Visibility="{Binding DataContext.DeleteEnabled, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=control1:ControlBase}, Converter={StaticResource BooleanVisibilityConverter}}" />
With setting the path to DataContext you already have access to your viewmodel and of course to the DeleteEnabled-Property.
Hope this helps.

MenuItem + CommandParameter = null ! Why?

I trying to pass a parameter SelectedItems to command, but parameter is always null.
<sd:SharpTreeView Name="MyTreeView" Margin="10,38,120,10" Root="{Binding Tests}" ShowAlternation="True" SelectionMode="Multiple">
<sd:SharpTreeView.ContextMenu>
<ContextMenu>
<MenuItem
Header="Copy"
CommandParameter="{Binding ElementName=MyTreeView, Path=SelectedItems}"
Command="{Binding CopySelectedTests}" />
</ContextMenu>
</sd:SharpTreeView.ContextMenu>
<ListView.View>
...
I new in WPF, so for me it is was not helpful to read answers from here and from others questions. It always remain null.
I have tried it not in ContextMenu and it works fine, but it is not what I want.
EDIT 1
I tried this:
CommandParameter="{Binding Path=UIElement.(sd:SharpTreeView.SelectedItems), RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}"
this:
CommandParameter="{Binding Path=SelectedItems, RelativeSource={RelativeSource Mode=FindAncestor, AncestorLevel=1, AncestorType={x:Type ContextMenu}}}"
and many other, it is always null.
It only does not null for this:
CommandParameter="{Binding}"
But it is not what I need.
I finally solved it:
CommandParameter="{Binding
Path=PlacementTarget.SelectedItems,
RelativeSource=
{RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}
}"
The important part was to specify PlacementTarget.

WPF Datagrid ContextMenu SelectedItem

I have the following problem.
I have a DataGrid with a ContextMenu. But when the Command is triggered the SelectedItem is always null. Any recommendations?
Thats my ContextMenu:
<DataGrid.ContextMenu>
<ContextMenu>
<MenuItem Command="{Binding OpenFileCommand}"
CommandParameter="{Binding Path=SelectedItem,
RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"
Header="open file" />
i also tried:
CommandParameter="{Binding ElementName=nameOfGrid, Path=SelectedItem}"
Thanks for your help. I fixed it this way: CommandParameter="{Binding PlacementTarget.SelectedItem,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ContextMenu}}}"
I think your answer is here: CommandParameters in ContextMenu in WPF
Another better option imho is to keep "SelectedItem" binded in your ViewModel.
Then you don't need a command parameter anymore, and you can juste use the SelectedItem binded from your ViewModel.
The problem is that the ContextMenu is in a different VisualTree.
You could use the Tag for binding the Command. See the following link

In Button Click Context Menu How do I bind to viewModel?

I have a button on click on that button opening a context menu, now clicking on the context menus is to be binded to viewModel. But its not happening.
<Button Content="Copy" Tag="{Binding LinkViewModel, RelativeSource={RelativeSource Mode=Self}}" Command="{Binding LinkCopyCommand, UpdateSourceTrigger=PropertyChanged}" >
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Copy Download link " Command="{Binding Path=Parent.PlacementTarget.Tag.CopyViewCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}" />
<MenuItem ... />
</ContextMenu>
</Button.ContextMenu>
</Button>
I have tried the tag property but it seems to me that its not working. The viewmodel is working fine if I bind to the button itself, but the contextMenu dataBinding is not working.
EDIT:
Now as the code is working after discussion, I think to post it here.
What the changes I made is I put UpdateSourceTrigger="Propertychanged" here is the code
<Button Content="Copy" Tag="{Binding LinkViewModel, RelativeSource={RelativeSource Mode=Self}}" Command="{Binding LinkCopyCommand, UpdateSourceTrigger=PropertyChanged}" >
<Button.ContextMenu>
<ContextMenu Width="{Binding RelativeSource={RelativeSource Self}}">
<MenuItem Header="Copy View link " Command="{Binding CopyViewCommand, UpdateSourceTrigger=PropertyChanged}" />
<MenuItem ... />
</ContextMenu>
</Button.ContextMenu>
</Button>
However I don't know how come suddenly it works, it has to work with tag property in case of Button Context menu. If anybody put some light into this I think many people like me who are new WPF and data binding will be benefited.
I'm assuming you are using this Button inside the UserControl. Please try below code
<Button Content="Copy" Tag="{Binding LinkViewModel, RelativeSource={RelativeSource Mode=Self}}" Command="{Binding LinkCopyCommand, UpdateSourceTrigger=PropertyChanged}" >
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Copy Download link " Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.CopyViewCommand}" />
<MenuItem ... />
</ContextMenu>
</Button.ContextMenu>
</Button>
<Button Content="Copy" Command="{Binding LinkCopyCommand, UpdateSourceTrigger=PropertyChanged}" >
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Copy Download link " Command="{Binding Path=CopyViewCommand}" />
<MenuItem ... />
</ContextMenu>
</Button.ContextMenu>
CopyViewCommand Bound directly from your DataContext... which is your ViewModel..
You have to setyour ContextMenu's DataContext to your ViewModel. One way to do this is by having an Opened eventhandler for the context menu.
Take a look at my answer in the below link -
Context Menu items command binding WPF using MVVM

Categories