WPF Datagrid имеет два режима выбора: одиночный и расширенный. В WPF ListView есть третий - Multiple. Этот режим позволяет вам щелкнуть и выбрать несколько строк без удерживания CTRL или Shift. Кто-нибудь знает, как это сделать для сети данных?
Множественный выбор WPF Datagrid без CTRL или Shift
Ответы (4)
Это не поддерживается в DataGrid в наборе инструментов, и похоже, что это не будет поддерживаться, если DataGrid поставляется с .NET 4. Еще одна причина, по которой этот элемент управления не готов к производственному использованию. Я бы выбрал один из следующих вариантов:
- Создайте собственную сетку с помощью ListView / GridView
- Измените исходный код DataGrid в наборе инструментов (это не должно быть слишком сложно, поскольку расширенный выбор уже поддерживается?)
- Ищите любые доступные коммерческие WPF DataGrids (они обычно добавляют огромное количество полезных функций)
Я согласен с тем, что DataGrid должен поддерживать это, и думаю, вам следует сообщить об ошибке / предложении по крайней мере для этого. Может, еще не поздно вставить его в .NET 4 .. :)
Я создавал приложение с аналогичным требованием, которое работало бы как для сенсорного экрана, так и для рабочего стола. Потратив на это некоторое время, решение, которое я придумал, кажется более чистым. В дизайнере я добавил в сетку данных следующие установщики событий:
<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);
}
Надеюсь, это поможет и кому-то другому.
Вы можете попробовать этот простой обходной путь без изменения / наследования элемента управления 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 в качестве поведения по умолчанию.
Единственный недостаток этого метода заключается в том, что вы не можете щелкнуть и перетащить, чтобы выполнить выбор диапазона, как это было изначально.
Основываясь на предыдущей статье, я написал («нравится») код 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;
}
Это решение отлично работает для меня, поэтому я надеюсь, что оно поможет и вам.