How to Close a Window in WPF on a escape key - c#

Possible Duplicate:
How can I assign the 'Close on Escape-key press' behavior to all WPF windows within a project?
I want to close the windows in my wpf project when the user clicks the escape button. I don't want to write the code in every window but want to create a class which can catch the when the user press the escape key.

Option 1
Use Button.IsCancel property.
<Button Name="btnCancel" IsCancel="true" Click="OnClickCancel">Cancel</Button>
When you set the IsCancel property of a button to true, you create a
Button that is registered with the AccessKeyManager. The button is
then activated when a user presses the ESC key.
However, this works properly only for Dialogs.
Option2
You add a handler to PreviewKeyDown on the window if you want to close windows on Esc press.
public MainWindow()
{
InitializeComponent();
this.PreviewKeyDown += new KeyEventHandler(HandleEsc);
}
private void HandleEsc(object sender, KeyEventArgs e)
{
if (e.Key == Key.Escape)
Close();
}

Here is a button-less solution that is clean and more MVVM-ish. Add the following XAML into your dialog/window:
<Window.InputBindings>
<KeyBinding Command="ApplicationCommands.Close" Key="Esc" />
</Window.InputBindings>
<Window.CommandBindings>
<CommandBinding Command="ApplicationCommands.Close" Executed="CloseCommandBinding_Executed" />
</Window.CommandBindings>
and handle the event in the code-behind:
private void CloseCommandBinding_Executed(object sender, System.Windows.Input.ExecutedRoutedEventArgs e)
{
if (MessageBox.Show("Close?", "Close", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes)
this.Close();
}

One line to put after InitializeComponent():
PreviewKeyDown += (s,e) => { if (e.Key == Key.Escape) Close() ;};
Please note that this kind of code behind does not break MVVM pattern since this is UI related and you don't access any viewmodel data. The alternative is to use attached properties which will require more code.

You can create a custom DependencyProperty:
using System.Windows;
using System.Windows.Input;
public static class WindowUtilities
{
/// <summary>
/// Property to allow closing window on Esc key.
/// </summary>
public static readonly DependencyProperty CloseOnEscapeProperty = DependencyProperty.RegisterAttached(
"CloseOnEscape",
typeof(bool),
typeof(WindowUtilities),
new FrameworkPropertyMetadata(false, CloseOnEscapeChanged));
public static bool GetCloseOnEscape(DependencyObject d)
{
return (bool)d.GetValue(CloseOnEscapeProperty);
}
public static void SetCloseOnEscape(DependencyObject d, bool value)
{
d.SetValue(CloseOnEscapeProperty, value);
}
private static void CloseOnEscapeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is Window target)
{
if ((bool)e.NewValue)
{
target.PreviewKeyDown += Window_PreviewKeyDown;
}
else
{
target.PreviewKeyDown -= Window_PreviewKeyDown;
}
}
}
private static void Window_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (sender is Window target)
{
if (e.Key == Key.Escape)
{
target.Close();
}
}
}
}
And use it your windows' XAML like this:
<Window ...
xmlns:custom="clr-namespace:WhereverThePropertyIsDefined"
custom:WindowUtilities.CloseOnEscape="True"
...>
The answer is based on the content of the gist referenced in this answer.

The InputBinding options here are nice and flexible.
If you want to use an event handler, be aware that the Preview events happen quite early. If you have a nested control that should take the Esc key for its own purposes, stealing it at the window level may brake that control's functionality.
Instead you can handle the event at the window level only if nothing else wants to with:
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
if (!e.Handled && e.Key == Key.Escape && Keyboard.Modifiers == ModifierKeys.None)
{
this.Close();
}
}

Related

Do not trigger click on focused element when hitting ctrl+space

