Plik WPF Datagrid ma dwa tryby zaznaczenia, pojedynczy lub rozszerzony. ListView WPF ma trzeci - wiele. Ten tryb umożliwia klikanie i wybieranie wielu wierszy bez przytrzymywania CTRL lub Shift. Ktoś wie jak to zrobić dla datagridu?WPF Datagrid Wybór wielokrotny bez CTRL lub spacji
Odpowiedz
Nie jest to obsługiwane w DataGrid w zestawie narzędzi i wygląda na to, że won't be supported, gdy DataGrid jest dostarczana z .NET 4 albo. Kolejny powód, dla którego ta kontrola nie jest gotowa do użycia w produkcji. Chciałbym przejść z jednej z tych opcji:
- przewróceniu własne siatki z ListView/GridView
- Modyfikacja kodu źródłowego DataGrid w zestawie narzędzi (to nie powinno być zbyt trudne, ponieważ rozszerzony wybór jest już obsługiwany ?)
- Look dla żadnego z komercyjnych datagrids WPF dostępny (na ogół dodać ogromną ilość użytecznych funkcjonalności)
Zgadzam się, że DataGrid powinien wspierać to i myślę, że powinien file a bug/suggestion za to w każdym razie. Może to nie jest zbyt późno, aby dostać go w .NET 4 .. :)
Można spróbować to proste obejście bez konieczności modyfikowania/dziedziczenie DataGrid
kontroli przez obsługę podglądu myszką w dół zdarzenie następująco:
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);
}
Metoda TryFindFromPoint
z blog post by Philipp Sumi służy do pobrania instancji DataGridRow
z klikniętego punktu.
Sprawdzając ModifierKeys
, możesz zachować Ctrl i Shift jako domyślne zachowanie.
Tylko jeden powrót z tej metody polega na tym, że nie można klikać i przeciągać, aby dokonać wyboru zakresu, tak jak to możliwe.
Tworzyłem aplikację o podobnym wymaganiu, która byłaby odpowiednia zarówno dla ekranu dotykowego, jak i pulpitu. Po spędzeniu trochę czasu nad rozwiązaniem, które wymyśliłem, wydaje się być czystsze. W projektancie, dodałem następujące ustawiające zdarzeń do DataGrid:
<DataGrid.RowStyle>
<Style TargetType="DataGridRow" >
<EventSetter Event="MouseEnter" Handler="MouseEnterHandler"></EventSetter>
<EventSetter Event="PreviewMouseDown" Handler="PreviewMouseDownHandler"></EventSetter>
</Style>
</DataGrid.RowStyle>
Następnie w kodzie, I obsługiwane wydarzenia jak:
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;
}
}
Oto kod dla metody pomocnika 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;
}
Mam nadzieję, że pomoże to komuś innemu.
Bazując na poprzednim artykule napisałem ("jak") kod MVVM:
Po pierwsze dodać do swojej głównej Widok:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
Istotna część Widok:
<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>
Więcej informacji o EventToCommandBehavior: here
W ten sposób Twój ViewModel musi wdrożyć te polecenia:
//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;
}
}
});
}
}
końcu wdrożyć metodę (gdzieś w Modelu), aby znaleźć wiersz (y).
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;
}
Rozwiązanie to działa na mnie idealnie więc mam nadzieję, że pomoże to dla ciebie też.