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

WPF DataGrid列ActualWidth异常 等待ObservableCollection更新方法

核心原因

WPF DataGrid的Auto宽度列实际渲染宽度计算,属于UI渲染队列中低优先级任务。你在数据更新后同步调用UpdateLayout()、Items.Refresh(),或是用Thread.Sleep()阻塞线程,都无法等待列宽计算完成——Thread.Sleep()会直接卡住UI线程,阻塞整个布局渲染流程,此时读取到的ActualWidth是未计算完成的旧值/默认值,自然会出现错位。调试模式下执行速度慢,列宽计算有足够时间跑完,所以能拿到正确结果。

可落地解决方案

方案1:Dispatcher排队等待渲染完成(推荐)

不要在更新ObservableCollection的同步代码块中直接读取列宽,将生成TextBox的逻辑投递到WPF Dispatcher队列的最低优先级,等所有布局、渲染、交互事件全部处理完成后再执行,此时拿到的ActualWidth是完全准确的。
修正后的代码如下:

// 1. 先完成ObservableCollection的数据填充、ItemsSource绑定操作
popup.DataGridSelectedObjects.ItemsSource = yourRevitSelectedElementCollection;
popup.DataGridSelectedObjects.UpdateLayout();

// 2. 将列宽读取、TextBox生成逻辑投递到ContextIdle优先级队列
popup.Dispatcher.BeginInvoke(new Action(() =>
{
    StackRegexPanel.Children.Clear();
    foreach (DataGridColumn dc in popup.DataGridSelectedObjects.Columns)
    {
        AddRegexItemToStackpanel(dc.Header.ToString(), dc);
    }
}), System.Windows.Threading.DispatcherPriority.ContextIdle);

// 原有生成TextBox的方法不需要修改
public void AddRegexItemToStackpanel(string parameterName, DataGridColumn dc)
{
    TextBox printTextBlock = new TextBox();
    printTextBlock.Width = dc.ActualWidth;
    printTextBlock.Margin = new Thickness(0.5, 0, 0, 0);
    printTextBlock.Name = dc.Header.ToString().Replace("__", "_");
    StackRegexPanel.Children.Add(printTextBlock);
}

方案2:监听LayoutUpdated事件兜底(适配极端场景)

如果方案1在个别场景下仍有偏差,可以给DataGrid注册一次性的LayoutUpdated事件,等所有列宽计算完成(所有列的ActualWidth大于0)时再生成TextBox,执行完成后立刻注销事件避免重复触发:

// 在更新ItemsSource的位置注册事件
popup.DataGridSelectedObjects.LayoutUpdated += GenerateMatchWidthTextBoxes;

private void GenerateMatchWidthTextBoxes(object sender, EventArgs e)
{
    DataGrid dg = sender as DataGrid;
    // 校验所有列宽已完成计算
    if (dg.Columns.Any(col => col.ActualWidth <= double.Epsilon))
    {
        return;
    }
    // 列宽计算完成,执行生成逻辑
    StackRegexPanel.Children.Clear();
    foreach (DataGridColumn dc in dg.Columns)
    {
        AddRegexItemToStackpanel(dc.Header.ToString(), dc);
    }
    // 注销事件,避免重复触发
    dg.LayoutUpdated -= GenerateMatchWidthTextBoxes;
}
注意事项
  • 禁止在UI线程使用Thread.Sleep()等待UI更新,该方法会阻塞UI线程,让布局、渲染流程完全暂停,加再长的延迟都等不到列宽计算完成。
  • 如果DataGrid开启了列虚拟化,被滚出视口、未实际渲染的列可能拿不到准确宽度,性能允许的情况下可以临时将EnableColumnVirtualization设为false,等TextBox生成完成后再恢复。
  • Revit插件场景下所有WPF UI操作必须在Revit主UI线程执行,不要在后台线程中操作控件,避免触发跨线程异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 17:30:50