Execute the Command in Listview MenuItem MVVM - c#

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>

Related

Bind ContextMenu Item inside Button to ViewModel

I'm trying to add a ContextMenu with an ItemMenu to a button
<Button x:Name="RemoteMachine" Command="{Binding ElementName=RemoteMachines, Path=DataContext.RemoteMachineSelectedCommand}" CommandParameter="{Binding}">
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Delete" CommandParameter="{Binding}"
Command="{Binding ElementName=RemoteMachines, Path=DataContext.DeleteRemoteMachineCommand}"/>
</ContextMenu>
</Button.ContextMenu>
</Button>
In my model I have the following
public ICommand RemoteMachineSelectedCommand => new CommandHandler(p => MachineSelectedAction(p, true), true);
public ICommand DeleteRemoteMachineCommand => new CommandHandler(p => DeleteRemoteMachineAction(p), true);
The Button command works correctly while the ContextMenu one doesn't.
I suppose I'm binding it someway wrong.
Any suggestion?
You could bind the Tag property of the Button to the command and then bind to it from the MenuItem using the PlacementTarget property of the parent ContextMenu:
<Button x:Name="RemoteMachine"
Tag="{Binding ElementName=RemoteMachines, Path=DataContext.DeleteRemoteMachineCommand}"
Command="{Binding ElementName=RemoteMachines, Path=DataContext.RemoteMachineSelectedCommand}"
CommandParameter="{Binding}">
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Delete"
CommandParameter="{Binding PlacementTarget.CommandParameter,
RelativeSource={RelativeSource AncestorType=ContextMenu}}"
Command="{Binding PlacementTarget.Tag,
RelativeSource={RelativeSource AncestorType=ContextMenu}}"/>
</ContextMenu>
</Button.ContextMenu>
</Button>

How to bind command click of MenuContextItem from one User Control to Other in WPF

In my code I have following User Controls
MyListUserControl
MyListItemUserControl
MyListUserControl loads list with items loaded from MyListItemUserControl,
on MyListItemUserControl I have a button on click of that I'm showing ContextMenu with multiple options.
Now on click of that MenuItem I have to call method from MyListUserControl, is it possible to do that?
In short following is the structure that I have
MyListUserControl -> MyListItemUserControl -> ContextMenu Item Click -> Command from MyListUserControl
If I write ListItem code in same ListUserControl its working fine. But I would prefer the item and list code in separate classes.
MyListUserControl xaml Code
<ListView ItemsSource="{Binding Data}">
<ListView.ItemTemplate>
<DataTemplate>
<local1:MyListItemUserControl HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch"
CommentData="{Binding}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I have DelegateCommand Defined in this class, which I would like to call from MyListItemUserControl
MyListItemUserControl xaml
<ContextMenu>
<MenuItem Command="{Binding Source={StaticResource Proxy}, Path=Data.MenuItem_Clicked}"
Header="{x:Static propertyRes:Resources.Txt_Copy}"
Style="{StaticResource menuItems}" />
<MenuItem Header="{x:Static propertyRes:Resources.Txt_Edit}" Style="{StaticResource menuItems}" />
<MenuItem Header="{x:Static propertyRes:Resources.Txt_Delete}" Style="{StaticResource menuItems}" />
</ContextMenu>
this is pseudo code, but should be enough for you to get the idea
<MyListItemUserControl>
<MyListItemUserControl.ItemTemplate>
<Grid>
<Grid.ContextMenu>
<ContextMenu>
<ContextMenuItem
Header="Do stuff"
Command="{Binding ElementName=DetailsView, Path=DataContext.DoStuffCommand"
CommandParameter="{Binding}"/>
</ContextMenu>
</Grid.ContextMenu>
<!-- your content -->
</Grid>
</<MyListItemUserControl.ItemTemplate>
</MyListItemUserControl>
<MyListItemUserControl x:Name="DetailsView">
<!-- I assume that data context is MyListItemViewModel or something similar
it has the command: DataContext.DoStuff that takes items from list as parameter -->
</MyListItemUserControl>
EDIT:
If your controls are defined in seperate files you just need to pass the commnad. I usually do this by adding a dependency property in code behind that holds the command:
public static readonly DependencyProperty DoStuffCommandProperty = DependencyProperty.Register(
"DoStuffCommand", typeof(ICommand), typeof(MainWindow), new PropertyMetadata(default(ICommand)));
public ICommand DoStuffCommand
{
get { return (ICommand) GetValue(DoStuffCommandProperty); }
set { SetValue(DoStuffCommandProperty, value); }
}
so you can do:
<ListView ItemsSource="{Binding Data}">
<ListView.ItemTemplate>
<DataTemplate>
<local1:MyListItemUserControl HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch"
CommentData="{Binding}" DoStuffCommand="{Binding DoStuffCommandInSomeViewModel}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

