如何取消MVVM WPF应用程序中的窗口关闭

时间:2016-03-21 11:05:25

标签: c# wpf icommand cancellation

如何在点击取消按钮(或右上角的X或Esc)后取消退出特定表格?

WPF:

b

视图模型:

<Window
  ...
  x:Class="MyApp.MyView"
  ...
/>
  <Button Content="Cancel" Command="{Binding CancelCommand}" IsCancel="True"/>
</Window>

目前的代码不起作用。如果用户选择“不”,我希望用户保持当前状态。在弹出对话框中。 此外,重写CanExecute并没有帮助。它只是禁用按钮。我想允许用户点击按钮,但随后通知他/她,数据将丢失。 也许我应该在按钮上分配一个事件监听器?

编辑:

我在“取消”按钮上设置了弹出窗口。但我仍然无法管理Esc或X按钮(右上角)。我似乎对“取消”按钮感到困惑,因为当我点击X按钮或Esc时执行了Execute方法。

EDIT2:

我改变了问题。它是如何取消取消按钮&#39;。但是,它并不是我想要的。我需要取消Esc或X按钮。 在&#39; MyViewModel&#39;我补充说:

public class MyViewModel : Screen {
  private CancelCommand cancelCommand;
  public CancelCommand CancelCommand {
    get { return cancelCommand; }
  }
  public MyViewModel() {
    cancelCommand = new CancelCommand(this);
  }
}

public class CancelCommand : ICommand {

  public CancelCommand(MyViewModel viewModel) {
    this.viewModel = viewModel;
  }

  public override void Execute(object parameter) {
    if (true) { // here is a real condition
      MessageBoxResult messageBoxResult = System.Windows.MessageBox.Show(
        "Really close?",  "Warning", 
        System.Windows.MessageBoxButton.YesNo);
      if (messageBoxResult == MessageBoxResult.No) { return; }
    }
    viewModel.TryClose(false);
  }

  public override bool CanExecute(object parameter) {
    return true;
  }
}

这解决了我的问题。但是,我需要ICommand来理解,单击了哪个按钮,保存或取消。有没有办法消除事件的使用?

4 个答案:

答案 0 :(得分:6)

您正在尝试在ViewModel类中执行View的工作。让您的View类处理结束请求以及是否应该取消它。

要取消关闭窗口,您可以订阅Closing视图事件,并在显示CancelEventArgs.Cancel后将MessageBox设置为true。

以下是一个例子:

<Window
    ...
    x:Class="MyApp.MyView"
    Closing="OnClosing"
    ...
/>
</Window>

代码背后:

private void OnClosing(object sender, CancelEventArgs e)
{
    MessageBoxResult result = MessageBox.Show("Really close?",  "Warning", MessageBoxButton.YesNo);
    if (result != MessageBoxResult.Yes)
    {
        e.Cancel = true;
    }

    bool shouldClose = ((MyViewModel) DataContext).TryClose(false);
    if(!shouldClose)
    {
        e.Cancel = true;
    }
}

答案 1 :(得分:1)

我不是MVVM专家,但我认为Yusufs的答案不是完全MVVM。另一方面,Torpederos的答案对于仅紧密抵消而言有点复杂。这是我的方法。 在此示例中,我订阅了关闭事件,但始终被取消

private void OnClosing(object sender, CancelEventArgs e)
{
    e.Cancel = true;
    return;
}

在XAML中,我添加了这个

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
<i:Interaction.Triggers>
    <i:EventTrigger EventName="Closing">
        <i:InvokeCommandAction Command="{Binding Close}" />
    </i:EventTrigger>
</i:Interaction.Triggers>

最后是视图模型

public ICommand Close { get; set; }
Close = new RelayCommand(CommandClose);
private void CommandClose(object sender)
{
    if (Dirty)
    {
        // Save your data here
    }
    Environment.Exit(0);
}

在这种方法中,关闭事件首先触发。那取消了结账。之后,将调用交互触发器,并通过RelayCommand触发视图模型中的代码。 在视图模型中,我可以使用视图中不可访问的Dirty标志。

答案 2 :(得分:0)

可以在in the article of Nish Nishant上找到以“视图模型”方式执行此操作的一个很好的例子,他在那里使用附加属性来通过命令连接窗口事件。

