多くの行を持つ可能性のあるデータグリッドがあります。ユーザーが行の1つを右クリックすると、各行のコンテキストメニューを表示し、ユーザーがオプションをクリックしたときにアクション(同じアクションですが、現在選択されている行に応じて異なるデータ項目)を実行する必要があります。
このための最良の戦略は何ですか?
ContextMenuOpeningイベントを使用してメニューを作成しているにもかかわらず、各行のContextMenuが過剰になるのではないかと心配しています。これは、コンテキストメニューの「遅延読み込み」のようなものです。データグリッドに1つのContextMenuのみを使用する必要がありますか?しかし、これを使用すると、クリックイベントに関して、正しい行を決定するなどの作業がさらに必要になります。
私の知る限り、一部のアクションは行に応じて無効または有効になるため、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}" />