Create contextmenus for datagrid rows

前端 未结 1 1975
天命终不由人
天命终不由人 2020-11-30 06:45

I have a datagrid that potentially can have many rows. As the user right clicks one of the rows, I need to show a context menu for each of the rows and perform an action (sa

相关标签:
1条回答
  • 2020-11-30 07:45

    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 model
    var 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}" />
    
    0 讨论(0)
提交回复
热议问题