web-dev-qa-db-ja.com

データグリッド行のコンテキストメニューを作成する

多くの行を持つ可能性のあるデータグリッドがあります。ユーザーが行の1つを右クリックすると、各行のコンテキストメニューを表示し、ユーザーがオプションをクリックしたときにアクション(同じアクションですが、現在選択されている行に応じて異なるデータ項目)を実行する必要があります。

このための最良の戦略は何ですか?

ContextMenuOpeningイベントを使用してメニューを作成しているにもかかわらず、各行のContextMenuが過剰になるのではないかと心配しています。これは、コンテキストメニューの「遅延読み込み」のようなものです。データグリッドに1つのContextMenuのみを使用する必要がありますか?しかし、これを使用すると、クリックイベントに関して、正しい行を決定するなどの作業がさらに必要になります。

14
Jay

私の知る限り、一部のアクションは行に応じて無効または有効になるため、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のリソースコレクションで作成され、値ではなく参照によってデータグリッド行に接続されているオブジェクトは1つだけだと思います。

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がfalseを返すと、CanExecuteが無効なアイテムとして表示されないという問題があります。考えられる回避策は、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()};

また、XAMLのMenuItemはより単純になります。

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