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

当第二个ObservableCollection<T>变更时如何更新第一个?WPF数据网格未更新问题

问题分析与解决方案

你的核心问题是未建立监控目录的文件集合(LoadedFiles1)与DataGrid绑定的业务集合(TFiles)之间的实时联动,同时存在跨线程操作UI集合、文件路径处理错误的问题,最终导致DataGrid无法响应目录文件的增删变化。以下是具体修正方案:

1. 核心问题拆解

  • 你注释了LoadedFiles1的CollectionChanged事件订阅,且之前的事件处理逻辑存在参数类型错误(e.NewItems中的元素是FileInfo对象,不是字符串)。
  • FileSystemWatcher的事件在后台线程触发,直接修改UI绑定的ObservableCollection会引发线程异常。
  • FileWatcher中创建FileInfo时使用e.Name仅获取文件名,缺失完整路径,导致后续文件匹配逻辑失效。
  • 未处理删除文件时同步更新TFiles集合的逻辑。

2. 修改后的完整代码

修正FileWatcher类(修复路径与集合线程同步)

internal class FileWatcher
{
    private readonly object filesLock = new object();
    private readonly ObservableCollection<FileInfo> _files = new ObservableCollection<FileInfo>();
    // 用只读属性避免外部直接赋值破坏集合引用
    public ObservableCollection<FileInfo> Files => _files;

    public FileWatcher(string path)
    {
        BindingOperations.EnableCollectionSynchronization(Files, filesLock);

        lock (filesLock)
        {
            foreach (var fileInfo in new DirectoryInfo(path).EnumerateFiles())
            {
                Files.Add(fileInfo);
            }
        }

        var fsw = new FileSystemWatcher(path)
        {
            IncludeSubdirectories = false,
            EnableRaisingEvents = true
        };
        fsw.Created += FileCreated;
        fsw.Deleted += FileDeleted;
    }

    private void FileCreated(object sender, FileSystemEventArgs e)
    {
        lock (filesLock)
        {
            // 使用完整路径创建FileInfo,确保文件信息准确
            Files.Add(new FileInfo(e.FullPath));
        }
    }

    private void FileDeleted(object sender, FileSystemEventArgs e)
    {
        // 用完整路径匹配文件,避免重名文件误删
        var removed = Files.FirstOrDefault(fi => fi.FullPath == e.FullPath);
        if (removed != null)
        {
            lock (filesLock)
            {
                Files.Remove(removed);
            }
        }
    }
}

修正MainWindow后台代码(建立集合联动+线程调度)

public partial class MainWindow : Window
{
    private readonly ObservableCollection<TxtFiles> _tFiles = new ObservableCollection<TxtFiles>();
    private ObservableCollection<FileInfo> _loadedFiles1;

    public MainWindow()
    {
        InitializeComponent();
        var fileWatcher = new FileWatcher(@"C:\User\");
        _loadedFiles1 = fileWatcher.Files;

        // 初始化加载现有文件
        foreach (var file in _loadedFiles1)
        {
            _tFiles.Add(FileInfoToTxtFiles(file.Name));
        }

        DG1.ItemsSource = _tFiles;
        // 订阅集合变更事件,实现实时联动
        _loadedFiles1.CollectionChanged += LoadedFiles_CollectionChanged;
    }

    private void LoadedFiles_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        // 切换到UI线程执行更新,避免跨线程操作异常
        Application.Current.Dispatcher.Invoke(() =>
        {
            if (e.Action == NotifyCollectionChangedAction.Add)
            {
                foreach (FileInfo file in e.NewItems)
                {
                    _tFiles.Add(FileInfoToTxtFiles(file.Name));
                }
            }
            else if (e.Action == NotifyCollectionChangedAction.Remove)
            {
                foreach (FileInfo file in e.OldItems)
                {
                    // 根据文件名匹配并删除对应的业务对象
                    var toRemove = _tFiles.FirstOrDefault(t => t.Name == Path.GetFileNameWithoutExtension(file.Name));
                    if (toRemove != null)
                    {
                        _tFiles.Remove(toRemove);
                    }
                }
            }
        });
    }

    public static TxtFiles FileInfoToTxtFiles(string fileName)
    {
        string[] temp = fileName.Split('_', '.');
        // 增加格式校验,避免索引越界
        if (temp.Length >= 4)
        {
            return new TxtFiles(temp[0], temp[1], temp[2], temp[3]);
        }
        // 格式不符合时返回默认值
        return new TxtFiles(fileName, "", "", "");
    }
}

可选:TxtFiles实现属性变更通知

如果后续需要修改TxtFiles的属性并让DataGrid实时更新,需实现INotifyPropertyChanged接口:

public class TxtFiles : INotifyPropertyChanged
{
    private string _name;
    private string _no1;
    private string _no2;
    private string _no3;

    public string Name
    {
        get => _name;
        set
        {
            _name = value;
            OnPropertyChanged();
        }
    }

    public string No1
    {
        get => _no1;
        set
        {
            _no1 = value;
            OnPropertyChanged();
        }
    }

    public string No2
    {
        get => _no2;
        set
        {
            _no2 = value;
            OnPropertyChanged();
        }
    }

    public string No3
    {
        get => _no3;
        set
        {
            _no3 = value;
            OnPropertyChanged();
        }
    }

    public TxtFiles(string name, string n01, string no2, string no3)
    {
        Name = name;
        No1 = n01;
        No2 = no2;
        No3 = no3;
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

关键说明

  • 线程安全:通过Dispatcher.Invoke将集合更新操作切换到UI线程执行,避免跨线程操作UI控件的异常。
  • 集合联动:在CollectionChanged事件中同时处理文件的添加和删除,保证业务集合与监控目录的文件状态完全同步。
  • 路径准确性:使用e.FullPath创建FileInfo,避免因相对路径导致的文件匹配错误。
  • 异常防护:在文件名解析逻辑中增加格式校验,防止不符合预期的文件名引发索引越界异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 08:31:01