WPF MVVM 取消 Window.Closing 事件

7
在使用MVVMLight Toolkit的WPF应用程序中,如果需要取消窗口关闭事件,我想知道您的意见,最好的实现方式是什么。 在Window.Closing事件中,我可以设置e.Cancel = true,这可以防止关闭窗体。要确定是否允许关闭或应该阻止关闭是在ViewModel上下文中的。 一个解决方案可能是定义一个Application变量,并在视图代码后台的普通事件处理程序中查询它? 谢谢

在普通的MVVM中,使用附加属性很简单。但是我不知道在MVVMLight中如何实现... - Gayot Fow
2个回答

17

MVVM Light 提供了 EventToCommand:

这样你就可以在 XAML 中将关闭事件与 VM 绑定。

<Window ...
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        xmlns:command="http://www.galasoft.ch/mvvmlight">
<i:Interaction.Triggers>
  <i:EventTrigger EventName="Closing">
    <command:EventToCommand Command="{Binding ClosingCommand}"
                            PassEventArgsToCommand="True" />
  </i:EventTrigger>
</i:Interaction.Triggers>

并且在虚拟机中:

public RelayCommand<CancelEventArgs> ClosingCommand { get; private set; }

ctor() {
  ClosingCommand = new RelayCommand<CancelEventArgs>(args => args.Cancel = true);
}

如果您不想将 CancelEventArgs 传递给 VM:

您可以采用类似的方法使用一个 Behavior,并且只需使用来自 VM 的简单的 bool(将此 bool 绑定到 Behavior) 来指示关闭事件应该被取消。

更新:

以下示例的下载链接

要使用 Behavior 进行此操作,您可以使用一个如下的 Behavior

internal class CancelCloseWindowBehavior : Behavior<Window> {
  public static readonly DependencyProperty CancelCloseProperty =
    DependencyProperty.Register("CancelClose", typeof(bool),
      typeof(CancelCloseWindowBehavior), new FrameworkPropertyMetadata(false));

  public bool CancelClose {
    get { return (bool) GetValue(CancelCloseProperty); }
    set { SetValue(CancelCloseProperty, value); }
  }

  protected override void OnAttached() {
    AssociatedObject.Closing += (sender, args) => args.Cancel = CancelClose;
  }
}

现在在XAML中:

<i:Interaction.Behaviors>
  <local:CancelCloseWindowBehavior CancelClose="{Binding CancelClose}" />
</i:Interaction.Behaviors>

在此示例中,我有一个 Button 用于切换来自 VM 的布尔属性 CancelClose,该属性指示是否应取消 Closing 事件。这将让您测试 Behavior


是的,这可能是一个解决办法,但我希望尽可能地保持MVVM分离视图和模型的优势。 如何使用Behavior处理?如果我使用布尔值,窗口关闭事件是否可以被取消? - pillesoft
@pillesoft 请查看答案末尾的更新,其中提供了使用“Behavior”进行操作的示例。还有一个下载链接,可以提供一个项目示例供您测试。需要注意的一件事是,MVVM相对于MVC等模式的主要优势在于允许对VM代码进行单元测试。因此,如果您可以在VM中很好地进行逻辑单元测试(在这种情况下,可以使用单元测试项目),我个人不会立即将某种方法标记为错误。 - Viv
我可以使用MVVM Light模式将参数传递给事件参数吗?像这样:<command:EventToCommand Command="{Binding SelectedItemChangedCommand}" PassEventArgsToCommand="True" CommandParameter="{Binding ElementName=MainTreeView, Path=SelectedItem}" /> - mdziadowiec
行为对我很有用,但是如果你有InvokeCommandAction和在关闭事件上的行为,那么行为会首先被调用。 - XAMlMAX

0
您可以使用 Messages 进行控制,例如:
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        Messenger.Default.Register<CloseApplicationMessage>(this, m => Close());
        Loaded += MainWindowLoaded;
        Closing += MainWindowClosing;
    }

    private void MainWindowClosing(object sender, CancelEventArgs e)
    {
        //Ask for saving
        var closingMessage = new ClosingApplicationMessage();
        Messenger.Default.Send(closingMessage);
        if (closingMessage.Cancel)
            e.Cancel = true;
    }
...

MVVM模式的信息:

public class ClosingApplicationMessage
{
    public bool Cancel { get; set; }
}

通过这种方式,在任何你正在监听 ClosingApplicationMessage 的地方,你都可以控制应用程序何时关闭,并且可以取消它。 希望这能帮到你...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接