Создание контекстных меню для строк datagrid

У меня есть datagrid, который потенциально может иметь много строк. Когда пользователь щелкает правой кнопкой мыши по одной из строк, мне нужно показать контекстное меню для каждой из строк и выполнить действие (такое же действие, но другой элемент данных в соответствии с текущей выбранной строкой), когда пользователь нажимает эту опцию.

Какова лучшая стратегия для этого?

Я боюсь, что ContextMenu для каждой строки слишком велико, хотя я создаю меню, используя событие ContextMenuOpening, вроде "ленивой загрузки" для контекстного меню. Должен ли я использовать только один ContextMenu для datagrid? Но с этим я бы сделал еще несколько работ относительно события click, чтобы определить правильную строку и т.д.

Ответы

Ответ 1

Насколько я знаю, некоторые действия будут отключены или активированы в зависимости от строки, поэтому нет смысла в одном ContextMenu для DataGrid.

У меня есть пример контекстного меню на уровне строк.

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

DataGrid должен иметь привязку к списку моделей просмотра с командами:

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; }
}

Контекстное меню создается в коллекции ресурсов UserControl, и я думаю, что есть только один объект, который связан с строками datagrid по ссылке, а не по значению.

Вот еще один пример ContextMenu для Command внутри a MainViewModel. Я полагаю, что DataGrid имеет правильную модель представления как DataContext, также атрибут CommandParameter должен быть помещен перед атрибутом Command:

    <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>

Модели:

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; }
}

Но есть проблема, что MenuItem не отображается как отключенный элемент, если CanExecute возвращает false. Возможное обходное решение использует свойство ParentModel внутри ItemModel, но оно не сильно отличается от первого решения. Ниже приведен пример вышеописанного решения:

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()};

И MenuItem в XAML будет проще:

<MenuItem Header="Edit" CommandParameter="{Binding}"
              Command="{Binding ParentViewModel.DataGridActionCommand}" />