使用“X”按钮或 ESC 键关闭 WPF 窗口时的确认

本文关键字:WPF 窗口 确认 按钮 ESC 使用 | 更新日期: 2023-09-27 18:37:00

当使用单击"X"按钮或按 ESC 键关闭桌面应用程序中的 WPF 窗口时,如何请求确认?
我想用最少的代码来制作它。
类似的问题在这里,但在 MVVM Light 上,代码太多。

使用“X”按钮或 ESC 键关闭 WPF 窗口时的确认

利用OnClosing虚拟方法:

protected override void OnClosing(CancelEventArgs e)
{
    // show the message box here and collect the result
    // if you want to stop it, set e.Cancel = true
    e.Cancel = true;
}

该内部代码可能如下所示:

var result = MessageBox.Show(...);
if (result == DialogResult.{SomeEnumVal}) { e.Cancel = true; }

我正在寻找一种更 MVVM 的方式来做到这一点。 所以这就是对我有用的方法。

窗口代码

<Window x:Class="My.Namespace.Wpf.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    mc:Ignorable="d" 
    Closing="Window_Closing">
<i:Interaction.Triggers>
    <i:EventTrigger EventName="Closing">
        <i:InvokeCommandAction Command="{Binding ExitApplicationCommand}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />
    </i:EventTrigger>
</i:Interaction.Triggers>

背后的代码

/// <summary>
/// Handles the Closing event of the Window control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="System.ComponentModel.CancelEventArgs"/> instance containing the event data.</param>
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
    e.Cancel = !_viewModel.ShouldCloseApp;
}

视图模型中的命令

public bool ShouldCloseApp { get; private set; }
private RelayCommand<Window> _exitApplicationCommand;
public RelayCommand<Window> ExitApplicationCommand
{
    get
    {
        if (_exitApplicationCommand == null)
        {
            _exitApplicationCommand = new RelayCommand<Window>(exitApplicationCommand);
        }
        return _exitApplicationCommand;
    }
}
/// <summary>
/// This closes a specified window.  If you pass the main window, then this application 
/// will exit.  This is because the application shut down mode is set to OnMainWindowClose.
/// </summary>
/// <param name="window">The window to close.</param>
private void exitApplicationCommand(Window window)
{
    try
    {
        DialogService.ShowConfirmation(
            UIStrings.MainWindowViewModel_ExitProgramHeader,
            UIStrings.MainWindowViewModel_ExitProgramMessage,
            UIStrings.MainWindowViewModel_ExitProgramAcceptText,
            UIStrings.MainWindowViewModel_ExitProgramCancelText,
            (DialogResult result) =>
            {
                if ((result.Result.HasValue) && (result.Result.Value))
                {
                    if (ElectroTekManager.Manager.ConnectedElectroTek != null)
                    {
                        SendToStatusOperation operation = new SendToStatusOperation(ElectroTekManager.Manager.ConnectedElectroTek, (operationResult, errorMessage) =>
                        {
                            if (operationResult != FirmwareOperation.OperationResult.Success)
                            {
                                log.Debug(string.Format("{0} {1}", CautionStrings.MainWindowViewModel_LogMsg_UnableToSendToStatus, errorMessage));
                            }
                            else if (!string.IsNullOrEmpty(errorMessage))
                            {
                                log.Debug(errorMessage);
                            }
                            Application.Current.Dispatcher.Invoke(new Action(() => closeApp(window)));
                        });
                        operation.Execute();
                    }
                    else
                    {
                        closeApp(window);
                    }
                }
            });
    }
    catch (Exception ex)
    {
        log.Debug(CautionStrings.MainWindowViewModel_LogMsg_FailedToShowConfirmation, ex);
    }
}
/// <summary>
/// Closes the application.
/// </summary>
/// <param name="window">The window.</param>
private void closeApp(Window window)
{
    ShouldCloseApp = true;
    Dispose();
    Application.Current.Shutdown();
}

确认后,我调用Application.Current.Shutdown()。 这会再次触发代码中的关闭事件,但不会再次触发 exit 命令。

AI 发现恕我直言,附加行为的好解决方案。添加此功能是仅将一个命名空间和一个属性添加到窗口标记:

MainWindow.xaml

<Window x:Class="WpfClosingByEscape.MainWindow"
    ...
    xmlns:bhv="clr-namespace:WpfClosingByEscape.Behaviors"
    bhv:WindowClosingBehavior.Enabled="True"
    >
...
</Window>

行为类中的其他所有内容:

private static void OnValueChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (!(d is Window))
        {
            return;
        }
        var window = d as Window;
        if ((bool)e.NewValue)
        {
            InputBinding escapeBinding = new InputBinding(AppCommands.WindowCloseCommand, new KeyGesture(Key.Escape));
            escapeBinding.CommandParameter = window;
            window.InputBindings.Add(escapeBinding);
            window.Closing += Window_Closing;
        }
        else
        {
            window.Closing -= Window_Closing;
        }
    }
static void Window_Closing(object sender, CancelEventArgs e)
    {
#if DEBUG
#else
        e.Cancel = MessageBox.Show(window, "Are you sure?", "Exit",
            MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No) == MessageBoxResult.Yes;
#endif
    }