WPF中MvvmCross框架下子ViewModel如何处理MainWindow Closing事件
WPF平台MvvmCross框架下窗口关闭时子ViewModel执行保存逻辑的规范实现
MvvmCross WPF端的ViewDisappearing/ViewDestroy等生命周期方法,仅响应MvvmCross自有导航栈触发的页面切换、导航回退动作,WPF原生窗口的Closing事件属于WPF平台自身的窗口生命周期范畴,不会触发MvvmCross的页面生命周期回调,这是框架的设计边界问题。
以下是完全符合MVVM解耦要求、无业务逻辑代码后置的实现方案:
实现步骤
1. 视图层做事件到命令的纯映射(无业务逻辑)
借助XAML行为和MvvmCross原生命令绑定组件,直接在MainWindow的XAML中把窗口Closing事件绑定到对应ViewModel的命令,不需要在xaml.cs中编写任何业务代码:
<views:MvxWindow x:Class="YourApp.Wpf.Views.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:views="clr-namespace:MvvmCross.Platforms.Wpf.Views;assembly=MvvmCross.Platforms.Wpf" xmlns:mvx="clr-namespace:MvvmCross.Binding.Wpf;assembly=MvvmCross.Binding.Wpf" xmlns:i="http://schemas.microsoft.com/xaml/behaviors"> <i:Interaction.Triggers> <i:EventTrigger EventName="Closing"> <mvx:MvxEventToCommand Command="{Binding WindowClosingCommand}" PassEventArgsToCommand="True"/> </i:EventTrigger> </i:Interaction.Triggers> <!-- 窗口原有内容、子视图承载区域 --> </views:MvxWindow>
若不想引入Xaml.Behaviors依赖,也可以在MainWindow.xaml.cs中仅写一行事件透传代码,不掺杂任何业务逻辑:
Closing += (s, e) => (DataContext as MainWindowViewModel)?.WindowClosingCommand.Execute(e);,这种纯视图层事件转发的写法同样符合MVVM规范,不属于违规的代码后置。
2. 主窗口ViewModel广播关闭通知
主窗口对应的ViewModel不需要处理具体的子视图保存逻辑,只需要通过MvvmCross原生的IMvxMessenger弱消息组件,向全局广播窗口即将关闭的通知即可:
using System.ComponentModel; using MvvmCross.Commands; using MvvmCross.Plugin.Messenger; using MvvmCross.ViewModels; public class MainWindowViewModel : MvxViewModel { private readonly IMvxMessenger _messenger; public IMvxCommand<CancelEventArgs> WindowClosingCommand { get; } public MainWindowViewModel(IMvxMessenger messenger) { _messenger = messenger; WindowClosingCommand = new MvxCommand<CancelEventArgs>(OnWindowClosing); } private void OnWindowClosing(CancelEventArgs args) { // 仅广播消息,不处理任何具体业务 _messenger.Publish(new MainWindowClosingMessage(this)); } } // 定义全局窗口关闭消息 public class MainWindowClosingMessage : MvxMessage { public MainWindowClosingMessage(object sender) : base(sender) { } }
3. 子ViewModel订阅消息执行保存逻辑
所有需要在关闭时保存数据的子ViewModel,只需要注入IMvxMessenger订阅关闭消息即可,不需要和主窗口、主窗口ViewModel产生任何强引用:
using MvvmCross.Plugin.Messenger; using MvvmCross.ViewModels; public class YourChildViewModel : MvxViewModel { private readonly IMvxMessenger _messenger; private readonly MvxSubscriptionToken _closingSubscriptionToken; // 需要持久化保存的示例属性 public string CustomConfig { get; set; } public int RecentSelectedIndex { get; set; } public YourChildViewModel(IMvxMessenger messenger) { _messenger = messenger; // 订阅窗口关闭消息,持有token防止消息被GC回收 _closingSubscriptionToken = _messenger.Subscribe<MainWindowClosingMessage>(HandleAppClosing); } private void HandleAppClosing(MainWindowClosingMessage msg) { // 在这里实现自定义属性保存逻辑,比如写入本地配置、持久化到数据库/文件 Properties.Settings.Default.CustomConfig = CustomConfig; Properties.Settings.Default.RecentSelectedIndex = RecentSelectedIndex; Properties.Settings.Default.Save(); // 若需要拦截关闭逻辑,可自行扩展消息参数传递Cancel标识 } public override void ViewDestroy(bool viewFinishing = true) { base.ViewDestroy(viewFinishing); // View销毁时取消订阅,避免内存泄漏 if (viewFinishing) { _messenger.Unsubscribe<MainWindowClosingMessage>(_closingSubscriptionToken); } } }
方案合规性说明
- 所有业务逻辑(属性保存、关闭拦截判断)全部位于ViewModel层,视图层仅做事件和命令的绑定映射,无业务侵入
- 子ViewModel和主窗口完全解耦,通过MvvmCross官方提供的信使组件通信,符合框架设计范式
- 不需要重写MvvmCross框架源码、不需要破坏现有MVVM层级结构,所有依赖均为官方原生组件
内容的提问来源于stack exchange,提问作者HarshBangera
相关产品推荐
相关产品推荐

