Wpf 将DoubleClick命令从DataGrid行绑定到VM

Wpf 将DoubleClick命令从DataGrid行绑定到VM,wpf,mvvm,binding,datagrid,command,Wpf,Mvvm,Binding,Datagrid,Command,我有一个Datagrid,不喜欢在我的viewmodel上为单击(也称为选中)行触发双击命令 视图: ... 视图模型: public ICommand MouseDoubleClickCommand { get { if (mouseDoubleClickCommand == null) { mouseDoubleClickCommand = new RelayC

我有一个Datagrid,不喜欢在我的viewmodel上为单击(也称为选中)行触发双击命令

视图:


...
视图模型:

    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;
        }
    }
public图标命令和mousedoubleclick命令
{
得到
{
if(mouseDoubleClickCommand==null)
{
mouseDoubleClickCommand=新的RelayCommand(
args=>
{
var sender=args.OriginalSource作为DependencyObject;
if(发送方==null)
{
返回;
}
var祖先=VisualTreeHelpers.FindAncestor(发送方);
if(祖先!=null)
{
Send(newfinddeailsmessage(this,SelectedItem.Name,false));
}
}
);
}
返回mousedoubleclick命令;
}
}
我想去掉视图模型中与视图相关的代码(带有依赖项对象和可视化树辅助对象的代码),因为这在某种程度上破坏了可测试性。但是从另一方面来说,我避免了当用户不点击一行而是点击标题时发生的事情


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

您可以尝试以下解决方法:

<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
,以下是使用附加行为实现它的方法:

EDIT:现在注册
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);
        }
    }
}
    <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>
Xaml:

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


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

为什么不直接使用
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>

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

我用这个

<!-- 
requires IsSynchronizedWithCurrentItem
for more info on virtualization/perf https://stackoverflow.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>


中,问题不是获取所选项目(它在虚拟机上仍然是数据绑定的),而是获取在双击数据网格的标题时不执行的命令。如果要阻止鼠标双击,可以尝试预览鼠标双击,并将e.Handled=true设置为您的条件。因此,您可以从viewmodel中重新编译代码,并将其放入codebehind中,以实现您的datagridGreat想法。事实上,我一直在为OnContextMenuoOpening编写代码。有时候,你只是在正确的时间没有正确的想法。谢谢我将把这个标记为答案。在
myGrd
中保留“I”可以节省多少时间和精力?:-)InputBindings对我不起作用,因为它只在双击DataGrid头而不是行时起作用。您还可以使用
InvokeCommandAction
代替MVVM Light的
EventToCommand
。此行为是System.Windows.Interactivity DLL的一部分。它几乎等同于
EventToCommand
,但没有一些高级功能。我想这会奏效,但如果我必须在这两者之间做出选择,我可能会坚持我的方法。如果我更改列,我总是要为每一列重复您的XAML代码。我喜欢这是一个近乎完美(读作最佳)的解决方案。毕竟,这就是attched事件的目的:感谢您提供的初始链接源-它非常有用。阅读此评论的其他人:请特别注意CommandParameter属性值(即“SearchItems/”)末尾的正斜杠。
<!-- 
requires IsSynchronizedWithCurrentItem
for more info on virtualization/perf https://stackoverflow.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>