天天看点

在MVVM设计中实现对ListViewItem双击事件的响应

ListView

控件最常用的事件是

SelectionChanged

;如果采用

MVVM

模式来设计 WPF 应用,通常,我们可以使用行为(如

InvokeCommandAction

)并结合命令来实现对该事件的响应;如果我们要实现对

ListViewItem

双击事件的响应——也就是说,双击

ListView

中的某一项——又该怎么做呢?

首先,

ListView

并没有提供相关的事件;其次,

ListViewItem

虽然有

PreviewMouseDoubleClick

(隧道事件),然而在 UI 中,我们却没有适合的方法来调用。那么究竟有没有办法来解决这个问题呢?答案肯定是有,以下便是两种解决方案。第一种是相对简单,在

DataTemplate

中使用

MouseBinding

;第二种方法是通过附加属性,相比第一种略为复杂一些。

  1. DataTemplate

    MouseBinding

    1. ViewModel

      代码如下
      public class MainViewModel
      {
          public MainViewModel()
          {
              Strs = new List<string>();
              for (int i = 0; i < 20; i++)
              {
                  Strs.Add(Guid.NewGuid().ToString("N"));
              }
      
              ListViewDoubleClickCommand = new Command<string>(ListViewDoubleClick);
          }
      
          private void ListViewDoubleClick(string value)
          {
      
          }
      
          public List<string> Strs { get; set; }
      
          public Command<string> ListViewDoubleClickCommand { get; }
      }
                 
    2. XAML

      绑定如下
      <ListView ItemsSource="{Binding Path=Strs}">
          <ListView.ItemTemplate>
              <DataTemplate>
                  <TextBlock Text="{Binding}">
                      <TextBlock.InputBindings>
                          <MouseBinding MouseAction="LeftDoubleClick"
                                        Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type Window}},Path=DataContext.ListViewDoubleClickCommand}"
                                        CommandParameter="{Binding}" />
                      </TextBlock.InputBindings>
                  </TextBlock>
              </DataTemplate>
          </ListView.ItemTemplate>
      </ListView>
                 
  2. 使用附加属性
    1. public class ControlDoubleClick : DependencyObject
      {
          public static readonly DependencyProperty CommandProperty = DependencyProperty.RegisterAttached("Command",
                                                                                                          typeof(ICommand), typeof(ControlDoubleClick), new PropertyMetadata(OnCommandChanged));
      
          public static ICommand GetCommand(Control target)
          {
              return (ICommand) target.GetValue(CommandProperty);
          }
      
          public static void SetCommand(Control target, ICommand value)
          {
              target.SetValue(CommandProperty, value);
          }
      
          public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.RegisterAttached(
              "CommandParameter",
              typeof(object), typeof(ControlDoubleClick), new PropertyMetadata(defaultValue: null));
      
          public static object GetCommandParameter(Control target)
          {
              return target.GetValue(CommandParameterProperty);
          }
      
          public static void SetCommandParameter(Control target, object value)
          {
              target.SetValue(CommandParameterProperty, value);
          }
      
          private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
          {
              if (d is Control target)
              {
                  target.PreviewMouseDoubleClick -= Element_PreviewMouseDoubleClick;
                  target.PreviewMouseDoubleClick += Element_PreviewMouseDoubleClick;
              }
          }
      
          private static void Element_PreviewMouseDoubleClick(object sender, MouseButtonEventArgs e)
          {
              if (sender is Control target)
              {
                  ICommand command = GetCommand(target);
                  if (command != null)
                  {
                      if (command.CanExecute(GetCommandParameter(target)))
                      {
                          command.Execute(GetCommandParameter(target));
                      }
                  }
              }
          }
      }
                 
    2. <ListView ItemsSource="{Binding Path=Strs}">
          <ListView.ItemContainerStyle>
              <Style TargetType="{x:Type ListViewItem}">
                  <Setter Property="local:ControlDoubleClick.Command"
                          Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type Window}},Path=DataContext.ListViewDoubleClickCommand}" />
                  <Setter Property="local:ControlDoubleClick.CommandParameter" Value="{Binding}"></Setter>
              </Style>
          </ListView.ItemContainerStyle>
          <ListView.ItemTemplate>
              <DataTemplate>
                  <TextBlock Text="{Binding}" />
              </DataTemplate>
          </ListView.ItemTemplate>
      </ListView>