C# WPF子窗口关闭后更新父窗口数据问题求助
解决子窗口关闭后更新父窗口数据的方案
针对你遇到的子窗口关闭后父窗口数据不更新的问题,核心原因是当前代码中子窗口的保存操作异步执行但未等待完成就关闭窗口,同时父窗口用Thread.Sleep这种不可靠的方式等待刷新,以下是几种可行的解决方案:
方案一:确保子窗口保存完成后再关闭,父窗口同步刷新
步骤1:修改子窗口ViewModel的保存逻辑,等待接口调用完成
在EditUserWindowViewModel的SaveUser命令中,将异步操作改为等待完成后再关闭窗口:
SaveUser = new CustomCommand(async () => { // 等待用户编辑接口执行完成 await EditUsers(); // 找到当前窗口并关闭 foreach (Window window in Application.Current.Windows) { if (window.DataContext == this) { CloseWin(window); break; // 找到后退出循环,避免无效遍历 } } });
步骤2:修改父页面ViewModel的编辑命令,移除无效等待并直接刷新
在ProfilePageViewModel的Edit命令中,去掉Thread.Sleep,利用ShowDialog()的阻塞特性,等子窗口关闭后直接刷新数据:
// 如果你的CustomCommand支持异步委托,用以下写法 Edit = new CustomCommand(async () => { EditUserWindow euw = new EditUserWindow(); euw.ShowDialog(); // 阻塞直到子窗口关闭 // 此时保存操作已完成,刷新用户数据 await GetUserId(); }); // 如果CustomCommand不支持异步,可改为同步调用(不推荐,会阻塞UI) Edit = new CustomCommand(() => { EditUserWindow euw = new EditUserWindow(); euw.ShowDialog(); _ = GetUserId(); // 用_忽略异步返回值,避免编译警告 });
注意:如果你的CustomCommand原本只支持同步委托,需要扩展它以支持异步:
public class CustomCommand : ICommand { private readonly Func<Task> _executeAsync; private bool _canExecute = true; public CustomCommand(Func<Task> executeAsync) { _executeAsync = executeAsync; } public event EventHandler CanExecuteChanged; public bool CanExecute(object parameter) => _canExecute; public async void Execute(object parameter) { _canExecute = false; CanExecuteChanged?.Invoke(this, EventArgs.Empty); try { await _executeAsync(); } finally { _canExecute = true; CanExecuteChanged?.Invoke(this, EventArgs.Empty); } } }
方案二:订阅子窗口的Closed事件刷新数据
如果不想修改子窗口的关闭逻辑,可以在父页面中监听子窗口的关闭事件,触发数据刷新:
Edit = new CustomCommand(() => { EditUserWindow euw = new EditUserWindow(); // 订阅窗口关闭事件 euw.Closed += (sender, e) => { _ = GetUserId(); // 刷新用户数据 }; euw.ShowDialog(); });
这种方式同样需要确保子窗口的保存操作在关闭前完成(参考方案一的步骤1),否则刷新可能拿到旧数据。
方案三:用事件聚合器解耦父子窗口(适合大型项目)
如果项目需要更高的解耦性,可以使用事件聚合器,让子窗口在保存完成后发布事件,父页面订阅事件并刷新:
1. 定义更新事件类
public class UserUpdatedEvent { public UserApi UpdatedUser { get; set; } }
2. 子窗口发布事件
在EditUserWindowViewModel中注入事件聚合器(比如Prism的IEventAggregator,或自己实现简单版本),保存完成后发布事件:
private readonly IEventAggregator _eventAggregator; public EditUserWindowViewModel(IEventAggregator eventAggregator) { _eventAggregator = eventAggregator; Task.Run(GetUserId); SaveUser = new CustomCommand(async () => { await EditUsers(); // 获取更新后的用户数据(可选,也可以让父页面自行拉取) var updatedUser = await Api.GetAsync<UserApi>(SingInWindowViewModel.UsId, "User"); // 发布用户更新事件 _eventAggregator.GetEvent<UserUpdatedEvent>().Publish(updatedUser); // 关闭窗口 foreach (Window window in Application.Current.Windows) { if (window.DataContext == this) { CloseWin(window); break; } } }); // ...其他逻辑 }
3. 父页面订阅事件
在ProfilePageViewModel中订阅事件,收到事件后更新数据:
private readonly IEventAggregator _eventAggregator; private SubscriptionToken _userUpdatedToken; public ProfilePageViewModel(IEventAggregator eventAggregator) { _eventAggregator = eventAggregator; // 订阅用户更新事件 _userUpdatedToken = _eventAggregator.GetEvent<UserUpdatedEvent>().Subscribe(OnUserUpdated); Task.Run(GetUserId); Task.Run(GetAlbumsList); // ...其他初始化逻辑 } private void OnUserUpdated(UserApi updatedUser) { ProfileUser = updatedUser; SignalChanged("ProfileUser"); } // 记得在ViewModel销毁时取消订阅,避免内存泄漏 public void Dispose() { if (_userUpdatedToken != null) { _eventAggregator.GetEvent<UserUpdatedEvent>().Unsubscribe(_userUpdatedToken); } }
额外优化建议
- 移除
Thread.Sleep:这种硬编码等待时间的方式完全不可靠,依赖网络环境,应该用异步等待代替。 - 提取公共逻辑:
MasterWinViewModel和ProfilePageViewModel中都有GetUserId方法,可以把它提取到公共服务类中,避免重复代码。 - 异步操作规范:所有异步数据获取操作建议用
await而不是Task.Run直接包裹,避免不必要的线程切换。
内容的提问来源于stack exchange,提问作者memes
相关产品推荐
相关产品推荐

