将 DataGrid 行中的 DoubleClick 命令绑定到 VM

Posted

技术标签:

【中文标题】将 DataGrid 行中的 DoubleClick 命令绑定到 VM【英文标题】:Bind DoubleClick Command from DataGrid Row to VM 【发布时间】:2013-06-29 11:10:15 【问题描述】:

我有一个 Datagrid,不喜欢我的解决方法,即在我的视图模型上为单击(又名选定)行触发双击命令。

查看:

   <DataGrid  EnableRowVirtualization="True"
              ItemsSource="Binding SearchItems"
              SelectedItem="Binding SelectedItem"
              SelectionMode="Single"
              SelectionUnit="FullRow">

        <i:Interaction.Triggers>
            <i:EventTrigger EventName="MouseDoubleClick">
                <cmd:EventToCommand Command="Binding MouseDoubleClickCommand" PassEventArgsToCommand="True" />
            </i:EventTrigger>
        </i:Interaction.Triggers>
        ...
  </DataGrid>

视图模型:

    public ICommand MouseDoubleClickCommand
    
        get
        
            if (mouseDoubleClickCommand == null)
            
                mouseDoubleClickCommand = new RelayCommand<MouseButtonEventArgs>(
                    args =>
                    
                        var sender = args.OriginalSource as DependencyObject;
                        if (sender == null)
                        
                            return;
                        
                        var ancestor = VisualTreeHelpers.FindAncestor<DataGridRow>(sender);
                        if (ancestor != null)
                        
                            MessengerInstance.Send(new FindDetailsMessage(this, SelectedItem.Name, false));
                        
                    
                    );
            
            return mouseDoubleClickCommand;
        
    

我想在我的视图模型中摆脱与视图相关的代码(具有依赖对象和可视化树助手的代码),因为这会以某种方式破坏可测试性。但另一方面,这样我可以避免当用户没有点击行而是点击标题时发生某些事情。

PS:我尝试查看附加行为,但无法在工作时从 Skydrive 下载,因此最好使用“内置”解决方案。

【问题讨论】:

【参考方案1】:

你可以试试这个解决方法:

<DataGrid  EnableRowVirtualization="True"
          ItemsSource="Binding SearchItems"
          SelectedItem="Binding SelectedItem"
          SelectionMode="Single"
          SelectionUnit="FullRow">
    <DataGrid.Columns>
        <DataGridTemplateColumn Header=".....">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                        <TextBlock .....>
                            <i:Interaction.Triggers>
                                <i:EventTrigger EventName="MouseDoubleClick">
                                    <cmd:EventToCommand Command="Binding MouseDoubleClickCommand" PassEventArgsToCommand="True" />
                                </i:EventTrigger>
                            </i:Interaction.Triggers>
                        </TextBlock>
                </DataTemplate>
    ...................

在这种情况下,您必须为DataGrid 中的每一列指定DataTemplate

【讨论】:

我想这会起作用,但如果我必须在这个和我的解决方法之间做出选择,我可能会坚持我的。如果我更改列,我总是必须为每一列重复您的 XAML 代码。【参考方案2】:

以下是使用附加行为实现它的方法:

编辑:现在在 DataGridRow 而不是 DataGrid 上注册行为,以便忽略 DataGridHeader 的点击。

行为:

public class Behaviours

    public static DependencyProperty DoubleClickCommandProperty =
       DependencyProperty.RegisterAttached("DoubleClickCommand", typeof(ICommand), typeof(Behaviours),
                                           new PropertyMetadata(DoubleClick_PropertyChanged));

    public static void SetDoubleClickCommand(UIElement element, ICommand value)
    
        element.SetValue(DoubleClickCommandProperty, value);
    

    public static ICommand GetDoubleClickCommand(UIElement element)
    
        return (ICommand)element.GetValue(DoubleClickCommandProperty);
    

    private static void DoubleClick_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    
        var row = d as DataGridRow;
        if (row == null) return;

        if (e.NewValue != null)
        
            row.AddHandler(DataGridRow.MouseDoubleClickEvent, new RoutedEventHandler(DataGrid_MouseDoubleClick));
        
        else
        
            row.RemoveHandler(DataGridRow.MouseDoubleClickEvent, new RoutedEventHandler(DataGrid_MouseDoubleClick));
        
    

    private static void DataGrid_MouseDoubleClick(object sender, RoutedEventArgs e)
    
        var row= sender as DataGridRow;

        if (row!= null)
        
            var cmd = GetDoubleClickCommand(row);
            if (cmd.CanExecute(row.Item))
                cmd.Execute(row.Item);
        
    

