C# 将ViewModel方法绑定到视图';s Alt+;F4和关闭按钮

C# 将ViewModel方法绑定到视图';s Alt+;F4和关闭按钮,c#,wpf,mvvm,C#,Wpf,Mvvm,我已经创建了一个自定义方法来关闭视图模型中的表单,并将其绑定到按钮。如何使标题栏中的默认关闭按钮和Alt+F4运行相同的命令 public void Close(object parameter) { // Check if any field has been edited if (IsDirty()) { string message = "You have unsaved changes.\n\nAre you sure you want

我已经创建了一个自定义方法来关闭视图模型中的表单,并将其绑定到按钮。如何使标题栏中的默认关闭按钮和Alt+F4运行相同的命令

public void Close(object parameter)
{
    // Check if any field has been edited
    if (IsDirty())
    {
        string message = "You have unsaved changes.\n\nAre you sure you want to close this form?";
        string title = "Close Window";
        MessageBoxResult result = MessageBox.Show(message, title, MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);

        if (result == MessageBoxResult.Cancel)
            return;
    }

    employeeWindow.Close();
}

我还是WPF和MVVM的新手,我只是不了解已有的任何解决方案,也不了解如何定制它们来运行上述代码。

您正在寻找的是上的事件。它带有一个类型为
bool
的属性,可以设置为
true
,这将取消关闭窗口。使用“关闭”按钮关闭窗口并按Alt+F4时,将触发关闭事件

代码隐藏 在代码隐藏场景中,您可以像这样在XAML中添加一个事件处理程序

<Window Closing="OnClosing" ...>
事件触发器(非MVVM) 在MVVM中,您可以安装NuGet软件包,该软件包替代了传统的混合行为(
System.Windows.Interactivity
)。您可以使用
EventTrigger
将事件绑定到视图模型中的命令。在本例中,您将
CancelEventArgs
直接传递给命令

<Window ...>
   <b:Interaction.Triggers>
      <b:EventTrigger EventName="Closing">
         <b:InvokeCommandAction Command="{Binding ClosingCommand}"
                                PassEventArgsToCommand="True"/>
      </b:EventTrigger>
   </b:Interaction.Triggers>
   <!-- ...other markup. -->
</Window>
然后,使用NuGet包创建一个行为。此行为是可重用的,并封装了与视图模型解耦的关闭逻辑。
标题
消息
依赖项属性允许绑定消息框内容

public class WindowClosingBehavior : Behavior<Window>
{
   public static readonly DependencyProperty CaptionProperty = DependencyProperty.Register(
      nameof(Caption), typeof(string), typeof(WindowClosingBehavior), new PropertyMetadata(string.Empty));

   public static readonly DependencyProperty MessageProperty = DependencyProperty.Register(
      nameof(Message), typeof(string), typeof(WindowClosingBehavior), new PropertyMetadata(string.Empty));

   public string Caption
   {
      get => (string)GetValue(CaptionProperty);
      set => SetValue(CaptionProperty, value);
   }

   public string Message
   {
      get => (string)GetValue(MessageProperty);
      set => SetValue(MessageProperty, value);
   }

   protected override void OnAttached()
   {
      base.OnAttached();
      AssociatedObject.Closing += OnClosing;
   }

   protected override void OnDetaching()
   {
      base.OnDetaching();
      AssociatedObject.Closing -= OnClosing;
   }

   private void OnClosing(object sender, CancelEventArgs e)
   {
      if (!(AssociatedObject.DataContext is IStatefulViewModel statefulViewModel))
         return;

      if (!statefulViewModel.IsDirty())
         return;

      e.Cancel = ConfirmClosing();
   }

