UpdateLayout 调用完全是多余的。修改Width 将自动触发布局通道。此外,为了提高性能,应将两个迭代合并为一个迭代。
完整的实现在用户体验方面非常值得怀疑:如果用户调整列的宽度,您将覆盖并重置他的自定义,这会提供糟糕的体验。此外,网格可能会不断调整自身大小,这看起来和感觉也很奇怪。您至少应该将DataGrid.CanUserResizeColumns 设置为false。
我相信添加一个允许用户隐藏特定列以压缩视图的列过滤器具有更高的价值。
从您发布的代码中,无法准确判断发生了什么或可能导致问题的原因。 DataGrid 控件的实际布局配置及其上下文显然未公开。
但是,问题可能与DataGrid 如何计算每列的宽度有关,以防您为DataGrid 提供一个固定的Width,并且此值会导致内容超出可用视口宽度(在这种情况下会出现ScrollViewer):
a) 如果每列宽度设置为 Auto,DataGrid 将简单地为每列提供所需的最小宽度。
b) 如果列的宽度设置为星号* 大小,则分配的宽度是相对于可用剩余空间计算的。
为了计算一个合理的值,算法会使用DataGrid的有效大小。
关键是,当使用ScrollViewer 时,虚拟宽度将是无限的。现在,如果算法将最后一列的宽度设置为无限,你会得到一个非常小的滚动条。因此,infinite 不是一个合理的值。实际上,无法计算最终宽度。这就是为什么算法回退到使用DataGrid 的有效宽度,即ScrollViewer.ViewportWidth。结果是所有列都必须压缩到可用的视口空间中,导致列剪裁其标题和单元格内容。
我可以提供两种解决方案。
第一个解决方案是为最后一列使用固定的Width,例如1000 DIP。
该解决方案的扩展性非常差。最后一列的宽度要么太小,要么太极端,没有多大意义(这就是为什么无法计算最后一列的合理宽度的原因——在这种情况下什么是合理的?)。
第二个更好的解决方案将尝试检测ScrollViewer 是否可以滚动:在这种情况下,最后一列将至少部分看不见。这会为Auto 的最后一列生成合理的Width:所需的最小空间。
另一种情况,当ScrollViewer 无法滚动时,所有列都在视口中可见。这导致* 的Width 合理,以便让最后一列填充剩余空间:
private void OnLoadingRow(object sender, DataGridRowEventArgs e)
{
var dataGrid = sender as DataGrid;
foreach (var dataGridColumn in dataGrid.Columns)
{
dataGridColumn.Width = 0;
dataGridColumn.Width = DataGridLength.Auto;
}
if (TryFindVisualChildElementByName(dataGrid, string.Empty, out ScrollViewer scrollViewer)
&& scrollViewer.ScrollableWidth == 0)
{
dataGrid.Columns.Last().Width = new DataGridLength(1, DataGridLengthUnitType.Star);
}
}
public static bool TryFindVisualChildElementByName<TChild>(
DependencyObject parent,
string childElementName,
out TChild resultElement) where TChild : FrameworkElement
{
resultElement = null;
if (parent is Popup popup)
{
parent = popup.Child;
if (parent == null)
{
return false;
}
}
for (var childIndex = 0; childIndex < VisualTreeHelper.GetChildrenCount(parent); childIndex++)
{
DependencyObject childElement = VisualTreeHelper.GetChild(parent, childIndex);
if (childElement is TChild frameworkElement)
{
if (string.IsNullOrWhiteSpace(childElementName)
|| frameworkElement.Name.Equals(childElementName, StringComparison.OrdinalIgnoreCase))
{
resultElement = frameworkElement;
return true;
}
}
if (TryFindVisualChildElementByName(childElement, childElementName, out resultElement))
{
return true;
}
}
return false;
}