Создать контекстное меню для строк сетки данных - PullRequest
14 голосов
/ 05 марта 2011

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

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

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

1 Ответ

34 голосов
/ 06 марта 2011

Насколько я знаю, некоторые действия будут отключены или включены в зависимости от строки, поэтому нет смысла в единственном 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, и я думаю, что есть только один объект, который связан со строками таблицы данных по ссылке, а не по значению.

Вот еще один пример ContextMenu для Command внутри 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}" />
...