Множественный выбор WPF Datagrid без CTRL или Shift

WPF Datagrid имеет два режима выбора: одиночный и расширенный. В WPF ListView есть третий - Multiple. Этот режим позволяет вам щелкнуть и выбрать несколько строк без удерживания CTRL или Shift. Кто-нибудь знает, как это сделать для сети данных?


person Aran Mulholland    schedule 13.01.2010    source источник


Ответы (4)


Это не поддерживается в DataGrid в наборе инструментов, и похоже, что это не будет поддерживаться, если DataGrid поставляется с .NET 4. Еще одна причина, по которой этот элемент управления не готов к производственному использованию. Я бы выбрал один из следующих вариантов:

  1. Создайте собственную сетку с помощью ListView / GridView
  2. Измените исходный код DataGrid в наборе инструментов (это не должно быть слишком сложно, поскольку расширенный выбор уже поддерживается?)
  3. Ищите любые доступные коммерческие WPF DataGrids (они обычно добавляют огромное количество полезных функций)

Я согласен с тем, что DataGrid должен поддерживать это, и думаю, вам следует сообщить об ошибке / предложении по крайней мере для этого. Может, еще не поздно вставить его в .NET 4 .. :)

person Oskar    schedule 13.01.2010

Я создавал приложение с аналогичным требованием, которое работало бы как для сенсорного экрана, так и для рабочего стола. Потратив на это некоторое время, решение, которое я придумал, кажется более чистым. В дизайнере я добавил в сетку данных следующие установщики событий:

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

Надеюсь, это поможет и кому-то другому.

person sumit    schedule 18.09.2012

Вы можете попробовать этот простой обходной путь без изменения / наследования элемента управления 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);
}

Используется метод TryFindFromPoint из сообщения в блоге Филиппа Суми. чтобы получить экземпляр DataGridRow из точки, по которой вы щелкнули.

Установив флажок ModifierKeys, вы по-прежнему можете использовать Ctrl и Shift в качестве поведения по умолчанию.

Единственный недостаток этого метода заключается в том, что вы не можете щелкнуть и перетащить, чтобы выполнить выбор диапазона, как это было изначально.

person Mr.Tiger    schedule 07.08.2011

Основываясь на предыдущей статье, я написал («нравится») код 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: здесь

Таким образом, ваша 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;
    }

Это решение отлично работает для меня, поэтому я надеюсь, что оно поможет и вам.

person Péter Hidvégi    schedule 29.03.2017