Why is my CommandParameter null when I have the Path as SelectedItem?

So I am trying to pass the SelectedItem as a parameter so that I can make use of the data that is bound to it.
Essentially I want to open a MessageBox and display the Name property of the User that is bound that item.
This is my xaml
<ItemsControl ItemsSource="{Binding CardViewModel.Users}"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.UseDefaultEffectDataTemplate="True">
<ItemsControl.ItemTemplate>
<DataTemplate>
<controls:UserCard>
<controls:UserCard.ContextMenu>
<!-- Bind the DataContext of the CM to the DataContext that's bound to the RootObject-->
<ContextMenu DataContext="{Binding DataContext, Source={local:RootObject}}">
<MenuItem Header="Edit"
Command="{Binding CardViewModel.EditUser}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}},
Path=PlacementTarget.SelectedItem}"/>
</ContextMenu>
</controls:UserCard.ContextMenu>
</controls:UserCard>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The command works fine everything is bound just fine besides that when I click the MenuItem and it fires the command, I put a breakpoint where the action is and it shows the parameter as null I am suspecting that it's me who is binding it wrong.
public void DisplayEditUser(object user)
{
if (user != null)
{
MessageBox.Show("Not null");
}
}
The problem is that ContextMenu.PlacementTarget wasn't the ItemsControl but the UserCard, so binding source resolving will absolutely fail. To solve it, you need to bind ItemsControl.SelectedItem to one property of UserCard such as Tag as a relay.
<controls:UserCard Tag="{Binding SelectedItem, RelativeSource={RelativeSource AncestorType=ListBox}}">
CommandParameter="{Binding PlacementTarget.Tag, RelativeSource={RelativeSource AncestorType=ContextMenu}}"

WPF parent child relation in binding

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}"/>

WPF: Binding a ContextMenu to an MVVM Command