In my WPF application a "global" search box appears when hitting Ctrl+Space. It behaves like Spotlight in Mac OS when hitting Command+Space.
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public static RoutedCommand OpenSpotlight { get; set; } = new RoutedCommand();
public MainWindow()
{
OpenSpotlight.InputGestures.Add(new KeyGesture(Key.Space, ModifierKeys.Control));
}
private void OpenSpotlight_Execute(object sender, ExecutedRoutedEventArgs e)
{
// Code which opens the search box ...
}
}
MainWindow.xaml
<Window.CommandBindings>
<CommandBinding Command="{x:Static local:MainWindow.OpenSpotlight}" Executed="OpenSpotlight_Execute"/>
</Window.CommandBindings>
Works fine, except one problem: when any button is focused, hitting Ctrl+Space triggers the button to be clicked because Space key is being hit.
Is there any way to omit this behaviour? I think of changing/removing the focus globally when Ctrl key is hit but don't know how this can be implemented ...
Instead of using a RoutedCommand and a CommandBinding, you could just handle the PreviewKeyDown event:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
PreviewKeyDown += OnPreviewKeyDown;
}
private void OnPreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Space
&& (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)))
{
e.Handled = true;
// Code which opens the search box ...
}
}
}
This solution doesn't require you to add anything to the XAML markup.
I haven't tried this, but seems quite logical to me.
You can handle the KeyDown and/or PreviewKeyDown event of a button and skip the Space press. Something like this could work :
private void GlobalButton_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Space)
e.Handled = true;
}
Wondering how you'ld do this for all the buttons? Here's a function to find a control of a given type:
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T :
DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
Simply loop through the buttons on window_load or similar events:
foreach (Button btn in FindVisualChildren<Button>(this))
{
btn.KeyDown += GlobalButton_PreviewKeyDown;
btn.PreviewKeyDown += GlobalButton_PreviewKeyDown;
}
Hopefully this helps.

Handling some keys while textbox is focused

