WPF DataGrid列自适应内容且末列填余空,支持横向滚动的实现方案
WPF DataGrid 列宽自适应与滚动条需求解决方案
问题场景与需求
- 使用WPF的DataGrid展示ViewModel列表,数据通过按钮绑定
FolderBrowserDialog从文件夹加载;启动时若存在历史路径,会自动填充DataGrid,因此存在已填充和未填充两种状态 - 核心需求:
- 数据填充后,所有列自动适配内容宽度,末列填充剩余空白以支持整行选中
- 窗口缩小至DataGrid宽度小于列总宽度时,显示横向滚动条,且列宽保持内容宽度
- 上述效果需在按钮触发填充和启动自动填充两种场景下均生效
已尝试的两种方案
方案1:设置列Width为Auto,末列设为*
- 优点:整行可选中
- 缺点:窗口调整时列被挤压且无横向滚动条;空DataGrid填充数据后列未自适应内容
- 代码:
<Grid Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="3" Background="#f7f5f2" Margin="10"> <DataGrid x:Name="ModList" ItemsSource="{Binding Mods}" Style="{DynamicResource DataGridStyle1}" CellStyle="{DynamicResource DataGridCellStyle1}" ColumnHeaderStyle="{DynamicResource DataGridColumnHeaderStyle1}" RowStyle="{DynamicResource DataGridRowStyle1}" RowDetailsTemplate="{DynamicResource DataGridRowDetailsTemplate1}" Margin="10" dd:DragDrop.IsDragSource="True" dd:DragDrop.IsDropTarget="True" dd:DragDrop.SelectDroppedItems="True" dd:DragDrop.DropHandler="{Binding}" dd:DragDrop.DropTargetAdornerBrush="#484D54"> <DataGrid.Columns> <DataGridTemplateColumn x:Name="DataGridColumnEnabled" Width="Auto"> <DataGridTemplateColumn.CellTemplate> <DataTemplate> <CheckBox x:Name="CheckBoxIsEnabled" IsChecked="{Binding IsEnabled, UpdateSourceTrigger=PropertyChanged}" Command="{Binding DataContext.ToggleCheckBoxCommand, RelativeSource={RelativeSource AncestorType=Window}}"/> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn> <DataGridTextColumn x:Name="DataGridColumnLoadorder" Header="Loadorder" IsReadOnly="True" Width="Auto" Binding="{Binding LoadOrder, UpdateSourceTrigger=PropertyChanged}" CanUserSort="False"/> <DataGridTextColumn x:Name="DataGridColumnMod" Header="Mod" IsReadOnly="True" Width="Auto" Binding="{Binding DisplayName}" CanUserSort="False" VirtualizingPanel.VirtualizationMode="Standard"/> <DataGridTemplateColumn x:Name="DataGridColumnNotification" IsReadOnly="True" Width="Auto" CanUserSort="False"> <DataGridTemplateColumn.CellTemplate> <DataTemplate> <Image Source="/WarningIcon.png" Height="16" Width="16" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Cursor="Help" Visibility="{Binding HasConflicts}"> <ToolTipService.ToolTip> <ToolTip Content="Mod(s) detected altering the same asset(s). 
This may be intentional, please check before committing loadorder."/> </ToolTipService.ToolTip> </Image> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn> <DataGridTextColumn x:Name="DataGridColumnAuthor" Header="Author" IsReadOnly="True" Width="Auto" Binding="{Binding Author}" CanUserSort="False"/> <DataGridTextColumn x:Name="DataGridColumnVersion" Header="Version" IsReadOnly="True" Width="Auto" Binding="{Binding Version}" CanUserSort="False"/> <DataGridTextColumn x:Name="DataGridColumnSource" Header="Source" IsReadOnly="True" Width="*" Binding="{Binding Source}" CanUserSort="False"/> </DataGrid.Columns> </DataGrid> </Grid>
方案2:方案1基础上后台设置列MinWidth
- 优点:窗口调整时显示横向滚动条
- 缺点:空DataGrid填充数据后列仍未自适应内容
- 代码:
- XAML新增事件:
SizeChanged="ModList_SizeChanged" - 后台代码:
- XAML新增事件:
public partial class MainWindow : Window { // Other code private void ModList_SizeChanged(object sender, EventArgs e) { DataGridColumnEnabled.MinWidth = DataGridColumnEnabled.ActualWidth; DataGridColumnLoadorder.MinWidth = DataGridColumnLoadorder.ActualWidth; DataGridColumnMod.MinWidth = DataGridColumnMod.ActualWidth; DataGridColumnNotification.MinWidth = DataGridColumnNotification.ActualWidth; DataGridColumnAuthor.MinWidth = DataGridColumnAuthor.ActualWidth; DataGridColumnVersion.MinWidth = DataGridColumnVersion.ActualWidth; DataGridColumnSource.MinWidth = 200; } }
可行实现方案
针对方案2的缺点,需要在数据填充完成后主动触发列宽自适应,同时保留MinWidth的设置逻辑,具体步骤如下:
1. 监听数据集合变化
在窗口构造函数中监听ViewModel的Mods集合变化,确保数据加载完成后触发自适应:
public MainWindow() { InitializeComponent(); var viewModel = DataContext as YourViewModelType; viewModel.Mods.CollectionChanged += Mods_CollectionChanged; } private void Mods_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { // 仅在添加数据或重置集合后执行自适应 if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add || e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Reset) { // 延迟执行,确保数据渲染完成 Dispatcher.BeginInvoke(new Action(() => { AutoFitColumns(); UpdateColumnMinWidths(); }), System.Windows.Threading.DispatcherPriority.Render); } }
2. 实现列宽自适应与MinWidth更新方法
private void AutoFitColumns() { // 遍历非末列,设置为Auto适配内容 foreach (var column in ModList.Columns) { if (column != DataGridColumnSource) { column.Width = DataGridLength.Auto; } } // 末列设置为*填充剩余空间 DataGridColumnSource.Width = new DataGridLength(1, DataGridLengthUnitType.Star); } private void UpdateColumnMinWidths() { // 更新所有列的MinWidth为当前实际宽度,防止窗口缩小时被挤压 DataGridColumnEnabled.MinWidth = DataGridColumnEnabled.ActualWidth; DataGridColumnLoadorder.MinWidth = DataGridColumnLoadorder.ActualWidth; DataGridColumnMod.MinWidth = DataGridColumnMod.ActualWidth; DataGridColumnNotification.MinWidth = DataGridColumnNotification.ActualWidth; DataGridColumnAuthor.MinWidth = DataGridColumnAuthor.ActualWidth; DataGridColumnVersion.MinWidth = DataGridColumnVersion.ActualWidth; DataGridColumnSource.MinWidth = Math.Max(200, DataGridColumnSource.ActualWidth); }
3. 调整SizeChanged事件逻辑
修改原事件方法,避免重复设置MinWidth,仅处理滚动条显示逻辑:
private void ModList_SizeChanged(object sender, EventArgs e) { var totalMinWidth = ModList.Columns.Sum(c => c.MinWidth); if (ModList.ActualWidth < totalMinWidth) { ModList.HorizontalScrollBarVisibility = ScrollBarVisibility.Visible; } else { DataGridColumnSource.Width = new DataGridLength(1, DataGridLengthUnitType.Star); ModList.HorizontalScrollBarVisibility = ScrollBarVisibility.Auto; } }
额外优化
- 启动自动填充场景:在
Window_Loaded事件中调用AutoFitColumns()和UpdateColumnMinWidths(),确保窗口加载完成后列宽适配 - 对于
DataGridTemplateColumn,确保模板内控件的HorizontalAlignment设为Stretch,避免限制列宽自适应
内容的提问来源于stack exchange,提问作者Merlock
相关产品推荐
相关产品推荐

