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

如何在折叠DataGrid行时动态调整其列宽?

解决WPF DataGrid折叠行时动态调整列宽的问题

针对你这个需求,我有两个实用的方案,结合你的现有结构来帮你实现:

方案一:在ViewModel触发后手动调整列宽(简单直接)

因为你已经通过IsChecked属性批量控制行的IsVisible,那我们可以在这个属性的setter里,完成行可见性更新后通知View去调整DataGrid的列宽。

步骤1:在ViewModel中添加事件通知

首先,在你的ViewModel里加一个事件,当需要调整列宽时触发它:

private bool _isChecked;
public bool IsChecked
{
    get => _isChecked;
    set
    {
        if (_isChecked != value)
        {
            _isChecked = value;
            // 批量设置所有行的可见性
            foreach (var item in CheckinPaths)
            {
                item.IsVisible = value;
            }
            // 触发列宽调整事件
            ColumnsNeedResize?.Invoke(this, EventArgs.Empty);
            OnPropertyChanged(nameof(IsChecked));
        }
    }
}

// 定义列宽调整事件
public event EventHandler ColumnsNeedResize;

步骤2:在View后台实现列宽调整逻辑

在你的View的代码后台,订阅ViewModel的事件,然后执行列宽调整:

public YourView()
{
    InitializeComponent();
    // 确保DataContext已加载后订阅事件
    Loaded += (s, e) =>
    {
        if (DataContext is YourViewModel vm)
        {
            vm.ColumnsNeedResize += (sender, args) => ResizeDataGridColumns();
        }
    };
}

private void ResizeDataGridColumns()
{
    // 先更新布局,确保行的可见性状态已经生效
    dgChangesMade.UpdateLayout();

    // 遍历所有列,设置为根据可见单元格内容自动调整宽度
    foreach (var column in dgChangesMade.Columns)
    {
        // SizeToCells会根据当前可见行的单元格内容计算宽度
        column.Width = new DataGridLength(1, DataGridLengthUnitType.SizeToCells);
        // 如果需要固定调整后的宽度,可以改成:
        // column.Width = new DataGridLength(column.ActualWidth, DataGridLengthUnitType.Pixel);
    }
}

这个方案的好处是批量操作时只触发一次调整,性能更优,适合你这种批量折叠/展开行的场景。

方案二:用附加属性封装自动调整逻辑(MVVM友好)

如果你不想写后台代码,想更贴合MVVM模式,可以用WPF的附加属性来封装这个行为,让DataGrid自动监听行的可见性变化并调整列宽。

步骤1:创建附加属性类

新建一个静态类,实现附加属性和监听逻辑:

public static class DataGridResizeBehavior
{
    public static readonly DependencyProperty AutoResizeOnRowVisibilityChangedProperty =
        DependencyProperty.RegisterAttached(
            "AutoResizeOnRowVisibilityChanged",
            typeof(bool),
            typeof(DataGridResizeBehavior),
            new PropertyMetadata(false, OnAutoResizeChanged));

    public static bool GetAutoResizeOnRowVisibilityChanged(DependencyObject obj)
    {
        return (bool)obj.GetValue(AutoResizeOnRowVisibilityChangedProperty);
    }

    public static void SetAutoResizeOnRowVisibilityChanged(DependencyObject obj, bool value)
    {
        obj.SetValue(AutoResizeOnRowVisibilityChangedProperty, value);
    }

    private static void OnAutoResizeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is DataGrid dg && (bool)e.NewValue)
        {
            // 监听新行加载事件,给每行绑定可见性变化监听
            dg.LoadingRow += (s, args) =>
            {
                args.Row.IsVisibleChanged += Row_IsVisibleChanged;
            };

            // 监听ItemsSource变化,确保已有行也被监听
            dg.ItemsSourceChanged += (s, args) =>
            {
                foreach (var item in dg.Items)
                {
                    if (dg.ItemContainerGenerator.ContainerFromItem(item) is DataGridRow row)
                    {
                        row.IsVisibleChanged += Row_IsVisibleChanged;
                    }
                }
            };
        }
    }

    private static void Row_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        if (sender is DataGridRow row && row.DataGridOwner is DataGrid dg)
        {
            // 延迟执行,确保布局更新完成后再调整列宽
            Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() =>
            {
                foreach (var column in dg.Columns)
                {
                    column.Width = new DataGridLength(1, DataGridLengthUnitType.SizeToCells);
                }
            }), DispatcherPriority.Background);
        }
    }
}

步骤2:在XAML中启用附加属性

在你的DataGrid标签里添加这个附加属性即可:

<DataGrid x:Name="dgChangesMade"
          local:DataGridResizeBehavior.AutoResizeOnRowVisibilityChanged="True"
          ItemsSource="{Binding CheckinPaths}">
    <!-- 你的列定义 -->
    <DataGrid.Columns>
        <DataGridTemplateColumn Header="Path" x:Name="dgPath">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <CheckBox />
                        <TextBlock Text="{Binding ., Converter={StaticResource YourPathConverter}}" />
                    </StackPanel>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
        <!-- 其他列 -->
    </DataGrid.Columns>
</DataGrid>

这个方案的好处是逻辑封装在附加属性里,可复用性强,不需要在每个View里写后台代码。如果担心频繁触发调整(比如单个行可见性变化),可以在Row_IsVisibleChanged里加个防抖逻辑,比如用DispatcherTimer延迟50ms执行,避免短时间内多次调整。

注意事项

  • 确保你的DataGrid的CanUserResizeColumns属性没有设为False(默认是True,所以一般没问题)。
  • 对于DataGridTemplateColumn,SizeToCells会根据模板内可见元素的实际宽度计算列宽,完全适配你第一列的CheckBox+TextBlock结构。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 04:16:24