Xaml:

    <DataGrid x:Name="grid" EnableRowVirtualization="True"
          SelectedItem="Binding SelectedItem"
          SelectionMode="Single"
          SelectionUnit="FullRow" ItemsSource="Binding SearchItems">
       <DataGrid.RowStyle>
           <Style TargetType="DataGridRow">
                <Setter Property="Behaviours.DoubleClickCommand" Value="Binding ElementName=grid, Path=DataContext.SortStateCommand"/>
           </Style>
       </DataGrid.RowStyle>

然后您需要修改您的 MouseDoubleClickCommand 以删除 MouseButtonEventArgs 参数并将其替换为您的 SelectedItem 类型。

【讨论】:

我喜欢这是一个近乎完美(最好阅读)的解决方案。毕竟这就是附加事件的意义:codeproject.com/Articles/28959/…【参考方案3】:

您为什么不直接使用CommandParameter

<DataGrid x:Name="myGrd"
          ItemsSource="Binding SearchItems"
          SelectedItem="Binding SelectedItem"
          SelectionMode="Single"
          SelectionUnit="FullRow">

    <i:Interaction.Triggers>
        <i:EventTrigger EventName="MouseDoubleClick">
            <cmd:EventToCommand Command="Binding MouseDoubleClickCommand"  
                                CommandParameter="Binding ElementName=myGrd, Path=SelectedItem" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
    ...
</DataGrid>

你的命令是这样的:

public ICommand MouseDoubleClickCommand

    get
    
        if (mouseDoubleClickCommand == null)
        
            mouseDoubleClickCommand = new RelayCommand<SearchItem>(
                item =>
                
                    var selectedItem = item;
                );
        

        return mouseDoubleClickCommand;
    

编辑:我现在用这个代替Interaction.Triggers

<DataGrid.InputBindings>
    <MouseBinding MouseAction="LeftDoubleClick"
                  Command="Binding Path=MouseDoubleClickCommand"
                  CommandParameter="Binding ElementName=myGrd, Path=SelectedItem" />
</DataGrid.InputBindings>

【讨论】:

问题是没有得到选择的项目(它在虚拟机上无论如何都是数据绑定的),而是得到命令不执行,例如,双击数据网格的标题。 如果你想阻止 mousedoubleclick,你可以尝试 PreviewMouseDoubleClick 并根据你的条件设置 e.Handled=true。因此您可以从视图模型中删除代码并将其放入数据网格的代码隐藏中 好主意。实际上,我一直在 OnContextMenuOpening 的代码隐藏中做同样的事情。有时你只是没有在正确的时间有正确的想法。谢谢。我将把它标记为答案。 将“i”从myGrd 中删除,您节省了多少时间和精力? :-) InputBindings 对我不起作用,因为它仅在双击 DataGrid 标题而不是行时才起作用。您还可以使用 InvokeCommandAction 代替 MVVM Light 的 EventToCommand。此行为是 System.Windows.Interactivity DLL 的一部分。它几乎等同于EventToCommand,但没有一些高级功能。【参考方案4】:

比这里提出的任何解决方案都简单。

我正在使用这个。

<!-- 
requires IsSynchronizedWithCurrentItem
for more info on virtualization/perf https://***.com/questions/9949358/datagrid-row-virtualization-display-issue 
 -->
        <DataGrid ItemsSource="Binding SearchItems" 
                  IsSynchronizedWithCurrentItem="True"
                  AutoGenerateColumns="false" CanUserAddRows="False" CanUserDeleteRows="False" IsReadOnly="True" EnableRowVirtualization="True"
                  >

            <!-- for details on ICollection view (the magic behind Binding Accounts/ https://marlongrech.wordpress.com/2008/11/22/icollectionview-explained/ -->

            <DataGrid.InputBindings>
                <MouseBinding
                    MouseAction="LeftDoubleClick"
                    Command="Binding MouseDoubleClickCommand"
                    CommandParameter="Binding SearchItems/" />
            </DataGrid.InputBindings>
        </DataGrid>

来自WPF DataGrid: CommandBinding to a double click instead of using Events

【讨论】:

感谢您提供最初的链接源——它很有用。对于阅读此评论的其他人:特别注意 CommandParameter 属性值末尾的正斜杠(即“SearchItems/”)。

以上是关于将 DataGrid 行中的 DoubleClick 命令绑定到 VM的主要内容,如果未能解决你的问题,请参考以下文章

单击DataGrid行中的复选框时触发PropertyChanged事件

wpf datagrid中如何获取选中行中模板如textbox,combobox的内容

easyui datagrid行中点击a标签链接,行被选中,但是获取不到对应的参数

WPF Datagrid 样式选定的行

Jquery easyui datagrid从codeigniter控制器加载数据

如何删除 Material UI DataGrid (Reactjs) 中的特定行