WPF DataGrid设置SelectionMode=Extended后无法实现多行选择
问题分析与解决:WPF DataGrid多选功能失效
问题描述
所有DataGrid均存在多行选择功能失效的问题,仅能单选一行。仅实现了Mouse_DoubleClick事件处理,此前未启用过多选功能,所有Grid定义结构基本一致,表现完全相同。
相关代码
XAML 定义
<DataGrid x:Name="FacilityGrid" Margin="0" Sorting="FacilityGrid_Sorting" ItemsSource="{Binding Facilities}" SelectedItem="{Binding SelectedFacility}" Height="Auto" Width="Auto" SelectionMode="Extended" AutoGenerateColumns="False" CanUserAddRows="False" CanUserResizeColumns="True" SelectionUnit="FullRow" Background="Transparent" CanUserResizeRows="False" CanUserReorderColumns="False" CanUserSortColumns="False" ScrollViewer.HorizontalScrollBarVisibility="Auto" ScrollViewer.VerticalScrollBarVisibility="Auto" GridLinesVisibility="All" VerticalAlignment="Stretch" AlternationCount="2" EnableColumnVirtualization="False" EnableRowVirtualization="True" AlternatingRowBackground="WhiteSmoke" SelectionChanged="FacilityGrid_SelectionChanged"> <DataGrid.ColumnHeaderStyle> <Style TargetType="{x:Type DataGridColumnHeader}"> <Setter Property="FontWeight" Value="SemiBold"/> <Setter Property="HorizontalContentAlignment" Value="Center"/> </Style> </DataGrid.ColumnHeaderStyle> <DataGrid.CellStyle> <Style TargetType="DataGridCell" > <Style.Triggers> <Trigger Property="IsSelected" Value="True"> <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/> <Setter Property="Background" Value="Transparent"/> <Setter Property="BorderBrush" Value="Black"/> <Setter Property="BorderThickness" Value="0,2,0,2"/> </Trigger> </Style.Triggers> <Setter Property="FontWeight" Value="SemiBold"/> <EventSetter Event="MouseDoubleClick" Handler="DataGridCell_MouseDoubleClick"/> </Style> </DataGrid.CellStyle> <DataGrid.RowStyle> <Style TargetType="DataGridRow"> <Setter Property="ContextMenu" Value="{StaticResource RowMenu}"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="DataGridRow"> <Border BorderThickness="{TemplateBinding Border.BorderThickness}" BorderBrush="{TemplateBinding Border.BorderBrush}" Background="{TemplateBinding Panel.Background}" Name="DGR_Border" SnapsToDevicePixels="True"> <SelectiveScrollingGrid> <DataGridCellsPresenter ItemsPanel="{TemplateBinding ItemsControl.ItemsPanel}" SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}"/> </SelectiveScrollingGrid> </Border> </ControlTemplate> </Setter.Value> </Setter> <Setter Property="Height" Value="25"/> <Setter Property="FontSize" Value="12"/> <Setter Property="Visibility" Value="{Binding IsVisible, Converter={StaticResource BooleanToVisibilityConverter}}"/> <Style.Triggers> <Trigger Property="IsSelected" Value="True"> <Setter Property="Background" Value="Azure"/> </Trigger> </Style.Triggers> </Style> </DataGrid.RowStyle> <!--hide the default button at the beginning of each row--> <DataGrid.RowHeaderStyle> <Style TargetType="{x:Type DataGridRowHeader}"> <Setter Property="Width" Value="0"/> </Style> </DataGrid.RowHeaderStyle> <DataGrid.Columns> </DataGrid.Columns> </DataGrid>
C# 代码
private ObservableCollection<Facility> _Facilities = new ObservableCollection<Facility>(); public ObservableCollection<Facility> Facilities { get { return _Facilities; } set { if (value != _Facilities) { _Facilities = value; NotifyPropertyChanged(); } } } private Facility _SelectedFacility; public Facility SelectedFacility { get { return _SelectedFacility; } set { if (value != _SelectedFacility) { _SelectedFacility = value; NotifyPropertyChanged(); } } } private void FacilityGrid_Sorting(object sender, DataGridSortingEventArgs e) { Mouse.OverrideCursor = Cursors.Wait; UIDispatcher.BeginInvoke((System.Action)(() => { Mouse.OverrideCursor = null; }), DispatcherPriority.ContextIdle); } private void DataGridCell_MouseDoubleClick(object sender, MouseButtonEventArgs e) { try { //this means it's on the main form var tabControl = Extensions.FindParent<TabControl>((DataGridCell)sender); if (tabControl != null) { var facility = ((DataGridCell)sender)?.DataContext as Facility; if (facility != null) { facility.RaiseOpenEvent(); } } else { var facility = ((DataGridCell)sender)?.DataContext as Facility; if (facility != null) { facility.RaiseSelectEvent(); } } } catch { } } private void FacilityGrid_SelectionChanged(object sender, SelectionChangedEventArgs e) { var dg = sender as DataGrid; if (dg != null) { MainViewModel.GetMainViewModel().SelectedFacilityRow = (dg.SelectedIndex + 1).ToString("#,#"); } }
问题原因
- 自定义DataGridRow模板破坏多选交互:重写
DataGridRow的ControlTemplate时,仅保留了DataGridCellsPresenter,缺失了默认模板中处理选择交互的必要元素(如视觉状态管理、选择输入处理逻辑),导致多选操作无法正常触发。 - ViewModel仅绑定单个选中项:当前仅绑定了
SelectedItem(单个对象),虽不会直接阻止DataGrid本身的多选行为,但无法在ViewModel层面获取选中的多个对象,且可能因绑定逻辑间接干扰多选状态的维护。
解决方案
1. 修复DataGridRow的ControlTemplate
还原或完善DataGridRow的模板,确保包含默认模板核心结构。以下是简化版的正确模板示例:
<ControlTemplate TargetType="DataGridRow"> <Border x:Name="DGR_Border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True"> <SelectiveScrollingGrid> <SelectiveScrollingGrid.ColumnDefinitions> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="*"/> </SelectiveScrollingGrid.ColumnDefinitions> <SelectiveScrollingGrid.RowDefinitions> <RowDefinition Height="*"/> <RowDefinition Height="Auto"/> </SelectiveScrollingGrid.RowDefinitions> <DataGridRowHeader x:Name="RowHeader" SelectiveScrollingGrid.SelectiveScrollingOrientation="Vertical" Visibility="{Binding HeadersVisibility, ConverterParameter={x:Static DataGridHeadersVisibility.Row}, Converter={x:Static DataGrid.HeadersVisibilityConverter}, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"/> <DataGridCellsPresenter x:Name="CellsPresenter" Grid.Column="1" ItemsPanel="{TemplateBinding ItemsPanel}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/> <DataGridDetailsPresenter x:Name="DetailsPresenter" Grid.Column="1" Grid.Row="1" Visibility="{TemplateBinding DetailsVisibility}" SelectiveScrollingGrid.SelectiveScrollingOrientation="Vertical"/> </SelectiveScrollingGrid> </Border> <ControlTemplate.Triggers> <Trigger Property="IsSelected" Value="True"> <Setter TargetName="DGR_Border" Property="Background" Value="Azure"/> </Trigger> <!-- 可保留其他自定义触发器 --> </ControlTemplate.Triggers> </ControlTemplate>
若不需要行头,可通过RowHeaderStyle隐藏,但不要在模板中直接移除该元素,否则会破坏选择逻辑。
2. 支持多选的ViewModel绑定
添加存储多个选中项的集合属性,并在SelectionChanged事件中手动更新:
private ObservableCollection<Facility> _SelectedFacilities = new ObservableCollection<Facility>(); public ObservableCollection<Facility> SelectedFacilities { get { return _SelectedFacilities; } set { if (value != _SelectedFacilities) { _SelectedFacilities = value; NotifyPropertyChanged(); } } } // 更新SelectionChanged事件处理 private void FacilityGrid_SelectionChanged(object sender, SelectionChangedEventArgs e) { var dg = sender as DataGrid; if (dg != null) { MainViewModel.GetMainViewModel().SelectedFacilityRow = (dg.SelectedIndex + 1).ToString("#,#"); // 更新选中集合 SelectedFacilities.Clear(); foreach (var item in dg.SelectedItems) { if (item is Facility facility) SelectedFacilities.Add(facility); } } }
3. 验证基础配置
确保DataGrid的SelectionMode设置为Extended或Multiple,SelectionUnit设置为FullRow(你的代码中已正确设置,可忽略此步)。
内容的提问来源于stack exchange,提问作者Steve S
相关产品推荐
相关产品推荐

