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

WPF DataGrid的ItemContainerGenerator状态为NotStarted的原因及解决方法

WPF DataGrid ItemContainerGenerator状态问题分析与解决

问题描述

我有一个WPF DataGrid,当滚动到顶部或数据量不足无需滚动时,ItemContainerGenerator.Status处于NotStarted状态,仅在滚动时状态才会改变。请问这是什么原因?如何强制ItemContainerGenerator.Status切换为就绪状态?

相关代码片段

ItemContainerGenerator状态变更事件处理

private void ItemContainerGenerator_StatusChanged(object sender, EventArgs e) {
    if ((sender as ItemContainerGenerator).Status == GeneratorStatus.ContainerGenerated) {
        
        if (obj != null) {
            Win.Dispatcher.BeginInvoke(new Action(() => {
                Win.SelectedItem = obj;
                Win.ScrollIntoView(obj);
            }), DispatcherPriority.Render);
        }
    }
}

窗口打开逻辑(关联ShowAsync)

protected override async Task ExecuteAsync(OleMenuCmdEventArgs e) 
{
   var record = GetRecord(); //从列表框获取要在DataGrid中选中的记录
   ToolWindowPane win = Package.FindToolWindow(typeof(Pane), key, false);

   if (win!= null) {
     MyToolWindow.MyDictionary[record.Name].SelectRecord(record.Name);
     return MyToolWindow.ShowAsync(key, false);
   }
  ///...首次打开窗口等其他代码
}

窗口类核心代码

public MyToolWindow() //构造函数
{
  SelectRecord(record);
}

private void SelectRecord(string record) 
{  
   var obj = LookForRecordToSelect(record);
   datagrid.SelectedItem = obj;
   datagrid.ScrollIntoView(obj);
}

补充:首次选择能生效可能是因为逻辑定义在构造函数中。

原因分析

WPF的ItemContainerGenerator默认采用虚拟化加载机制:只有当Item进入可视区域时,才会生成对应的UI容器(比如DataGridRow)。当DataGrid处于顶部或数据量少到无需滚动时,所有Item可能还没触发容器生成逻辑,导致Status停留在NotStarted;而滚动操作会触发可视区域变化,强制生成可见Item的容器,从而改变Status。

另外,非首次打开窗口时调用SelectRecord的时机过早——此时DataGrid还没完成布局渲染,容器生成逻辑尚未启动,所以ItemContainerGenerator无法进入就绪状态。

解决方法

方法1:强制触发布局与容器生成

通过强制DataGrid完成布局,再手动触发容器生成逻辑:

private void SelectRecord(string record) 
{  
   var obj = LookForRecordToSelect(record);
   // 强制完成布局测量与排列
   datagrid.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
   datagrid.Arrange(new Rect(datagrid.DesiredSize));

   // 监听容器生成状态,完成后执行选择
   if (datagrid.ItemContainerGenerator.Status != GeneratorStatus.ContainerGenerated)
   {
       void OnStatusChanged(object s, EventArgs args)
       {
           var generator = s as ItemContainerGenerator;
           if (generator.Status == GeneratorStatus.ContainerGenerated)
           {
               datagrid.SelectedItem = obj;
               datagrid.ScrollIntoView(obj);
               generator.StatusChanged -= OnStatusChanged; // 移除监听避免重复触发
           }
       }
       datagrid.ItemContainerGenerator.StatusChanged += OnStatusChanged;
       // 触发布局更新,启动容器生成
       datagrid.UpdateLayout();
   }
   else
   {
       // 容器已就绪,直接执行选择
       datagrid.SelectedItem = obj;
       datagrid.ScrollIntoView(obj);
   }
}

方法2:延迟执行选择逻辑

非首次打开窗口时,先等待窗口显示完成,再通过Dispatcher以渲染优先级执行选择操作,确保DataGrid已完成初始化:

protected override async Task ExecuteAsync(OleMenuCmdEventArgs e) 
{
   var record = GetRecord();
   ToolWindowPane win = Package.FindToolWindow(typeof(Pane), key, false);

   if (win!= null) {
     await win.ShowAsync(key, false); // 等待窗口显示完成
     // 延迟到渲染阶段执行选择
     await win.Dispatcher.BeginInvoke(new Action(() => {
         MyToolWindow.MyDictionary[record.Name].SelectRecord(record.Name);
     }), DispatcherPriority.Render);
     return;
   }
  ///...首次打开窗口等其他代码
}

方法3:关闭虚拟化(仅小数据量场景)

如果数据量不大,可以关闭DataGrid的虚拟化,让所有Item容器一次性生成,ItemContainerGenerator会快速进入就绪状态:

<DataGrid x:Name="datagrid" VirtualizingStackPanel.IsVirtualizing="False"/>

注意:此方法会增加内存占用,大数据量场景不推荐使用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 00:23:13