Create contextmenus for datagrid rows
As far as I know, some of the actions will be disabled or enabled depending on the row, so there is no point in a single ContextMenu
for a DataGrid
.
I have an example of the row-level context menu.
<UserControl.Resources> <ContextMenu x:Key="RowMenu" DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}"> <MenuItem Header="Edit" Command="{Binding EditCommand}"/> </ContextMenu> <Style x:Key="DefaultRowStyle" TargetType="{x:Type DataGridRow}"> <Setter Property="ContextMenu" Value="{StaticResource RowMenu}" /> </Style></UserControl.Resources><DataGrid RowStyle="{StaticResource DefaultRowStyle}"/>
The DataGrid
must have a binding to a list of view models with commands:
public class ItemModel{ public ItemModel() { this.EditCommand = new SimpleCommand { ExecuteDelegate = _ => MessageBox.Show("Execute"), CanExecuteDelegate = _ => this.Id == 1 }; } public int Id { get; set; } public string Title { get; set; } public ICommand EditCommand { get; set; }}
The context menu is created in the resources collection of the UserControl
and I think there is only one object which is connected with datagrid rows by reference, not by value.
Here is another example of ContextMenu
for a Command
inside a MainViewModel
. I suppose that DataGrid
has a correct view model as the DataContext
, also the CommandParameter attribute must be placed before the Command attribute:
<ContextMenu x:Key="RowMenu" DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}"> <MenuItem Header="Edit" CommandParameter="{Binding}" Command="{Binding DataContext.DataGridActionCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid}}" /> </ContextMenu>
Models:
public class MainViewModel{ public MainViewModel() { this.DataGridActionCommand = new DelegateCommand<ItemModel>(m => MessageBox.Show(m.Title), m => m != null && m.Id != 2); } public DelegateCommand<ItemModel> DataGridActionCommand { get; set; } public List<ItemModel> Items { get; set; }}public class ItemModel{ public int Id { get; set; } public string Title { get; set; }}
But there is a problem that MenuItem
isn't displayed as a disabled item if CanExecute
returns false. The possible workaround is using a ParentModel
property inside the ItemModel
, but it doesn't differ much from the first solution.Here is example of above-described solution:
public class ItemModel{ public int Id { get; set; } public string Title { get; set; } public MainViewModel ParentViewModel { get; set; }}//Somewhere in the code-behind, create the main view model //and force child items to use this model as a parent modelvar mainModel = new MainViewModel { Items = items.Select(item => new ItemViewModel(item, mainModel)).ToList()};
And MenuItem in XAML will be simplier:
<MenuItem Header="Edit" CommandParameter="{Binding}" Command="{Binding ParentViewModel.DataGridActionCommand}" />