如何设置 DataGridColumn 的宽度以适应内容(“自动”),但完全填充 MVVM 中 DataGrid 的可用空间?
Posted
技术标签:
【中文标题】如何设置 DataGridColumn 的宽度以适应内容(“自动”),但完全填充 MVVM 中 DataGrid 的可用空间?【英文标题】:How can I set the width of a DataGridColumn to fit contents ("Auto"), but completely fill the available space for the DataGrid in MVVM? 【发布时间】:2011-06-27 05:22:27 【问题描述】:我有一个包含一些数据的 WPF DataGrid
。我想设置列的宽度,以便内容适合并且永远不会被裁剪(相反,水平滚动条应该变得可见)。此外,我希望DataGrid
能够填满整个可用位置(我正在使用DockPanel
)。我正在使用以下代码(简化):
<DataGrid ItemsSource="Binding Table">
<DataGrid.Columns>
<DataGridTextColumn MinWidth="100" Width="Auto" Header="Column 1" Binding="Binding Property1" />
<DataGridTextColumn MinWidth="200" Width="Auto" Header="Column 2" Binding="Binding Property2" />
</DataGrid.Columns>
</DataGrid>
这显然不适用于 Width="Auto"
,因为它总是看起来像这样:
这显然看起来很难看。我希望选择整行,或者更好的是选择列来填充整个宽度,但正如大家所见,这不起作用。
如果我改用Width="*"
,列的内容会被裁剪,这对我来说更糟。
我找到了similar question here,并在那里发布了解决方法。这可能有效,但我正在使用 MVVM 模式,所以 ItemsSource 在 ViewModel 中得到更新,我想不出从那里做的方法,因为我无法访问 ActualWidth
属性DataGridColumn
的。另外,如果可能的话,我想只在 XAML 中进行。
如果有任何帮助,我将不胜感激。谢谢!
编辑:由于我仍然不知道该怎么做,我开始了一个小赏金。我会很高兴有人提出可以解决我的问题的建议。再次感谢!
编辑 2:在 saus 的回答之后,我再次考虑了这些选项。问题是我需要在应用程序运行期间更新Width
和MinWidth
属性,所以不仅在加载窗口之后。我已经尝试过做类似的事情
column.Width = new DataGridLength(1, DataGridLengthUnitType.Auto);
column.MinWidth = column.ActualWidth;
column.Width = new DataGridLength(1, DataGridLengthUnitType.Star);
在DataGrid
的底层ItemsSource
更新时触发的某些事件。但是,这不起作用,因为在 Auto
上设置 Width
后,ActualWidth
属性似乎没有改变。是否可以选择以某种方式“重新绘制”它以更新 ActualWidth
属性?谢谢!
【问题讨论】:
我不确定我是否了解您想在这里做什么。如果列的宽度比DataGrid
更宽,您想要一个垂直滚动条并且不希望文本换行?你的意思是水平滚动条吗?否则,这对我来说毫无意义,但也许我在这里很慢:)
感谢您的提示!当然我的意思是水平滚动条,否则它没有任何意义;-)
【参考方案1】:
我建议使用您链接到的解决方法。它确实有效。在视图的代码隐藏中,在 InitializeComponent() 之后将以下内容添加到构造函数中:
Griddy.Loaded += SetMinWidths; // I named my datagrid Griddy
并将 SetMinWidths 定义为:
public void SetMinWidths(object source, EventArgs e )
foreach (var column in Griddy.Columns)
column.MinWidth = column.ActualWidth;
column.Width = new DataGridLength(1, DataGridLengthUnitType.Star);
我假设您不想使用此解决方案的原因是您认为 MVVM 禁止代码隐藏中的代码。但由于这完全是视图特定的逻辑,我相信在这种情况下是合理的。整个“MVVM 禁止代码隐藏中的代码”这件事有点误解。
但是,如果您受样式指南的约束,或者您希望此逻辑可用于您应用中的所有数据网格,您可以创建一个附加的行为来完成这项工作:
public class SetMinWidthToAutoAttachedBehaviour
public static bool GetSetMinWidthToAuto(DependencyObject obj)
return (bool)obj.GetValue(SetMinWidthToAutoProperty);
public static void SetSetMinWidthToAuto(DependencyObject obj, bool value)
obj.SetValue(SetMinWidthToAutoProperty, value);
// Using a DependencyProperty as the backing store for SetMinWidthToAuto. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SetMinWidthToAutoProperty =
DependencyProperty.RegisterAttached("SetMinWidthToAuto", typeof(bool), typeof(SetMinWidthToAutoAttachedBehaviour), new UIPropertyMetadata(false, WireUpLoadedEvent));
public static void WireUpLoadedEvent(DependencyObject d, DependencyPropertyChangedEventArgs e)
var grid = (DataGrid)d;
var doIt = (bool)e.NewValue;
if (doIt)
grid.Loaded += SetMinWidths;
public static void SetMinWidths(object source, EventArgs e)
var grid = (DataGrid)source;
foreach (var column in grid.Columns)
column.MinWidth = column.ActualWidth;
column.Width = new DataGridLength(1, DataGridLengthUnitType.Star);
然后对于要应用此行为的任何数据网格,添加以下内容:
<DataGrid ItemsSource="Binding Table" local:SetMinWidthToAutoAttachedBehaviour.SetMinWidthToAuto="true">
然后你的良心以及你的代码隐藏就会变得清晰。
【讨论】:
嗨,非常感谢!实际上,我不认为完全查看特定内容不应该在代码隐藏中。问题在于 ViewModel 中DataGrid
的内容发生了变化,因此 View 不知道何时必须再次调用 SetMinWidths
。也许一旦我的NotifyPropertyChanged
方法被调用,DataGrid 就会发生变化?我认为附加行为也存在同样的问题,但我可能会找到解决该问题的方法。很快就会回来提供更多信息。再次感谢!
查看我的帖子,我编辑了它:不幸的是,我没有让它像那样工作。原因见上文。
我们是否需要取消订阅该事件以避免内存泄漏?
对我来说,这种模式将所有列缩小到最小尺寸,因此正好相反。
此解决方案的另一个缺陷是它绝对无法编辑列的宽度。【参考方案2】:
在模板列内使用带有文本换行的 TextBlock:
<DataGrid ItemsSource="Binding Table">
<DataGrid.Columns>
<DataGridTextColumn MinWidth="100" Width="Auto" Header="Column 1"
Binding="Binding Property1" />
<DataGridTemplateColumn Width="*" Header="Column 2">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="Binding Property2" TextWrapping="Wrap" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
【讨论】:
感谢您的建议,但我不想换行。如上所述,我宁愿在文本太宽时立即获得垂直滚动条。 模板列的意义在于,您可以在 DataTemplate 中使用适合您需要的任何控件,例如这个怎么样:
对于列的 MinWidth,设置与 dataGrid 的 ScrollContent 的 Width 的绑定,并使用一个转换器将此宽度除以列数
您需要为转换器提供一些代码,但这会保持您的 MVVM 结构完整。
可能是一个很长的尝试,但没有时间尝试。
编辑:我对此进行了更多思考,并且有一个 pb:如果你有一个大柱子和几个小柱子,它就不会很好地工作。我假设所有列的宽度都相同,这显然不是这里的情况。
不过,您可能想以这种方式进行探索。在 Widths 上使用带转换器的绑定是我能想到的唯一可行的方法:因为在计算列宽时基本上需要考虑两个条件,所以没有简单的方法可以做到这一点。
【讨论】:
好的,谢谢你的想法,我会考虑的。但是,我确实有一个列比其他列宽得多,因此它不适用于第一个建议。我认为你是对的,我需要对列的宽度使用绑定(这也是我的想法),但是为每列找到正确的 minwidth/width 仍然是一个挑战^^ 肯定会的。 ^^ 如果我可以添加一条建议:如果可以避免,请不要将 MinWidth 属性与 Width 属性结合使用。它会导致严重的头痛。例如:将 minWidth 设置为 100,然后将宽度设置为 80,控件的宽度将是 80:如果您通过程序设置,宽度将接管 minWidth...【参考方案4】:我将Event
添加到DataGrid
中,根据DataGrid
的实际宽度重新定义它们的宽度:
private static void OnLoaded(object sender, RoutedEventArgs e)
DataGrid dg = sender as DataGrid;
foreach (var column in dg.Columns)
column.Width = new DataGridLength(dg.ActualWidth / dg.Columns.Count, DataGridLengthUnitType.Pixel);
行:
column.Width = new DataGridLength(1, DataGridLengthUnitType.Star);
完全没有意义。 DataGridLengthUnitType.Star
的定义将列的宽度缩小到最小宽度,并使它们的宽度静态且不可编辑。
【讨论】:
【参考方案5】:强制最后一列占用datagrid中的所有剩余空间
grid.Columns[grid.Columns.Count -1].Width = new
DataGridLength(250, DataGridLengthUnitType.Star);
【讨论】:
【参考方案6】:将其用于您希望适合其内容的任何列: 宽度 = "SizeToCells"
【讨论】:
以上是关于如何设置 DataGridColumn 的宽度以适应内容(“自动”),但完全填充 MVVM 中 DataGrid 的可用空间?的主要内容,如果未能解决你的问题,请参考以下文章
如何同时输入 , 和 .然后将其格式化为 .用于所有文化中的 DataGridColumn?
动态设置dataGridColumn的ItemRenderer
如何绑定 DataGridColumn.Visibility?