【问题标题】:Create contextmenus for datagrid rows为数据网格行创建上下文菜单
【发布时间】:2011-07-09 05:05:12
【问题描述】:

我有一个可能有很多行的数据网格。当用户右键单击其中一行时,我需要为每一行显示一个上下文菜单,并在用户单击该选项时执行一个操作(相同的操作,但根据当前选定的行不同的数据项)。

对此最好的策略是什么?

我担心每一行的 ContextMenu 都会过大,即使我正在使用 ContextMenuOpening 事件创建菜单,有点像上下文菜单的“延迟加载”。我应该只对数据网格使用一个 ContextMenu 吗?但是有了这个,我会在点击事件、确定正确的行等方面做更多的工作。

【问题讨论】:

    标签: wpf xaml datagrid contextmenu


    【解决方案1】:

    据我所知,某些操作将根据行被禁用或启用,因此DataGrid 的单个ContextMenu 没有意义。

    我有一个行级上下文菜单的示例。

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

    但是如果CanExecute 返回false,则MenuItem 不会显示为禁用项目。可能的解决方法是在ItemModel 中使用ParentModel 属性,但它与第一​​个解决方案没有太大区别。 以下是上述解决方案的示例:

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

    【讨论】:

    • 感谢您的示例,我会试一试。但是,和你一样,我也有同样的疑问,是否只有一个 Context Menu 对象?我得深入研究一下。
    • 我对此进行了测试,并比较了它提供相同值的每一行的 ContextMenus 的哈希码,因此它应该是同一个对象。很好!我真的需要每行一个命令吗?我想像一个父命令一样使用它,它位于主 ViewModel 的顶部。这可以通过调整 MenuItem 中的命令绑定来完成吗?我无法让它工作!该死的……
    • @Jay 你的意思是静态路由命令吗?实际上,可以使用事件处理程序或全局命令来代替行级命令。明天我将尝试实现类似的东西,如果您可以将命令代码添加到问题中,它将对我有所帮助。
    • 在我的场景中,我有 MainView 和相应的 MainViewModel(其中包含一个名为 DataGridActionCommand 的 Prism 的 DelegateCommand 实例)。主视图的数据网格具有在用户右键单击每一行时显示上下文菜单的行为(DataItem 是 ItemViewModel)。我希望每一行都从 MainViewModel 执行 DataGridActionCommand(并将 DataItem 传递给它),而不是行视图模型上的命令。这有帮助吗?如果您愿意,我可以发布部分场景的代码以便更好地理解。
    • @Jay 我已经编辑了我的答案。该解决方案并不理想,但正如您所描述的那样有效。
    猜你喜欢
    • 1970-01-01
    • 2013-05-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-15
    • 2011-02-11
    • 1970-01-01
    相关资源
    最近更新 更多