You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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("#,#");
    }
}

问题原因

  1. 自定义DataGridRow模板破坏多选交互:重写DataGridRow的ControlTemplate时,仅保留了DataGridCellsPresenter,缺失了默认模板中处理选择交互的必要元素(如视觉状态管理、选择输入处理逻辑),导致多选操作无法正常触发。
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 17:50:54