C# 如何正确处理wpf MVVM中的窗口关闭事件

C# 如何正确处理wpf MVVM中的窗口关闭事件,c#,wpf,mvvm,mvvm-light,C#,Wpf,Mvvm,Mvvm Light,我知道这个问题已经被问过很多次了,但我会尽量具体一些 我是WPF/MVVM的初学者,在我的项目中使用Galasoft的MVVM Light Toolkit 我有一个视图,其中包含一个表单,用户在其中输入一些患者详细信息。当他们单击“关闭”(X)按钮时,我想检查他们是否输入了内容,如果输入了,请询问他们是否希望在关闭前使用(是、否和取消)选项进行保存。我做了一些研究,发现很多人都在暗示EventToCommand这样的功能 XAML <Window xmlns:i="clr-names

我知道这个问题已经被问过很多次了,但我会尽量具体一些

我是WPF/MVVM的初学者,在我的项目中使用Galasoft的MVVM Light Toolkit

我有一个视图,其中包含一个表单,用户在其中输入一些患者详细信息。当他们单击“关闭”(X)按钮时,我想检查他们是否输入了内容,如果输入了,请询问他们是否希望在关闭前使用(是、否和取消)选项进行保存。我做了一些研究,发现很多人都在暗示EventToCommand这样的功能

XAML

<Window
   xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
   xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WPF45"
   DataContext="{Binding Main, Source={StaticResource Locator}}">
   <i:Interaction.Triggers>
      <i:EventTrigger EventName="Closing">
         <cmd:EventToCommand Command="{Binding OnClosingCommand}" 
            PassEventArgsToCommand="True"/>
      </i:EventTrigger>
   </i:Interaction.Triggers>
...
</Window>

...
查看模型

public class MainViewModel : ViewModelBase
{
   public RelayCommand<CancelEventArgs> OnClosingCommand { get; set; }

   public MainViewModel()
   {
      this.OnClosingCommand = 
         new RelayCommand<CancelEventArgs>(this.OnClosingCommandExecuted);
   }

   private void OnClosingCommandExecuted(CancelEventArgs cancelEventArgs)
   {
      // logic to check if view model has updated since it is loaded
      if (mustCancelClosing)
      {
         cancelEventArgs.Cancel = true;
      } 
   }
}
public类MainViewModel:ViewModelBase
{
public RelayCommand OnClosingCommand{get;set;}
公共主视图模型()
{
this.OnClosingCommand=
新的RelayCommand(此.OnClosingCommandExecuted);
}
private void OnClosingCommandExecuted(CancelEventArgs CancelEventArgs)
{
//检查视图模型加载后是否已更新的逻辑
如果(必须取消关闭)
{
cancelEventArgs.Cancel=true;
} 
}
}
以上示例取自

然而,MVVM Light Toolkit的创建者自己说,这打破了MVVM模式试图实现的关注分离,因为它将属于视图的事件参数(在本例中为
CancelEventArgs
)传递给视图模型。他在这篇文章中这样说


所以我的问题是,在不破坏MVVM模式的情况下,处理此类问题的正确方法是什么。如果方向正确,我们将不胜感激

我不是在假装绝对真理,但我喜欢下面的方法。
基本视图模型有一个
RelayCommand
/
DelegateCommand
如下所示:

public ICommand ClosingCommand { get; }
其中,
ICommand.Execute
实现为:

/// <summary>
/// Executes an action, when user closes a window, displaying this instance, using system menu.
/// </summary>
protected virtual void Closing()
{
}
反过来,UI使用附加的行为来处理
窗口。关闭

public static class WindowClosingBehavior
{
    public static readonly DependencyProperty ClosingProperty = DependencyProperty.RegisterAttached(
            "Closing", 
            typeof(ICommand), 
            typeof(WindowClosingBehavior),
            new UIPropertyMetadata(new PropertyChangedCallback(ClosingChanged)));

    public static ICommand GetClosing(DependencyObject obj)
    {
        return (ICommand)obj.GetValue(ClosingProperty);
    }

    public static void SetClosing(DependencyObject obj, ICommand value)
    {
        obj.SetValue(ClosingProperty, value);
    }

    private static void ClosingChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
    {
        var window = target as Window;
        if (window != null)
        {
            if (e.NewValue != null)
                window.Closing += Window_Closing;
            else
                window.Closing -= Window_Closing;
        }
    }

    private static void Window_Closing(object sender, CancelEventArgs e)
    {
        var window = sender as Window;
        if (window != null)
        {
            var closing = GetClosing(window);
            if (closing != null)
            {
                if (closing.CanExecute(null))
                    closing.Execute(null);
                else
                    e.Cancel = true;
            }
        }
    }
}
XAML(假设视图模型是窗口的
DataContext
):


@哦,对不起
GetClosing
/
SetClosing
这里只是常规的附加属性值包装。增加了遗漏的方法。
public static class WindowClosingBehavior
{
    public static readonly DependencyProperty ClosingProperty = DependencyProperty.RegisterAttached(
            "Closing", 
            typeof(ICommand), 
            typeof(WindowClosingBehavior),
            new UIPropertyMetadata(new PropertyChangedCallback(ClosingChanged)));

    public static ICommand GetClosing(DependencyObject obj)
    {
        return (ICommand)obj.GetValue(ClosingProperty);
    }

    public static void SetClosing(DependencyObject obj, ICommand value)
    {
        obj.SetValue(ClosingProperty, value);
    }

    private static void ClosingChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
    {
        var window = target as Window;
        if (window != null)
        {
            if (e.NewValue != null)
                window.Closing += Window_Closing;
            else
                window.Closing -= Window_Closing;
        }
    }

    private static void Window_Closing(object sender, CancelEventArgs e)
    {
        var window = sender as Window;
        if (window != null)
        {
            var closing = GetClosing(window);
            if (closing != null)
            {
                if (closing.CanExecute(null))
                    closing.Execute(null);
                else
                    e.Cancel = true;
            }
        }
    }
}
behaviors:WindowClosingBehavior.Closing="{Binding ClosingCommand}"