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

WPF DataGrid批量删除行异常:仅首行删除问题求助

问题分析与解决方案

你的问题根本不是竞态条件,而是WPF DataGrid的ItemContainerGenerator机制导致的行容器未及时生成。当你连续调用两个删除方法时,UI线程还没来得及为所有行生成对应的DataGridRow容器,第二个删除方法里row = (DataGridRow)UILog.ItemContainerGenerator.ContainerFromIndex(i)会返回null,导致找不到要删除的scE02条目。而MessageBox.Show会阻塞UI线程,给了DataGrid足够的时间生成所有行容器,所以两个删除都能成功。

下面是具体的修复方案,这也是WPF开发中操作列表控件的标准做法:


1. 改用数据绑定替代直接操作DataGrid.Items

WPF的DataGrid设计初衷是配合数据绑定使用,直接操作Items集合或者依赖ItemContainerGenerator是非常不可靠的,尤其是在快速操作场景下。我们需要将DataGrid绑定到一个ObservableCollection<UILogObject>,然后通过操作这个集合来间接更新UI。

步骤1:定义ObservableCollection数据源

在你的窗口/用户控件类中添加一个ObservableCollection字段和属性(如果使用MVVM,应该放在ViewModel中):

using System.Collections.ObjectModel;
using System.ComponentModel;

// 假设你的窗口类实现了INotifyPropertyChanged(如果没有可以加上)
public partial class YourWindow : Window, INotifyPropertyChanged
{
    private ObservableCollection<UILogObject> _uiLogItems = new ObservableCollection<UILogObject>();
    
    public ObservableCollection<UILogObject> UILogItems
    {
        get => _uiLogItems;
        set
        {
            _uiLogItems = value;
            OnPropertyChanged(nameof(UILogItems));
        }
    }

    // INotifyPropertyChanged实现
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    // 其他现有代码...
}

步骤2:在XAML中绑定DataGrid的ItemsSource

修改你的DataGrid XAML,将ItemsSource绑定到上面定义的UILogItems:

<DataGrid x:Name="UILog" 
          ItemsSource="{Binding UILogItems, RelativeSource={RelativeSource AncestorType=Window}}"
          <!-- 其他原有属性(列定义等)保持不变 -->
          />

步骤3:重构删除和添加行的方法

现在我们直接操作ObservableCollection,不再依赖ItemContainerGenerator:

重构UILogRemoveRow

private void UILogRemoveRow(string entryCode)
{
    // 先找到所有匹配entryCode的项(ToList()避免遍历集合时修改集合的问题)
    var itemsToRemove = UILogItems.Where(item => item.entryType == entryCode).ToList();
    foreach (var item in itemsToRemove)
    {
        UILogItems.Remove(item);
    }
}

重构UILogAddRow

private void UILogAddRow(string entryCode, string entryIdentifier, string entryDetail)
{
    // 直接通过LINQ检查是否存在匹配项
    bool itemExists = UILogItems.Any(item => item.entryType == entryCode);
    if (!itemExists)
    {
        UILogItems.Add(new UILogObject() 
        { 
            entryType = entryCode, 
            identifier = entryIdentifier, 
            detail = entryDetail 
        });
    }
    // 不需要调用UILog.Items.Refresh(),ObservableCollection会自动通知UI更新
}

2. 为什么这个方案能解决问题?

  • ObservableCollection实现了INotifyCollectionChanged接口,当集合中的元素添加/移除时,会自动通知DataGrid更新UI,完全不需要手动操作控件的Items集合或依赖行容器生成。
  • 避免了ItemContainerGenerator的局限性:这个类只会为当前可见的行生成容器,或者在UI线程空闲时生成其他行的容器,快速连续操作时必然会出现找不到行的情况。
  • 代码更加简洁、易维护,符合WPF的MVVM设计思想,也避免了直接操作UI控件带来的各种线程和时机问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 03:51:26