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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 06:42:36