如何将“按下Esc键关闭”行为分配给项目中的所有WPF窗口?

23

有没有一种简单的方法,可以让整个WPF应用程序在按下Escape键时尝试关闭当前拥有焦点的窗口?手动设置命令和输入绑定并不是一个大问题,但我想知道在所有窗口中重复这个XAML是否是最优雅的方法?

<Window.CommandBindings>
        <CommandBinding Command="Close" Executed="CommandBinding_Executed" />
</Window.CommandBindings>
<Window.InputBindings>
        <KeyBinding Key="Escape" Command="Close" />
</Window.InputBindings>
任何建设性的建议都欢迎!

类似的问题在这里:https://dev59.com/_Wsz5IYBdhLWcg3wv6fu - CharithJ
使用IsCancel标准属性。示例https://dev59.com/_Wsz5IYBdhLWcg3wv6fu#7692796 - CharithJ
8个回答

33

我能建议的改进是通过绑定一个静态命令实例来消除事件处理程序的需求。

注意:这只适用于.NET 4及更高版本,因为它需要绑定到KeyBinding属性。

首先,创建一个以窗口为参数并在Execute方法内调用Close的命令:

public class CloseThisWindowCommand : ICommand
{
    #region ICommand Members

    public bool CanExecute(object parameter)
    {
        //we can only close Windows
        return (parameter is Window);
    }

    public event EventHandler CanExecuteChanged;

    public void Execute(object parameter)
    {
        if (this.CanExecute(parameter))
        {
            ((Window)parameter).Close();
        }
    }

    #endregion

    private CloseThisWindowCommand()
    {

    }

    public static readonly ICommand Instance = new CloseThisWindowCommand();
}

然后您可以将KeyBinding与静态的Instance属性绑定:

<Window.InputBindings>
    <KeyBinding Key="Escape" Command="{x:Static local:CloseThisWindowCommand.Instance}" CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}" />
</Window.InputBindings>

我不知道这是否一定比你的方法更好,但它确实意味着在每个Window的顶部要少写一些样板代码,并且你不需要在每个窗口中包含一个事件处理程序。


这可能会导致内存泄漏吗?因为单例模式中有一个 EventHandler - Maslow
它本不应该这样,但由于我们并不真正关心CanExecute功能,您可以通过让CanExecute始终返回true并用空附加实现(例如public event EventHandler CanExecuteChanged { add {} remove {} })替换CanExecuteChanged处理程序来防范它。 - Steve Greatrex

24

或者你可以添加一个文本为“取消”的按钮并设置IsCancel = True。然后,Esc键将作为默认命令关闭。


1
不总是理想的,但仍然喜欢这个建议。足够简单。 - Peter Perháč

2

创建如下所示的RoutedUICommand

 private static RoutedUICommand EscUICommand = new RoutedUICommand("EscBtnCommand"
       , "EscBtnCommand"
       , typeof(WindowName)
       , new InputGestureCollection(new InputGesture[] 
           { new KeyGesture(Key.Escape, ModifierKeys.None, "Close") }));

并在构造函数中添加它的命令绑定

CommandBindings.Add(new CommandBinding(EscUICommand, (sender, e) => { this.Hide(); }));

这个很好用,而且当你已经有一个窗口类时,它可以将所有东西都放在那里。 - DonBoitnott
如果你需要在代码后面编写,你可以在构造函数中直接编写:PreviewKeyDown += (s,e) => { if (e.Key == Key.Escape) Close() ;}; - Alexandru Dicu

1
你也可以使用 PreviewKeyDown 事件。
PreviewKeyDown="UserControl_PreviewKeyDown"

后台代码调用关闭命令

private void UserControl_PreviewKeyDown(object sender, System.Windows.Input.KeyEventArgs e)
        {
            if (e.Key == Key.Escape)
            {
                _vm.OnCloseCommand(sender);
            }
        }

1
在使用 ShowDialog() 显示的 Window 上,您可以使用以下内容:
<!-- Button to close on Esc -->
<Button IsCancel="True" Width="0" Height="0"/>

0

另一种可能的方法是使用附加属性

下面是一个代码片段:

<script src="https://gist.github.com/meziantou/1e98d7d7aa6aa859d916.js"></script>


0

Preview事件发生得非常早。如果您有一个控件应该为自己的目的使用Esc键,那么在窗口级别上窃取它可能过于激进。

相反,只有在没有其他东西想要时才能处理它:

protected override void OnKeyDown(KeyEventArgs e)
{
    base.OnKeyDown(e);

    if (!e.Handled && e.Key == Key.Escape && Keyboard.Modifiers == ModifierKeys.None)
    {
        this.Close();
    }
}

-1

以上的方法都对我没用,除了Kai的。 我修改了他的答案:我在构造函数中添加了“btn_close.IsCancel = true;”。SettingsWindow是我的第二个窗口,主窗口是(默认)MainWindow。

  public partial class SettingsWindow : Window {
    public SettingsWindow() {
      InitializeComponent();
      btn_close.IsCancel = true;
    }
    private void btn_close_Click(object sender, RoutedEventArgs e) {
      this.Close();
    }
  }

希望能有所帮助,
西蒙 斯洛尼亚

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接