没有CTRL或空格的WPF数据网格多重选择

时间:2010-01-13 00:26:46

标签: wpf wpftoolkit selection wpfdatagrid

WPF Datagrid有两种选择模式,Single或Extended。 WPF ListView有第三个 - 多个。此模式允许您单击并选择多行而不按住CTRL或Shift。有人知道如何为datagrid做这个吗?

4 个答案:

答案 0 :(得分:12)

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

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

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

private void PreviewMouseDownHandler(object sender, MouseButtonEventArgs e)
{
    if (e.LeftButton == MouseButtonState.Pressed)
    {
        DataGridRow row = Utility.GetVisualParentByType(
                   (FrameworkElement)e.OriginalSource, typeof(DataGridRow)) as DataGridRow;

        row.IsSelected = !row.IsSelected;
        e.Handled = true;
    }
}

private void MouseEnterHandler(object sender, MouseEventArgs e)
{
    if (e.OriginalSource is DataGridRow && e.LeftButton == MouseButtonState.Pressed)
    {
        DataGridRow row = e.OriginalSource as DataGridRow;

        row.IsSelected = !row.IsSelected;
        e.Handled = true;
    }
}

以下是帮助方法GetVisualParentByType:

的代码
public static DependencyObject GetVisualParentByType(DependencyObject startObject, Type type)
{
    DependencyObject parent = startObject;
    while (parent != null)
    {
        if (type.IsInstanceOfType(parent))
            break;
        else
            parent = VisualTreeHelper.GetParent(parent);
    }

    return parent;
}

希望它也可以帮助其他人。

答案 1 :(得分:4)

您可以尝试这种简单的解决方法,而无需通过处理预览鼠标按下事件来修改/继承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保持为默认行为。

此方法只有一个缺点是您无法单击并拖动以执行原始范围选择。

答案 2 :(得分:3)

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

  1. 使用ListView / GridView
  2. 滚动您自己的网格
  3. 修改工具包中DataGrid的源代码(由于已经支持扩展选择,因此不应该太难)?
  4. 寻找可用的任何商业WPF DataGrids(它们通常会添加大量有用的功能)
  5. 我同意DataGrid应该支持这一点,我认为无论如何你应该file a bug/suggestion。也许现在进入.NET 4还为时不晚......:)

答案 3 :(得分:0)

根据前一篇文章,我写了一个(“喜欢”)MVVM代码:

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

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

观点的相关部分:

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

最后实现一个方法(在Model中的某个地方)来查找行。

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

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