   private bool ConfirmClosing()
   {
      var result = MessageBox.Show(
         Message,
         Caption,
         MessageBoxButton.OKCancel,
         MessageBoxImage.Warning,
         MessageBoxResult.Cancel);

      return result == MessageBoxResult.Cancel;
   }
}
公共类WindowClosingBehavior:行为
{
公共静态只读DependencyProperty CaptionProperty=DependencyProperty.Register(
名称(标题)、类型(字符串)、类型(WindowClosingBehavior)、新属性元数据(string.Empty));
公共静态只读DependencyProperty MessageProperty=DependencyProperty.Register(
nameof(消息)、typeof(字符串)、typeof(WindowClosingBehavior)、new PropertyMetadata(string.Empty));
公共字符串标题
{
get=>(字符串)GetValue(CaptionProperty);
set=>SetValue(CaptionProperty,value);
}
公共字符串消息
{
get=>(字符串)GetValue(MessageProperty);
set=>SetValue(MessageProperty,value);
}
受保护的覆盖无效附加()
{
base.onatached();
AssociatedObject.Closing+=OnClosing;
}
附加时受保护的覆盖无效()
{
base.OnDetaching();
AssociatedObject.Closing-=OnClosing;
}
私有void OnClosing(对象发送方,CancelEventArgs e)
{
如果(!(AssociatedObject.DataContext是IStatefulViewModel statefulViewModel))
回来
如果(!statefulViewModel.IsDirty())
回来
e、 取消=确认关闭();
}
私有布尔确认关闭()
{
var result=MessageBox.Show(
消息
说明文字
MessageBoxButton.ok取消,
MessageBoxImage。警告,
MessageBoxResult.Cancel);
返回结果==MessageBoxResult.Cancel;
}
}
将行为附加到窗口。请注意,您可以在任何窗口上执行此操作

<Window ...>
   <b:Interaction.Behaviors>
      <local:WindowClosingBehavior Caption="Close Window"
                                   Message="You have unsaved changes.&#13;&#10;&#13;&#10;Are you sure you want to close this form?"/>
   </b:Interaction.Behaviors>
   <!-- ...other markup. -->
</Window>


不要被
和#13混淆
字符,这些是XML中的换行符(
\n
)。

这是否回答了您的问题?太棒了,这就是我想要的一切,我遵循了你的MVVM行为。感谢您的解释。您所说的“MessageBox是一个视图组件,不能驻留在视图模型中”是什么意思。我的视图是由视图模型创建的,因为如果视图模型无法从数据库加载员工,我不希望创建或显示视图。这不符合MVVM吗?@smally-MVVM是关于从业务逻辑中剥离用户界面。这意味着您的视图模型中不得有任何视图组件引用,无论是
窗口
用户控件
还是任何其他控件。所以不,这是不符合MVVM的。
public interface IStatefulViewModel
{
   bool IsDirty();
}
public class MyViewModel : IStatefulViewModel
{
   // ...your code.

   public bool IsDirty()
   {
      // ...your checks.
   }
}
public class WindowClosingBehavior : Behavior<Window>
{
   public static readonly DependencyProperty CaptionProperty = DependencyProperty.Register(
      nameof(Caption), typeof(string), typeof(WindowClosingBehavior), new PropertyMetadata(string.Empty));

   public static readonly DependencyProperty MessageProperty = DependencyProperty.Register(
      nameof(Message), typeof(string), typeof(WindowClosingBehavior), new PropertyMetadata(string.Empty));

   public string Caption
   {
      get => (string)GetValue(CaptionProperty);
      set => SetValue(CaptionProperty, value);
   }

   public string Message
   {
      get => (string)GetValue(MessageProperty);
      set => SetValue(MessageProperty, value);
   }

   protected override void OnAttached()
   {
      base.OnAttached();
      AssociatedObject.Closing += OnClosing;
   }

   protected override void OnDetaching()
   {
      base.OnDetaching();
      AssociatedObject.Closing -= OnClosing;
   }

   private void OnClosing(object sender, CancelEventArgs e)
   {
      if (!(AssociatedObject.DataContext is IStatefulViewModel statefulViewModel))
         return;

      if (!statefulViewModel.IsDirty())
         return;

      e.Cancel = ConfirmClosing();
   }

   private bool ConfirmClosing()
   {
      var result = MessageBox.Show(
         Message,
         Caption,
         MessageBoxButton.OKCancel,
         MessageBoxImage.Warning,
         MessageBoxResult.Cancel);

      return result == MessageBoxResult.Cancel;
   }
}
<Window ...>
   <b:Interaction.Behaviors>
      <local:WindowClosingBehavior Caption="Close Window"
                                   Message="You have unsaved changes.&#13;&#10;&#13;&#10;Are you sure you want to close this form?"/>
   </b:Interaction.Behaviors>
   <!-- ...other markup. -->
</Window>