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

如何在WPF/MVVM应用的ViewModel中注册路由事件

问题分析与解决方案

你的思路存在误区:路由事件是WPF视觉树UI元素之间的通信机制,ViewModel不属于视觉树,无法直接订阅路由事件,强行绑定会打破MVVM的关注点分离原则(让ViewModel依赖UI层)。针对「应用关闭时触发ViewModel保存数据」的需求,以下是几种符合MVVM规范的实现方式:


方案1:直接在View的关闭事件中调用ViewModel方法

这是最简洁的实现,适合当前场景:

修改MainWindow.xaml.cs中的Window_Closing方法,直接从DataContext获取ViewModel并调用Save:

private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
    Debug.WriteLine("MainWindow Window_Closing");
    // 获取ViewModel并调用保存方法
    if (DataContext is MainWindowViewModel viewModel)
    {
        viewModel.Save();
    }
}

此时你之前定义的CloseTheShop路由事件可以直接移除,因为不需要用它来传递关闭信号。


方案2:使用ICommand绑定关闭事件(更符合MVVM规范)

通过WPF行为库将Window.Closing事件绑定到ViewModel中的命令,彻底实现View与ViewModel的解耦:

步骤1:安装行为库

在NuGet中安装Microsoft.Xaml.Behaviors.Wpf包。

步骤2:修改MainWindow.xaml

添加行为命名空间,并绑定Closing事件到ViewModel的命令:

<Window x:Class="RoutedEventA.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:vws="clr-namespace:RoutedEventA.Views"
        xmlns:vms="clr-namespace:RoutedEventA.ViewModels"
        xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
        mc:Ignorable="d"
        Title="MainWindow" Height="200" Width="400">

    <i:Interaction.Triggers>
        <!-- 将Closing事件绑定到ViewModel的SaveCommand -->
        <i:EventTrigger EventName="Closing">
            <i:InvokeCommandAction Command="{Binding SaveCommand}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>

    <Window.DataContext>
        <vms:MainWindowViewModel />
    </Window.DataContext>

    <Grid>
        <vws:MainWindowView/>
    </Grid>
</Window>

步骤3:在ViewModel中实现命令

添加RelayCommand实现(通用命令基类),并定义SaveCommand:

RelayCommand.cs

using System;
using System.Windows.Input;

namespace RoutedEventA
{
    public class RelayCommand : ICommand
    {
        private readonly Action _execute;
        private readonly Func<bool>? _canExecute;

        public event EventHandler? CanExecuteChanged;

        public RelayCommand(Action execute, Func<bool>? canExecute = null)
        {
            _execute = execute ?? throw new ArgumentNullException(nameof(execute));
            _canExecute = canExecute;
        }

        public bool CanExecute(object? parameter) => _canExecute?.Invoke() ?? true;

        public void Execute(object? parameter) => _execute();

        public void RaiseCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
    }
}

修改MainWindowViewModel.cs

namespace RoutedEventA.ViewModels;
internal class MainWindowViewModel
{
    public ICommand SaveCommand { get; }

    public MainWindowViewModel()
    {
        Debug.WriteLine("MainWindowViewModel ctor");
        SaveCommand = new RelayCommand(Save);
    }

    public void Save()
    {
        Debug.WriteLine("MainWindow Save");
        // 在这里实现数据保存逻辑
    }
}

为什么不推荐用路由事件实现ViewModel通信?

  • 路由事件依赖WPF视觉树,ViewModel不是UI元素,无法参与路由事件的传递链;
  • 强行让ViewModel订阅路由事件会引入UI层依赖(如RoutedEvent、EventManager),违反MVVM「ViewModel独立于UI」的核心原则;
  • 对于View到ViewModel的交互,命令模式或直接调用(在View的事件处理中)是更简洁、规范的方案。

内容的提问来源于stack exchange,提问作者Aad Slingerland

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 18:22:32