1. 程式人生 > 其它 >在MVVM設計中實現對ListViewItem雙擊事件的響應

在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. 建立一個附加屬性類ControlDoubleClick

      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. 修改XAML如下

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

      以上兩種方法都可以實現對ListViewItem雙擊事件的繫結。