WPF中窗口2触发事件时如何更新窗口1的视图?
问题根源与解决办法
兄弟,你这问题的核心其实是两个窗口各自创建了独立的MainViewModel实例——相当于你在窗口2改了一份文档,窗口1打开的是另一份完全不同的文档,那视图当然不会更新啦!另外还有个小问题:你用List来存集合数据,它不会主动通知视图自己变了,得换成ObservableCollection才行。
具体哪里错了?
- 实例不共享:看你的代码,
Scanner窗口和LoadingHistory窗口都各自new了一个MainViewModel,这俩是完全独立的对象。窗口2修改的是自己的ViewModel数据,窗口1绑定的那个ViewModel根本没收到任何更新,视图自然纹丝不动。 - 集合类型不对:
LoadingHistoryTopView里的Paint、Bake这些集合如果是List<T>的话,就算你往里面加元素,视图也感知不到——因为List不支持集合变更通知,只有ObservableCollection才会在内容变化时主动告诉视图“我变了,快更新!”。
怎么改?
第一步:让两个窗口共享同一个ViewModel实例
最直接的办法就是在应用启动时创建一个全局的ViewModel,然后把它传递给两个窗口,确保它们绑定的是同一个对象。
修改LoadingHistory窗口的构造函数
把原来自己new ViewModel的代码改成接收外部传入的实例:
public partial class LoadingHistory : Window { public List<LoadingHistoryModel> LoadhistoryVM = new List<LoadingHistoryModel>(); public LoadingHistoryTopView LoadhistorytopVM = new LoadingHistoryTopView(); public MainViewModel _MainViewModel; // 接收外部传来的共享ViewModel public LoadingHistory(MainViewModel sharedViewModel) { _MainViewModel = sharedViewModel; InitializeComponent(); DataContext = _MainViewModel; PopulateHistory(); } }
修改Scanner窗口的构造函数
同样接收共享实例,并且用这个实例初始化窗口1:
public partial class Scanner : Window { LoadCurrentBetween objMain = new LoadCurrentBetween(); LoadingHistory objLoadHistory; MainViewModel _MainViewModel; LoadHistoryUC objLoad = new LoadHistoryUC(); // 接收共享ViewModel public Scanner(MainViewModel sharedViewModel) { _MainViewModel = sharedViewModel; // 用同一个实例创建窗口1 objLoadHistory = new LoadingHistory(_MainViewModel); DataContext = _MainViewModel; InitializeComponent(); } private void PaintScan(object sender, RoutedEventArgs e) { // 插入数据库的代码保持不变 DbOperations.InsertScanHistory(_MainViewModel.LoadCurrentViewModelData.Part, Convert.ToInt32(_MainViewModel.LoadCurrentViewModelData.NoofHook), 2, _MainViewModel.LoadCurrentViewModelData.Date, _MainViewModel.LoadCurrentViewModelData.Time); // 调用窗口1的方法更新共享ViewModel的数据 objLoadHistory.PopulateHistory(); MessageBox.Show("PaintScan Done"); } }
在应用启动时创建共享实例
比如在App.xaml.cs或者主窗口里初始化:
// 创建一个全局共享的ViewModel var sharedVM = new MainViewModel(); // 打开两个窗口时都传入这个实例 var scannerWin = new Scanner(sharedVM); var historyWin = new LoadingHistory(sharedVM); scannerWin.Show(); historyWin.Show();
第二步:把集合换成ObservableCollection
修改LoadingHistoryTopView,把所有List换成ObservableCollection,并且确保这个类也实现INotifyPropertyChanged:
public class LoadingHistoryTopView : INotifyPropertyChanged { // 用ObservableCollection替代List private ObservableCollection<PainttopView> _paint = new ObservableCollection<PainttopView>(); public ObservableCollection<PainttopView> Paint { get => _paint; set { _paint = value; NotifyPropertyChanged(nameof(Paint)); } } // 同理修改Bake和Cool集合 private ObservableCollection<BaketopView> _bake = new ObservableCollection<BaketopView>(); public ObservableCollection<BaketopView> Bake { get => _bake; set { _bake = value; NotifyPropertyChanged(nameof(Bake)); } } private ObservableCollection<CooltopView> _cool = new ObservableCollection<CooltopView>(); public ObservableCollection<CooltopView> Cool { get => _cool; set { _cool = value; NotifyPropertyChanged(nameof(Cool)); } } // 其他属性也要正确触发PropertyChanged(示例) private int _hookNumber; public int HookNumber { get => _hookNumber; set { _hookNumber = value; NotifyPropertyChanged(nameof(HookNumber)); } } // INotifyPropertyChanged实现 public event PropertyChangedEventHandler PropertyChanged; private void NotifyPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
第三步:优化PopulateHistory方法
在窗口1的这个方法里,不要每次都重新赋值集合,而是先清空再添加元素,避免绑定失效:
public void PopulateHistory() { LoadhistoryVM = DbOperations.GetLoadHistory(); if (LoadhistoryVM.Count > 0) { // 先清空现有集合,避免重复数据 _MainViewModel.LoadHistoryTopActiveVM.Paint.Clear(); _MainViewModel.LoadHistoryTopActiveVM.Bake.Clear(); _MainViewModel.LoadHistoryTopActiveVM.Cool.Clear(); _MainViewModel.LoadHistoryTopActiveVM.Hooks.Clear(); for (var i = 0; i < LoadhistoryVM[0].GroupPartNumbers.Count(); i++) { switch (LoadhistoryVM[0].GroupPartNumbers[i].LinePosition) { case 2: PainttopView paint = new PainttopView(); paint.Hookno = LoadhistoryVM[0].GroupPartNumbers[i].HookNumber; paint.LinePosition = LoadhistoryVM[0].GroupPartNumbers[i].LinePosition; paint.isEmpty = LoadhistoryVM[0].GroupPartNumbers[i].HookNumber == 0; paint.ImgSource = paint.isEmpty ? "/Views/Image/RedStop.png" : "/Views/Image/Bluering.png"; _MainViewModel.LoadHistoryTopActiveVM.Paint.Add(paint); break; // 其他case代码保持不变 } _MainViewModel.LoadHistoryTopActiveVM.Hooks.Add(LoadhistoryVM[0].GroupPartNumbers[i].HookNumber); if (i == 0) { // 更新属性时确保触发PropertyChanged _MainViewModel.LoadHistoryTopActiveVM.HookNumber = LoadhistoryVM[0].GroupPartNumbers[i].HookNumber; _MainViewModel.LoadHistoryTopActiveVM.LinePosition = LoadhistoryVM[0].GroupPartNumbers[i].LinePosition; _MainViewModel.LoadHistoryTopActiveVM.PartNumber = LoadhistoryVM[0].GroupPartNumbers[i].PartNumber; _MainViewModel.LoadHistoryTopActiveVM.Workorder = LoadhistoryVM[0].GroupPartNumbers[i].Workorder; _MainViewModel.LoadHistoryTopActiveVM.Date = LoadhistoryVM[0].GroupPartNumbers[i].Date; } } } // 不需要重新设置DataContext,因为已经绑定了共享实例 // DataContext = _MainViewModel; }
最后再啰嗦两句
- 多窗口共享数据的核心就是共用同一个ViewModel实例,别各自new各自的。
- 集合一定要用
ObservableCollection,不然视图不知道集合变了。 - 所有绑定的属性都要正确实现
INotifyPropertyChanged,包括子ViewModel里的属性。
内容的提问来源于stack exchange,提问作者RackM
相关产品推荐
相关产品推荐