Let's say I have a Window with a property returning a Command (in fact, it's a UserControl with a Command in a ViewModel class, but let's keep things as simple as possible to reproduce the problem).
The following works:
<Window x:Class="Window1" ... x:Name="myWindow">
<Menu>
<MenuItem Command="{Binding MyCommand, ElementName=myWindow}" Header="Test" />
</Menu>
</Window>
But the following does not work.
<Window x:Class="Window1" ... x:Name="myWindow">
<Grid>
<Grid.ContextMenu>
<ContextMenu>
<MenuItem Command="{Binding MyCommand, ElementName=myWindow}" Header="Test" />
</ContextMenu>
</Grid.ContextMenu>
</Grid>
</Window>
The error message I get is
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'ElementName=myWindow'. BindingExpression:Path=MyCommand; DataItem=null; target element is 'MenuItem' (Name=''); target property is 'Command' (type 'ICommand')
Why? And how do I fix this? Using the DataContext is not an option, since this problem occurs way down the visual tree where the DataContext already contains the actual data being displayed. I already tried using {RelativeSource FindAncestor, ...} instead, but that yields a similar error message.
The problem is that the ContextMenu it not in the visual tree, so you basically have to tell the Context menu about which data context to use.
Check out this blogpost with a very nice solution of Thomas Levesque.
He creates a class Proxy that inherits Freezable and declares a Data dependency property.
public class BindingProxy : Freezable
{
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
public object Data
{
get { return (object)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
}
Then it can be declared in the XAML (on a place in the visual tree where the correct DataContext is known):
<Grid.Resources>
<local:BindingProxy x:Key="Proxy" Data="{Binding}" />
</Grid.Resources>
And used in the context menu outside the visual tree:
<ContextMenu>
<MenuItem Header="Test" Command="{Binding Source={StaticResource Proxy}, Path=Data.MyCommand}"/>
</ContextMenu>
Hurray for web.archive.org! Here is the missing blog post:
Binding to a MenuItem in a WPF Context Menu
Wednesday, October 29, 2008 — jtango18
Because a ContextMenu in WPF does not exist within the visual tree of
your page/window/control per se, data binding can be a little tricky.
I have searched high and low across the web for this, and the most
common answer seems to be “just do it in the code behind”. WRONG! I
didn’t come in to the wonderful world of XAML to be going back to
doing things in the code behind.
Here is my example to that will allow you to bind to a string that
exists as a property of your window.
public partial class Window1 : Window
{
public Window1()
{
MyString = "Here is my string";
}
public string MyString
{
get;
set;
}
}
<Button Content="Test Button" Tag="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}}">
<Button.ContextMenu>
<ContextMenu DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}" >
<MenuItem Header="{Binding MyString}"/>
</ContextMenu>
</Button.ContextMenu>
</Button>
The important part is the Tag on the button(although you could just as
easily set the DataContext of the button). This stores a reference to
the parent window. The ContextMenu is capable of accessing this
through it’s PlacementTarget property. You can then pass this context
down through your menu items.
I’ll admit this is not the most elegant solution in the world.
However, it beats setting stuff in the code behind. If anyone has an
even better way to do this I’d love to hear it.
I found out it wasn't working for me due to the menu item being nested, which mean I had to traverse up an extra "Parent" to find the PlacementTarget.
A better way is to find the ContextMenu itself as the RelativeSource and then just bind to the placement target of that. Also since the tag is the window itself, and your command is in the viewmodel, you need to have the DataContext set as well.
I ended up with something like this
<Window x:Class="Window1" ... x:Name="myWindow">
...
<Grid Tag="{Binding ElementName=myWindow}">
<Grid.ContextMenu>
<ContextMenu>
<MenuItem Command="{Binding PlacementTarget.Tag.DataContext.MyCommand,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=ContextMenu}}"
Header="Test" />
</ContextMenu>
</Grid.ContextMenu>
</Grid>
</Window>
What this means is that if you end up with a complicated context menu with submenus etc.. you don't need to keep adding "Parent" to each levels Commands.
-- EDIT --
Also came up with this alternative to set a tag on every ListBoxItem that binds to the Window/Usercontrol. I ended up doing this because each ListBoxItem was represented by their own ViewModel but I needed the menu commands to execute via the top level ViewModel for the control, but pass their the list ViewModel as a parameter.
<ContextMenu x:Key="BookItemContextMenu"
Style="{StaticResource ContextMenuStyle1}">
<MenuItem Command="{Binding Parent.PlacementTarget.Tag.DataContext.DoSomethingWithBookCommand,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=ContextMenu}}"
CommandParameter="{Binding}"
Header="Do Something With Book" />
</MenuItem>>
</ContextMenu>
...
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="ContextMenu" Value="{StaticResource BookItemContextMenu}" />
<Setter Property="Tag" Value="{Binding ElementName=thisUserControl}" />
</Style>
</ListView.ItemContainerStyle>
Based on HCLs answer, this is what I ended up using:
<Window x:Class="Window1" ... x:Name="myWindow">
...
<Grid Tag="{Binding ElementName=myWindow}">
<Grid.ContextMenu>
<ContextMenu>
<MenuItem Command="{Binding Parent.PlacementTarget.Tag.MyCommand,
RelativeSource={RelativeSource Self}}"
Header="Test" />
</ContextMenu>
</Grid.ContextMenu>
</Grid>
</Window>
If (like me) you have an aversion to ugly complex binding expressions, here is a simple code-behind solution to this problem. This approach still allows you to keep clean command declarations in your XAML.
XAML:
<ContextMenu ContextMenuOpening="ContextMenu_ContextMenuOpening">
<MenuItem Command="Save"/>
<Separator></Separator>
<MenuItem Command="Close"/>
...
Code behind:
private void ContextMenu_ContextMenuOpening(object sender, ContextMenuEventArgs e)
{
foreach (var item in (sender as ContextMenu).Items)
{
if(item is MenuItem)
{
//set the command target to whatever you like here
(item as MenuItem).CommandTarget = this;
}
}
}
Answer in 2020:
I'm leaving this answer here for anyone else who googled this question, as this is the first search result that shows up.
This worked for me and is simpler than the other suggested solutions:
<MenuItem Command="{Binding YourCommand}" CommandTarget="{Binding Path=PlacementTarget, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}"/>
As described here:
https://wpf.2000things.com/2014/06/19/1097-getting-items-in-context-menu-to-correctly-use-command-binding/

Categories