Wpf: Binding a Contextmenu to an Mvvm Command

WPF: Binding a ContextMenu to an MVVM Command

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>

WPF Context Menu command Binding

Have changed my XAML to,

<Window.Resources>
<local:ImageList x:Key="SliderViewModel"></local:ImageList>
</Window.Resources>

<Image Source="{Binding CurrentImage.Source, Mode=OneWay}" Grid.Row="0" Grid.Column="1">
<Image.ContextMenu>
<ContextMenu>
<MenuItem Header="Edit Image" Command="{Binding EditImageCommand, Source={StaticResource SliderViewModel}}"></MenuItem>
</ContextMenu>
</Image.ContextMenu>
</Image>

Working fine. Thanks

MVVM How to bind command to ContextMenu

You can't use RelativeSource in ContextMenu, because the menu is not a part of the visual tree. However this can be avoided by using Binding Source and x:Reference.

I assume your ViewModel looks like this

public class UserViewModel
{
public string Header { get; set; }
public ICommand MyCommand { get; }
... more code
}

Now let's bind Header and MyCommand properties of the VM

<ContextMenu x:Key="ContextMenu">
<ContextMenu.Items>
<MenuItem Header="{Binding Header, Source={x:Reference vm}}"
Command="{Binding MyCommand, Source={x:Reference vm}}"/>
</ContextMenu.Items>
</ContextMenu>

The important part is to have the ViewModel somewhere in the visual tree and set its x:Name, just like you've done in your example

<Page.DataContext>
<PDB:UsersViewModel x:Name="vm"/>
</Page.DataContext>

If you still want to know more about RelativeSource, this question seems to have the same problem as you. Basically Path of the binding has to be DataContext.MyViewModelProperty and the RelativeSource of the binding must be and element with DataContext set to the ViewModel.

MVVM binding command to contextmenu item

(Edit) Since you mentioned this is in an ItemsControl's template, things are different:

1) Get the BindingProxy class from this blog (and read the blog, as this is interesting information): How to bind to data when the DataContext is not inherited.

Basically the elements in the ItemsControl (or ContextMenu) are not part of the visual or logical tree, and therefore cannot find the DataContext of your UserControl. My apologies for not writing more on this here, but the author has done a good job explaining it step by step, so there's no way I could give a complete explanation in just a few lines.

2) Do something like this: (you may have to adapt it a bit to make it work in your control):

a. This will give you access to the UserControl DataContext using a StaticResource:

<UserControl.Resources>
<BindingProxy
x:Key="DataContextProxy"
Data="{Binding}" />
</UserControl.Resources>

b. This uses the DataContextProxy defined in (a):

<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Remove" CommandParameter="{Binding Name}"
Command="{Binding Path=Data.RemoveCommand, Source={StaticResource DataContextProxy}}"/>
</ContextMenu>

This has worked for us in things like trees and datagrids.

WPF binding command to ContextMenu

I had a similar problem before and solved it by passing the datacontext through the tag property of the container as below. I have it working on a grid ContextMenu but dont see any reason why this wont work on a button. Let me know if you have any problem

<Button  Content="qwerty" Tag="{Binding DataContext,ElementName=Lst}" Command="{Binding ElementName=Lst, Path=DataContext.SaveCommand}"  >
<Button.ContextMenu>
<ContextMenu DataContext="{Binding PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Send2" Command="{Binding SaveCommand}" />
</ContextMenu>
</Button.ContextMenu>
</Button>

How do I bind to a context MenuItem header in WPF MVVM?

You can only bind to properties, not fields.

public string HeaderText {get; set;}

Sample Image

Binding command of parent control to ContextMenu MenuItem

You can access the ViewModelLocator inside the ItemTemplate as well.

<DataGrid Grid.Column="0">
<DataGrid.ContextMenu>
<ContextMenu>
<MenuItem Header="Add template..." ItemsSource="{Binding JobTemplates}">
<MenuItem.ItemTemplate>
<DataTemplate>
<MenuItem Header="{Binding Name}" Command="{Binding Source={StaticResource Locator}, Path=Main.AddTemplateJobCommand}" CommandParameter="{Binding Name}"/>
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
</ContextMenu>
</DataGrid.ContextMenu>
</DataGrid>

A more general approach without MVVM light could be using a binding proxy.



Related Topics



Leave a reply



Submit