附加行为的示例代码(代码的作者:Nish Nishant

public class WindowClosingBehavior {

    public static ICommand GetClosed(DependencyObject obj) {
        return (ICommand)obj.GetValue(ClosedProperty);
    }

    public static void SetClosed(DependencyObject obj, ICommand value) {
        obj.SetValue(ClosedProperty, value);
    }

    public static readonly DependencyProperty ClosedProperty 
        = DependencyProperty.RegisterAttached(
        "Closed", typeof(ICommand), typeof(WindowClosingBehavior),
        new UIPropertyMetadata(new PropertyChangedCallback(ClosedChanged)));

    private static void ClosedChanged(DependencyObject target, DependencyPropertyChangedEventArgs e) {

        Window window = target as Window;

        if (window != null) {

            if (e.NewValue != null) {
                window.Closed += Window_Closed;
            }
            else {
                window.Closed -= Window_Closed;
            }
        }
    }

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

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

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

    private static void ClosingChanged(DependencyObject target, DependencyPropertyChangedEventArgs e) {

        Window window = target as Window;

        if (window != null) {

            if (e.NewValue != null) {
                window.Closing += Window_Closing;
            }
            else {
                window.Closing -= Window_Closing;
            }
        }
    }

    public static ICommand GetCancelClosing(DependencyObject obj) {
        return (ICommand)obj.GetValue(CancelClosingProperty);
    }

    public static void SetCancelClosing(DependencyObject obj, ICommand value) {
        obj.SetValue(CancelClosingProperty, value);
    }

    public static readonly DependencyProperty CancelClosingProperty 
        = DependencyProperty.RegisterAttached(
        "CancelClosing", typeof(ICommand), typeof(WindowClosingBehavior));

    static void Window_Closed(object sender, EventArgs e) {

        ICommand closed = GetClosed(sender as Window);

        if (closed != null) {
            closed.Execute(null);
        }
    }

    static void Window_Closing(object sender, CancelEventArgs e) {

        ICommand closing = GetClosing(sender as Window);

        if (closing != null) {

            if (closing.CanExecute(null)) {
                closing.Execute(null);
            }
            else {

                ICommand cancelClosing = GetCancelClosing(sender as Window);

                if (cancelClosing != null) {
                    cancelClosing.Execute(null);
                }

                e.Cancel = true;
            }
        }
    }
}   

示例如何绑定命令:

<Window 
    x:Class="WindowClosingDemo.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:nsmvvm="clr-namespace:NS.MVVM"
    nsmvvm:WindowClosingBehavior.Closed="{Binding ClosedCommand}"
    nsmvvm:WindowClosingBehavior.Closing="{Binding ClosingCommand}"
    nsmvvm:WindowClosingBehavior.CancelClosing="{Binding CancelClosingCommand}">

命令“ ClosedCommand”,“ ClosingCommand”和“ CancelClosingCommand”应在单独的视图模型中定义。

internal class MainViewModel : ViewModelBase {

    private ObservableCollection<string> log = new ObservableCollection<string>();

    public ObservableCollection<string> Log {
        get { return log; }
    }

    private DelegateCommand exitCommand;

    public ICommand ExitCommand {

        get {

            if (exitCommand == null) {
                exitCommand = new DelegateCommand(Exit);
            }

            return exitCommand;
        }
    }

    private void Exit() {
        Application.Current.Shutdown();
    }

    private DelegateCommand closedCommand;

    public ICommand ClosedCommand {

        get {

            if (closedCommand == null) {
                closedCommand = new DelegateCommand(Closed);
            }

            return closedCommand;
        }
    }

    private void Closed() {
        log.Add("You won't see this of course! Closed command executed");
        MessageBox.Show("Closed");
    }

    private DelegateCommand closingCommand;

    public ICommand ClosingCommand {

        get {

            if (closingCommand == null) {
                closingCommand = new DelegateCommand(ExecuteClosing, CanExecuteClosing);
            }

            return closingCommand;
        }
    }

    private void ExecuteClosing() {
        log.Add("Closing command executed");
        MessageBox.Show("Closing");
    }

    private bool CanExecuteClosing() {

        log.Add("Closing command execution check");

        return MessageBox.Show("OK to close?", "Confirm", MessageBoxButton.YesNo) == MessageBoxResult.Yes;
    }

    private DelegateCommand cancelClosingCommand;

    public ICommand CancelClosingCommand {

        get {

            if (cancelClosingCommand == null) {
                cancelClosingCommand = new DelegateCommand(CancelClosing);
            }

            return cancelClosingCommand;
        }
    }

    private void CancelClosing() {
        log.Add("CancelClosing command executed");
        MessageBox.Show("CancelClosing");
    }
}

答案 3 :(得分:0)

这是直接从 ViewModel 取消关闭窗口的另一个示例。

查看

<Window x:Class="WpfApplicationMvvmLight.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
    xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Platform"
    Title="MainWindow" Height="350" Width="525">
<i:Interaction.Triggers>
    <i:EventTrigger EventName="Closing">
        <cmd:EventToCommand Command="{Binding Path=ClosingCommand}" PassEventArgsToCommand="True" />
    </i:EventTrigger>
</i:Interaction.Triggers>
<Grid>
    <TextBlock>content...</TextBlock>
</Grid>

视图模型

using GalaSoft.MvvmLight.CommandWpf;
using System.ComponentModel;
using System.Windows;

namespace WpfApplicationMvvmLight
{
  class SampleViewModel
  {
    public SampleViewModel() {
      _closingCommand = new RelayCommand<CancelEventArgs>(OnClosingCommand);
    }

    private RelayCommand<CancelEventArgs> _closingCommand;
    public RelayCommand<CancelEventArgs> ClosingCommand {
      get {
        return _closingCommand;
      }
    }

    private void OnClosingCommand(CancelEventArgs e) {
      //display your custom message box here..
      var result = MessageBox.Show("Do you want to close?", "", MessageBoxButton.YesNoCancel);
      //set e.Cancel to true to prevent the window from closing
      e.Cancel = result != MessageBoxResult.Yes;
    }
  }
}

背后的代码

using System.Windows;

namespace WpfApplicationMvvmLight
{
  public partial class MainWindow : Window
  {
    public MainWindow() {
      InitializeComponent();
      this.DataContext = new SampleViewModel();
    }
  }
}

这是参考。 MVVM close window event