您正在寻找的是Window 上的Closing 事件。它的CancelEventArgs 带有Cancel 类型的bool 属性,可以设置为true,这将取消关闭窗口。使用关闭按钮关闭窗口并按下 Alt+F4 时都会触发关闭事件。
代码隐藏
在代码隐藏场景中,您可以像这样在 XAML 中添加事件处理程序。
<Window Closing="OnClosing" ...>
然后,您将在代码隐藏中创建此事件处理程序并相应地设置 Cancel 属性。
private void OnClosing(object sender, CancelEventArgs e)
{
// 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)
e.Cancel = true;
}
}
事件触发器(非 MVVM)
在 MVVM 中,您可以安装 Microsoft.Xaml.Behaviors.Wpf NuGet 包,它可以替代传统的 Blend 行为 (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>
此解决方案允许您在视图模型中定义命令。
public class MyViewModel
{
public MyViewModel()
{
ClosingCommand = new RelayCommand<CancelEventArgs>(ExecuteClosing);
}
public ICommand ClosingCommand { get; }
private void ExecuteClosing(CancelEventArgs e)
{
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)
e.Cancel = true;
}
}
我在这里不提供ICommand 实现。有关基础知识的更多信息,请参阅:
尽管此解决方案在视图模型上使用命令,但它不兼容 MVVM,因为消息框是一个视图组件,不能驻留在视图模型中。这同样适用于取消事件参数。
MVVM 行为
一种符合 MVVM 的方式可能是创建一个行为来移出确认码。为此,为您的视图模型创建一个包含IsDirty 方法的接口,并在您的视图模型中实现它。
public interface IStatefulViewModel
{
bool IsDirty();
}
public class MyViewModel : IStatefulViewModel
{
// ...your code.
public bool IsDirty()
{
// ...your checks.
}
}
然后,使用 Microsoft.Xaml.Behaviors.Wpf NuGet 包创建一个行为。此行为是可重用的,并且封装了与视图模型分离的关闭逻辑。 Caption 和 Message 依赖属性允许绑定消息框内容。
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. Are you sure you want to close this form?"/>
</b:Interaction.Behaviors>
<!-- ...other markup. -->
</Window>
不要被 &#13;&#10; 字符混淆,它们是 XML 中的换行符 (\n)。