23

Is there any straightforward way of telling the whole WPF application to react to Escape key presses by attempting to close the currently focused widow? It is not a great bother to manually setup the command- and input bindings but I wonder if repeating this XAML in all windows is the most elegant approach?

<Window.CommandBindings>
        <CommandBinding Command="Close" Executed="CommandBinding_Executed" />
</Window.CommandBindings>
<Window.InputBindings>
        <KeyBinding Key="Escape" Command="Close" />
</Window.InputBindings>

Any constructive suggestions welcome!

Peter Perháč
  • 20,434
  • 21
  • 120
  • 152

8 Answers8

33

All I can suggest to improve on that is to remove the need for an event handler by binding to a static command instance.

Note: this will only work in .NET 4 onwards as it requires the ability to bind to the KeyBinding properties.

First, create a command that takes a Window as a parameter and calls Close within the Execute method:

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

Then you can bind your KeyBinding to the static Instance property:

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

I don't know that this is necessarily better than your approach, but it does mean marginally less boilerplate at the top of every Window and that you don't need to include an event handler in each

Steve Greatrex
  • 15,789
  • 5
  • 59
  • 73
  • can this lead to memory leaks since the singleton has an `EventHandler`? – Maslow Apr 07 '16 at 13:56
  • It shouldn't do but as we don't really care about the `CanExecute` functionality you could safeguard against it by having `CanExecute` always return true and replace the `CanExecuteChanged` handler with empty attachment implementations (e.g. `public event EventHandler CanExecuteChanged { add {} remove {} }`) – Steve Greatrex Apr 07 '16 at 14:00
24

Or you could just add a button with Cancel as text and set IsCancel = True. Then Escape will work as default command to close.

CharithJ
  • 46,289
  • 20
  • 116
  • 131
Kai
  • 241
  • 1
  • 2
2

create RoutedUICommand like below

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

and add it command binding in constructor

CommandBindings.Add(new CommandBinding(EscUICommand, (sender, e) => { this.Hide(); }));
Peter Perháč
  • 20,434
  • 21
  • 120
  • 152
Mahi
  • 29
  • 1
  • This works quite nicely, and when you already have a class for your window, it keeps everything right there. – DonBoitnott Feb 11 '19 at 15:37
  • If you have to write code behind you could just write in constructor: PreviewKeyDown += (s,e) => { if (e.Key == Key.Escape) Close() ;}; – Alexandru Dicu Mar 12 '20 at 00:52
1

On Windows shown with ShowDialog() you can use:

<!-- Button to close on Esc -->
<Button IsCancel="True" Width="0" Height="0"/>
markmnl
  • 11,116
  • 8
  • 73
  • 109
1

You can also use PreviewKeyDown Event

PreviewKeyDown="UserControl_PreviewKeyDown"

Code behind call you close command

private void UserControl_PreviewKeyDown(object sender, System.Windows.Input.KeyEventArgs e)
        {
            if (e.Key == Key.Escape)
            {
                _vm.OnCloseCommand(sender);
            }
        }
Mohammad Atiour Islam
  • 5,380
  • 3
  • 43
  • 48
0

Another possible way is to use attached properties

Bellow is a gist code:

<script src="https://gist.github.com/meziantou/1e98d7d7aa6aa859d916.js"></script>
Rui Sebastião
  • 855
  • 1
  • 18
  • 36
0

The Preview events happen quite early. If you have a control that should take the Esc key for its own purposes, stealing it at the window level may be too aggressive.

Instead you can handle it only if nothing else wants to:

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

    if (!e.Handled && e.Key == Key.Escape && Keyboard.Modifiers == ModifierKeys.None)
    {
        this.Close();
    }
}
Drew Noakes
  • 300,895
  • 165
  • 679
  • 742
-1

None of above worked for me, except Kai's. I modified his answer: I added 'btn_close.IsCancel = true;' to constructor. SettingsWindow is my second window, and main window is (default) MainWindow.

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

Hope it helps,

Simon S love nia

chistabo
  • 13
  • 7