So, apparently I had some problem when handling keys such as F10 or F11.
I want to move the focus from current textbox into another textbox, but not in one particular textbox. So, I wrote some code to handle key:
private void checkKeys(KeyEventArgs e)
{
if (e.KeyCode == Keys.F10)
{
buyerName.Focus();
}
else if (e.KeyCode == Keys.F11)
{
discount.Focus();
}
}
But, if I put this into individual textbox, which kinda hassle to me. Is there any method to listen key whether in global userControl or textbox?
Edit : here's my structure that I want to ask :
Form-
|-User Control
|-TextBox
Edit 2 : here's some image might help img
To use a global keyboard listener in Winforms, you just need to add a handler to KeyUp action for the main form itself:
private void Form1_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.F10)
{
textBox1.Focus();
e.Handled = true; //To use F10, you need to set the handled state to true
} else if (e.KeyCode == Keys.F11)
{
textBox2.Focus();
}
}
Then make sure that the KeyPreview property on the main form is set to True.
The issue with the application freezing when pressing F10 is because it is waiting for another consecutive action. To bypass this simply set the Handled property on the keyevent to TRUE. This releases the unresolved event.
This is my entire form class, refactored to use a helper method as you are refering to. This works fine. But you have to make sure that the KeyPreview property on your form is True, unless your keypresses will not be matched to your event handlers.
namespace KeyTest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void Form1_KeyUp(object sender, KeyEventArgs e)
{
CheckKeys(e);
}
private void CheckKeys(KeyEventArgs e)
{
if (e.KeyCode == Keys.F10)
{
textBox1.Focus();
e.Handled = true;
}
else if (e.KeyCode == Keys.F11)
{
textBox2.Focus();
e.Handled = true;
}
}
}
}
Now in your comment you are mentioning a UserControl, if you want that, then you need to create an instance method on your UserControl class, and pass the event to that from your global keyboard event handler on your main form.
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
public void HandleKeys(KeyEventArgs e)
{
if (e.KeyCode == Keys.F10)
{
textBox1.Focus();
e.Handled = true;
}
else if (e.KeyCode == Keys.F11)
{
textBox2.Focus();
e.Handled = true;
}
}
}
Then on your main form:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void Form1_KeyUp(object sender, KeyEventArgs e)
{
CheckKeys(e);
}
private void CheckKeys(KeyEventArgs e)
{
uc1.HandleKeys(e); //Instance method on your user control.
}
}
This then works as intended.
As pointed out in one of the comments, a better way would be to override the ProcessCmdKey method on the Form base class. This would be done like so:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
userControl11.HandleKeys(keyData); // method on the userControl to handle the key code.
base.ProcessCmdKey(ref msg, keyData);
return true;
}
}
The handler on the UserControl stays more or less the same:
public void HandleKeys(Keys keys)
{
if (keys == Keys.F10)
{
nameTB.Focus();
} else if (keys == Keys.F11)
{
emailTB.Focus();
}
}
Whether this is a more correct way of doing it, I am unsure of. They certainly both accomplish the same result. The documentation shows the first method in for handling keyboard events at the form level here:
How to handle keyboard input
But states here that the ProcessCmdKey method is to provide additional handling of shortcuts and MDI accellerators.
ProcessCmdKey
I will leave that up to you to decide what is the best for your scenario. But keep it in to show how you would use it should you choose to.
You can hook up to the KeyUp event of your form.
That way, any key pressed while your form is focused will be send to you (if the control didn't handle the key).
Thanks to #Espen and #reza-aghaei for handling keys into main form. Unfortunately, I still didn't managed find a way to focus to designated textbox inside a UserControl. However, I make some dirty method which kinda crappy and un-efficient by searching child control from it's parent
//MainForm.cs
if(yourUserControl.Name)//Do some check for targeted userControl, if null can cause NullReferenceException
{
if (e.KeyCode == Keys.F10)
{
this.Controls.Find("textboxName", true).First().Focus();
e.Handled = true;
}
}

KeyDown preventing text appearing in TextBox [UWP]

I have the current UWP app targeting 10240:
<Page x:Class="App8.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<ContentControl KeyDown="ContentControl_KeyDown">
<TextBox TextChanged="TextBox_TextChanged"/>
</ContentControl>
</Grid>
</Page>
And:
namespace App8
{
public sealed partial class MainPage : Page
{
public MainPage() => InitializeComponent();
private void ContentControl_KeyDown(object sender, KeyRoutedEventArgs e) => e.Handled = true;
private void TextBox_TextChanged(object sender, TextChangedEventArgs e) => Debug.WriteLine("NEVER RUNNING CODE");
}
}
When I write in the textbox I want to avoid any key events going to the main screen. In order to do that I have the KeyDown in the parent element of the textbox, and I handle the event. But If I do that the textbox doesn't write anything.
I'd like to end any key events going in the ContentControl going to the Page, but allowing the textbox to work normally. Any ideas?
I'd like to end any key events going in the ContentControl going to the Page, but allowing the textbox to work normally. Any ideas?
For your requirement, you could make bool flag to tell main screen fire some events or not when the TextBox is focused or not.
private bool IsFocus;
private void MyTextBox_GettingFocus(UIElement sender, GettingFocusEventArgs args)
{
IsFocus = true;
}
private void MyTextBox_LostFocus(object sender, RoutedEventArgs e)
{
IsFocus = false;
}
Usage
public MainPage()
{
this.InitializeComponent();
Window.Current.Dispatcher.AcceleratorKeyActivated += Dispatcher_AcceleratorKeyActivated;
}
private void Dispatcher_AcceleratorKeyActivated(Windows.UI.Core.CoreDispatcher sender, Windows.UI.Core.AcceleratorKeyEventArgs args)
{
if (IsFocus)
{
System.Diagnostics.Debug.WriteLine("Do Not Fire Your Event ");
return;
}
else
{
System.Diagnostics.Debug.WriteLine(" Fire Your Event ");
}
}

WPF how to make textbox lose focus after hitting enter

I created some textboxes and I want user to enter decimal values into them. In every application I have ever used, when I type something into the textbox and hit enter, the value is accepted and textbox lose focus. How can I do it in my app?
I know it should be relatively easy to do it with a key event, but maybe there is a command or something.
I searched the stackoverflow but I only found questions about how to keep focus after hitting enter...
You can also create a generic behavior which can be easily applied to any textbox within your application. Here is a sample behavior class:-
public class TextBoxEnterKeyUpdateBehavior : Behavior<TextBox>
{
protected override void OnAttached()
{
if (this.AssociatedObject != null)
{
base.OnAttached();
this.AssociatedObject.KeyDown += AssociatedObject_KeyDown;
}
}
protected override void OnDetaching()
{
if (this.AssociatedObject != null)
{
this.AssociatedObject.KeyDown -= AssociatedObject_KeyDown;
base.OnDetaching();
}
}
private void AssociatedObject_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
TextBox textBox = sender as TextBox;
if (textBox != null)
{
if (e.Key == Key.Return)
{
if (e.Key == Key.Enter)
{
textBox.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
}
}
}
}
}
To use this class in your xaml, just include it in textbox behaviors collection like this :-
<TextBox>
<i:Interaction.Behaviors>
<TextBoxEnterKeyUpdateBehavior />
</i:Interaction.Behaviors>
</TextBox>
Here "i" refers to System.Windows.Interactivity namespace.

