如何在折叠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
相关产品推荐
相关产品推荐

