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

如何在选中新行后折叠DataGrid中上次选中行的所有层级详情?

嵌套DataGrid点击新行自动折叠所有层级详情的实现方案

问题场景

我使用了多层嵌套的DataGrid,当前交互存在以下情况:

  • 未选中任何行时:未选中任何行
  • 点击第一行后,该行详情展开:第一行详情展开
  • 点击该行详情内的第一行后,内层行详情展开:内层行详情展开
  • 切换点击外层DataGrid的第二行后,第一行的层级详情并未折叠:切换到第二行
  • 再次点击第一行时,会显示之前展开的两层行详情:第一行两层详情展开

需求:实现点击新行时,自动折叠上次选中行的所有层级详情,支持任意多层嵌套的DataGrid。

现有XAML代码

<DataGrid x:Name="dataGrid1" ItemsSource="{Binding Students}"  AutoGenerateColumns="False" IsReadOnly="True" HeadersVisibility="Column" Background="DarkBlue">
    <DataGrid.Columns>
        <DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
        <DataGridTextColumn Header="ID" Binding="{Binding ID}"/>
        <DataGridTextColumn Header="School" Binding="{Binding School}"/>
    </DataGrid.Columns>
    <DataGrid.RowDetailsTemplate>
        <DataTemplate>
            <DataGrid x:Name="nestedDataGrid1" ItemsSource="{Binding Teachers}" IsReadOnly="True" AutoGenerateColumns="False" HeadersVisibility="Column" Background="DarkRed">
                <DataGrid.Columns>
                    <DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
                    <DataGridTextColumn Header="ID" Binding="{Binding ID}"/>
                </DataGrid.Columns>
                <DataGrid.RowDetailsTemplate>
                    <DataTemplate>
                        <StackPanel Orientation="Vertical">
                            <TextBlock Text="Line 1" FontSize="30"/>
                            <TextBlock Text="Line 2" FontSize="30"/>
                            <TextBlock Text="Line 3" FontSize="30"/>
                            <TextBlock Text="Line 4" FontSize="30"/>
                            <TextBlock Text="Line 5" FontSize="30"/>
                        </StackPanel>
                    </DataTemplate>
                </DataGrid.RowDetailsTemplate>
            </DataGrid>
        </DataTemplate>
    </DataGrid.RowDetailsTemplate>
</DataGrid>

解决方案

方案一:后台代码递归折叠(适合非MVVM场景)

通过监听外层DataGrid的选中变化,递归遍历并折叠之前选中行的所有嵌套DataGrid行详情。

  1. 给外层DataGrid添加SelectionChanged事件:
<DataGrid x:Name="dataGrid1" ... SelectionChanged="DataGrid1_SelectionChanged">
  1. 后台实现递归折叠方法和事件处理:
private DataGridRow _lastSelectedRow;

private void DataGrid1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    // 折叠上次选中行的所有层级详情
    if (_lastSelectedRow != null)
    {
        _lastSelectedRow.DetailsVisibility = Visibility.Collapsed;
        var nestedGrid = FindVisualChild<DataGrid>(_lastSelectedRow);
        if (nestedGrid != null) CollapseAllNestedDetails(nestedGrid);
    }

    // 更新当前选中行记录
    _lastSelectedRow = dataGrid1.SelectedItem != null 
        ? dataGrid1.ItemContainerGenerator.ContainerFromItem(dataGrid1.SelectedItem) as DataGridRow 
        : null;
}

// 递归折叠指定DataGrid下所有行的详情及嵌套DataGrid的内容
private void CollapseAllNestedDetails(DataGrid parentGrid)
{
    foreach (var item in parentGrid.Items)
    {
        if (parentGrid.ItemContainerGenerator.ContainerFromItem(item) is DataGridRow row)
        {
            row.DetailsVisibility = Visibility.Collapsed;
            var nestedGrid = FindVisualChild<DataGrid>(row);
            if (nestedGrid != null) CollapseAllNestedDetails(nestedGrid);
        }
    }
    parentGrid.SelectedItem = null;
}

// 递归查找视觉树中的子DataGrid
private T FindVisualChild<T>(DependencyObject parent) where T : DependencyObject
{
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
    {
        var child = VisualTreeHelper.GetChild(parent, i);
        if (child is T typedChild)
            return typedChild;
        var result = FindVisualChild<T>(child);
        if (result != null)
            return result;
    }
    return null;
}

方案二:MVVM绑定实现(适合MVVM架构)

通过给数据模型添加IsExpanded属性,绑定到行详情的可见性,在选中新项时递归关闭旧项的展开状态。

  1. 定义带展开状态的数据模型:
public class Student : INotifyPropertyChanged
{
    public string Name { get; set; }
    public string ID { get; set; }
    public string School { get; set; }
    public ObservableCollection<Teacher> Teachers { get; set; } = new();

    private bool _isExpanded;
    public bool IsExpanded
    {
        get => _isExpanded;
        set
        {
            _isExpanded = value;
            OnPropertyChanged();
            // 折叠时同步关闭所有嵌套项的展开状态
            if (!value) Teachers.ToList().ForEach(t => t.IsExpanded = false);
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
        => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

public class Teacher : INotifyPropertyChanged
{
    public string Name { get; set; }
    public string ID { get; set; }

    private bool _isExpanded;
    public bool IsExpanded
    {
        get => _isExpanded;
        set
        {
            _isExpanded = value;
            OnPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
        => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
  1. 修改XAML绑定行详情可见性:
<Window.Resources>
    <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</Window.Resources>

<DataGrid x:Name="dataGrid1" ItemsSource="{Binding Students}" SelectedItem="{Binding SelectedStudent}" ...>
    <DataGrid.RowStyle>
        <Style TargetType="DataGridRow">
            <Setter Property="DetailsVisibility" Value="{Binding IsExpanded, Converter={StaticResource BooleanToVisibilityConverter}}"/>
        </Style>
    </DataGrid.RowStyle>
    <!-- 外层列定义 -->
    <DataGrid.RowDetailsTemplate>
        <DataTemplate>
            <DataGrid ItemsSource="{Binding Teachers}" ...>
                <DataGrid.RowStyle>
                    <Style TargetType="DataGridRow">
                        <Setter Property="DetailsVisibility" Value="{Binding IsExpanded, Converter={StaticResource BooleanToVisibilityConverter}}"/>
                    </Style>
                </DataGrid.RowStyle>
                <!-- 内层列定义 -->
                <!-- 内层行详情模板 -->
            </DataGrid>
        </DataTemplate>
    </DataGrid.RowDetailsTemplate>
</DataGrid>
  1. ViewModel中处理选中项切换:
public class MainViewModel : INotifyPropertyChanged
{
    public ObservableCollection<Student> Students { get; set; } = new();

    private Student _selectedStudent;
    public Student SelectedStudent
    {
        get => _selectedStudent;
        set
        {
            // 关闭旧选中项的展开状态
            if (_selectedStudent != null) _selectedStudent.IsExpanded = false;
            _selectedStudent = value;
            // 展开新选中项
            if (_selectedStudent != null) _selectedStudent.IsExpanded = true;
            OnPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
        => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

内容的提问来源于stack exchange,提问作者Jtastic

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 14:44:55