Have a ComboBox not "use" the F4 key

I have an application where the user wants F4 to be the "Process Orders" button. (There is a long history around that key performing this feature.)
Today I found out that if the focus is in a ComboBox then F4 makes the ComboBox perform a dropdown.
Is there a way to make that not happen?
Update: I tried this using Delphi and it happens there too. While I am still curious, this seems to be a "baked in" Windows thing. I am going to ask the users to pick another shortcut.
use this
cboTest.PreviewKeyDown += (o,e) => {
if (e.Key == Key.F4)
e.Handled = true;
};
cboTest is your ComboBox Name
Use a custom combo box like this:
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
namespace Your.Namespace
{
public class CustomComboBox : ComboBox
{
protected override void OnKeyDown(KeyRoutedEventArgs e)
{
if (e.Key != Windows.System.VirtualKey.F4)
{
base.OnKeyDown(e);
}
}
}
}
And then, in XAML, replace all the instances of the old combo box with the new custom combo box:
<CustomComboBox xmlns="using:Your.Namespace.Controls"
... />
In addition to the answers above, here is a reusable solution without the need to create a custom control:
public static class ComboBoxHelper
{
public static readonly DependencyProperty DisableF4HotKeyProperty =
DependencyProperty.RegisterAttached("DisableF4HotKey", typeof(bool),
typeof(ComboBoxHelper), new PropertyMetadata(false, OnDisableF4HotKeyChanged));
public static bool GetDisableF4HotKey(DependencyObject obj)
{
return (bool)obj.GetValue(DisableF4HotKeyProperty);
}
public static void SetDisableF4HotKey(DependencyObject obj, bool value)
{
obj.SetValue(DisableF4HotKeyProperty, value);
}
private static void OnDisableF4HotKeyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var box = d as ComboBox;
if (d == null) return;
box.PreviewKeyDown -= OnComboBoxKeyDown;
box.PreviewKeyDown += OnComboBoxKeyDown;
}
private static void OnComboBoxKeyDown(object _, KeyEventArgs e)
{
if (e.Key == System.Windows.Input.Key.F4)
{
e.Handled = true;
}
}
}
In your xaml file, add a namespace reference to the ComboBoxHelper class and set the attached property on your ComboBox:
<ComboBox h:ComboBoxHelper.DisableF4HotKey="True" />
How are you catching the F4 key? If you use the keypreview, you can override it from bubbling down to the combo box:
private void Form1_Load(object sender, EventArgs e)
{
this.KeyPreview = true;
this.KeyDown += new KeyEventHandler(Form1_KeyDown);
}
void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.F4)
{
e.Handled = true;
MessageBox.Show("F4 Pressed");
}
}
By default the F4 key opens or closes the dropdown list of a combo box. This behavior can be altered to ignore the F4 key and open the list using the down arrow key instead. This is done by sending a CB_SETEXTENDEDUI to the combo box providing a TRUE parameter. This is assuming that WPF does in fact use the native common controls internally.

Categories

Resources