【问题标题】:How to cancel window closing in MVVM WPF application如何在 MVVM WPF 应用程序中取消窗口关闭
【发布时间】:2016-07-07 20:43:25
【问题描述】:

单击“取消”按钮(或右上角的 X,或 Esc)后,如何取消退出特定表单?

WPF:

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

视图模型:

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;
  }
}

当前代码不起作用。如果在弹出对话框中选择“否”,我希望用户留在当前表单上。 此外,覆盖 CanExecute 也无济于事。它只是禁用按钮。我想让用户点击按钮,然后通知他/她,数据将丢失。 也许我应该在按钮上分配一个事件监听器?

编辑:

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

编辑2:

我改变了问题。这是“如何取消取消按钮”。然而,这不是我想要的。我需要取消 Esc 或 X 按钮。 在“MyViewModel”中我添加:

        protected override void OnViewAttached(object view, object context) {
            base.OnViewAttached(view, context);
            (view as MyView).Closing += MyViewModel_Closing;
        }

        void MyViewModel_Closing(object sender, System.ComponentModel.CancelEventArgs e) {
            if (true) {
                MessageBoxResult messageBoxResult = System.Windows.MessageBox.Show(
                  "Really close?",  "Warning", 
                  System.Windows.MessageBoxButton.YesNo);
                if (messageBoxResult == MessageBoxResult.No) {
                    e.Cancel = true;
                }
            }
        }

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

【问题讨论】:

  • 您的viewModel.TryClose(false) 函数是否向您的视图发送事件以关闭对话框?如果是这样,您可以从 xaml 代码中删除 IsCancel="true"。该部分导致表单关闭。
  • @qqww2 如果我删除 IsCancel="true" 然后如果我单击 Esc 它不会关闭窗口。我希望在 Esc 上关闭窗口。
  • 注册一个KeyBinding 到你的命令。 Here 就是一个例子。

标签: c# wpf icommand cancellation


【解决方案1】:

这是另一个直接从 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

【讨论】:

    【解决方案2】:

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

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

    这是一个例子:

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

    后面的代码:

    private void OnClosing(object sender, CancelEventArgs e)
    {
        var result = MessageBox.Show("Really close?",  "Warning", MessageBoxButton.YesNo);
        if (result != MessageBoxResult.Yes)
        {
            e.Cancel = true;
        }
    
        // OR, if triggering dialog via view-model:
    
        bool shouldClose = ((MyViewModel) DataContext).TryClose();
        if(!shouldClose)
        {
            e.Cancel = true;
        }
    }
    

    【讨论】:

    • 这里的问题是 View 不知道是否应该关闭它。我看不到如何访问 ViewModel 数据。
    • 好的。能否详细解释一下关闭窗口的算法是什么?
    • 我将 ViewModel 中的处理程序分配给 OnClosing 甚至。就像我在 EDIT2 之后所说的那样。但是,在这种情况下,我无法区分如何关闭。我的意思是,不添加额外的标志,我将在 ICommand 中设置。
    • viewModel.TryClose(false) 中的false 是否意味着它是从 UI 或代码调用的?此外,编辑后的代码在 MVVM 中不是一种理想的方式。
    • 我不确定我是否能清楚地理解你,但我编辑了调用视图模型类的答案。
    【解决方案3】:

    我不是 MVVM 专家,但在我看来,Yusufs 的回答并不完全是 MVVM。另一方面,鱼雷的答案对于仅关闭取消有点复杂。这是我的方法。 在这个例子中,我订阅了关闭事件,但它总是被取消

    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 标志。

    【讨论】:

      【解决方案4】:

      可以在 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");
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-09-04
        • 1970-01-01
        • 2016-09-04
        相关资源
        最近更新 更多