【问题标题】:WPF Datagrid Multiple Selection without CTRL or Shift没有 CTRL 或 Shift 的 WPF Datagrid 多选
【发布时间】:2011-01-04 10:36:30
【问题描述】:

WPF Datagrid 有两种选择模式,Single 或 Extended。 WPF ListView 有第三个 - Multiple。此模式允许您在不按住 CTRL 或 Shift 的情况下单击并选择多行。有人知道如何为数据网格执行此操作吗?

【问题讨论】:

    标签: wpf datagrid selection wpftoolkit


    【解决方案1】:

    我正在创建一个具有类似要求的应用程序,该应用程序适用于触摸屏和桌面。在花了一些时间之后,我想出的解决方案似乎更干净了。 在设计器中,我将以下事件设置器添加到数据网格中:

    <DataGrid.RowStyle>
       <Style TargetType="DataGridRow" >
         <EventSetter Event="MouseEnter" Handler="MouseEnterHandler"></EventSetter>
         <EventSetter Event="PreviewMouseDown" Handler="PreviewMouseDownHandler"></EventSetter>
       </Style>
    </DataGrid.RowStyle>
    

    然后在代码隐藏中,我将事件处理为:

    private void MouseEnterHandler(object sender, MouseEventArgs e)
    {
        if (e.LeftButton == MouseButtonState.Pressed &&
            e.OriginalSource is DataGridRow row)
        {
            row.IsSelected = !row.IsSelected;
            e.Handled = true;
        }
    }
    
    private void PreviewMouseDownHandler(object sender, MouseButtonEventArgs e)
    {
        if (e.LeftButton == MouseButtonState.Pressed && 
            e.OriginalSource is FrameworkElement element &&
            GetVisualParentOfType<DataGridRow>(element) is DataGridRow row)
        {
            row.IsSelected = !row.IsSelected;
            e.Handled = true;
        }
    }
    
    private static DependencyObject GetVisualParentOfType<T>(DependencyObject startObject)
    {
        DependencyObject parent = startObject;
    
        while (IsNotNullAndNotOfType<T>(parent))
        {
            parent = VisualTreeHelper.GetParent(parent);
        }
    
        return parent is T ? parent : throw new Exception($"Parent of type {typeof(T)} could not be found");
    }
    
    private static bool IsNotNullAndNotOfType<T>(DependencyObject obj)
    {
        return obj != null && !(obj is T);
    }
    

    希望它也对其他人有所帮助。

    【讨论】:

      【解决方案2】:

      根据之前的一篇文章,我写了一段(“点赞”)MVVM代码:

      首先将其添加到您的主视图中:

        xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
      

      View的相关部分:

             <DataGrid
                    Style="{StaticResource DataGridStyle}"
                    ItemsSource="{Binding Results}"
                    SelectionUnit="FullRow"
                    SnapsToDevicePixels="True"
                    SelectionMode="Extended">  <!--You can change selection mode with converter. It will work (i tested it.)-->
              <i:Interaction.Behaviors>  
                               <utils:EventToCommandBehavior Command="{Binding TouchCommand}"
                                                        Event="PreviewTouchDown"
                                                        PassArguments="True"></utils:EventToCommandBehavior> 
                              <utils:EventToCommandBehavior Command="{Binding MouseCommand}"
                                                        Event="PreviewMouseDown"
                                                        PassArguments="True"></utils:EventToCommandBehavior>
              </i:Interaction.Behaviors>
              <DataGrid.Resources>
                  <Style TargetType="{x:Type DataGridRow}">
                      <Setter Property="IsSelected"<Style.Triggers>
                          <Trigger Property="IsSelected" Value="True">
                              <Setter Property="Background">
                                  <Setter.Value>
                                      <SolidColorBrush>
                                          <SolidColorBrush.Color>
                                              <Color A="50" R="0" G="0" B="0" />
                                          </SolidColorBrush.Color>
                                      </SolidColorBrush>
                                  </Setter.Value>
                              </Setter>
                          </Trigger>
                      </Style.Triggers>
                  </Style> 
                </DataGrid.Resources>
              <DataGrid.Columns>
               <!-- your columns -->
              </DataGrid.Columns>
             </DataGrid>
      

      有关 EventToCommandBehavior 的更多信息: here

      这样,您的 ViewModel 必须实现这些命令:

          //i skipped the TouchCommand definition because MouseCommand runs for touch on screen too.
          public RelayCommand<MouseButtonEventArgs> MouseCommand
          {
              get
              {
                  return new RelayCommand<MouseButtonEventArgs>((e)=> {
                      if (e.LeftButton == MouseButtonState.Pressed)
                      {
                          //call this function from your utils/models
                          var row = FindTemplatedParentByVisualParent<DataGridRow>((FrameworkElement)e.OriginalSource,typeof(ICommandSource));
                          //add ICommanSource to parameters. (if actual cell contains button instead of data.) Its optional.
                          if(row!=null) 
                          {
                              row.IsSelected = !row.IsSelected;
                              e.Handled = true;
                          }  
                      }                 
                  });
              }
          }
      

      最后实现一个方法(在模型中的某处)来查找行。

         public static T FindTemplatedParentByVisualParent<T>(FrameworkElement element,Type exceptionType = null) where T : class
          {
              if (element != null && (exceptionType == null || element.TemplatedParent == null || (exceptionType != null  && element.TemplatedParent !=null && !exceptionType.IsAssignableFrom(element.TemplatedParent.GetType()))))
              {
                  Type type = typeof(T);
                  if (type.IsInstanceOfType(element.TemplatedParent))
                  {
                      return (element.TemplatedParent as T);
                  }
                  else
                  {
                      return FindTemplatedParentByVisualParent<T>((FrameworkElement)VisualTreeHelper.GetParent(element));
                  }
              }
              else
                  return null;
          }
      

      这个解决方案非常适合我,所以我希望它对你也有帮助。

      【讨论】:

        【解决方案3】:

        您可以尝试这个简单的解决方法,而无需修改/继承DataGrid 控件,方法是按如下方式处理预览鼠标按下事件:

        TheDataGrid.PreviewMouseLeftButtonDown += 
                         new MouseButtonEventHandler(TheDataGrid_PreviewMouseLeftButtonDown);
        
        
        void TheDataGrid_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            // get the DataGridRow at the clicked point
            var o = TryFindFromPoint<DataGridRow>(TheDataGrid, e.GetPosition(TheDataGrid));
            // only handle this when Ctrl or Shift not pressed 
            ModifierKeys mods = Keyboard.PrimaryDevice.Modifiers;
            if (o != null && ((int)(mods & ModifierKeys.Control) == 0 &&
                                                        (int)(mods & ModifierKeys.Shift) == 0))
            {
                o.IsSelected = !o.IsSelected;
                e.Handled = true;
            }
        }
        
        public static T TryFindFromPoint<T>(UIElement reference, Point point)
                        where T:DependencyObject
        {
            DependencyObject element = reference.InputHitTest(point) as DependencyObject;
            if (element == null) 
                return null;
            else if (element is T) 
                return (T)element;
            else return TryFindParent<T>(element);
        }
        

        来自blog post by Philipp SumiTryFindFromPoint 方法用于从您单击的点获取DataGridRow 实例。

        通过检查ModifierKeys,您仍然可以将 Ctrl 和 Shift 保留为默认行为。

        此方法的唯一缺点是您不能像原来那样单击并拖动来执行范围选择。

        【讨论】:

          【解决方案4】:

          工具包中的 DataGrid 不支持此功能,当 DataGrid 与 .NET 4 一起提供时,它看起来像 won't be supported。这个控件还没有准备好用于生产的另一个原因。我会选择以下选项之一:

          1. 使用 ListView/GridView 滚动您自己的网格
          2. 在工具包中修改DataGrid的源代码(应该不会太难,因为已经支持扩展选择?)
          3. 寻找任何可用的商业 WPF 数据网格(它们通常会添加大量有用的功能)

          我同意 DataGrid 应该支持这一点,我认为无论如何你都应该file a bug/suggestion。也许现在将其纳入 .NET 4.. 还为时不晚 :)

          【讨论】:

            猜你喜欢
            • 2011-08-19
            • 1970-01-01
            • 2011-11-01
            • 1970-01-01
            • 2010-10-23
            • 2014-10-26
            • 1970-01-01
            • 1970-01-01
            • 2011-09-22
            相关资源
            最近更新 更多