当第二个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
相关产品推荐
相关产品推荐

