WPF how to make textbox lose focus after hitting enter - c#

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.

Related

How to use the back function in UWP apps to change visibility of a grid?

I would like to use the back function in the UWP in the App.xaml.cs file to change the visibility property of a grid (grid1) on the MainPage.xaml file.
//Go Back
public void App_BackRequested(object sender, Windows.UI.Core.BackRequestedEventArgs e)
{
if(MainPage.MyGlobals.pageLocation == 0)
{
//Do Nothing
}
else if(MainPage.MyGlobals.pageLocation == 1)
{
MainPage.grid1.Visibility = Visibility.Collapsed;
MainPage.MyGlobals.pageLocation = 0;
}
}
I know it's not typical practice to change xaml elements' properties from a different page, but I would really like to change how the back feature works in this app. I believe I have to make the grid pubic, but even when I (thought I) found a way to do that I still couldn't change the properties of the grid with the way I have it written in my code.
As you've mentioned, changing XAML elements' properties from a different page is not a good practice. We can just use SystemNavigationManager.BackRequested event in MainPage like following to change the visibility of a grid.
protected override void OnNavigatedTo(NavigationEventArgs e)
{
SystemNavigationManager.GetForCurrentView().BackRequested += Page_BackRequested;
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
SystemNavigationManager.GetForCurrentView().BackRequested -= Page_BackRequested;
}
private void Page_BackRequested(object sender, BackRequestedEventArgs e)
{
if (MyGrid.Visibility == Visibility.Visible)
{
MyGrid.Visibility = Visibility.Collapsed;
e.Handled = true;
}
else
{
if (this.Frame.CanGoBack)
{
this.Frame.GoBack();
e.Handled = true;
}
}
}
And if you want to take advantage of the back function in App.xaml.cs and use a different function in MainPage, you can add a event in App that wraps SystemNavigationManager.BackRequested to allow other pages to override the default behavior by subscribing to this event like following:
public event EventHandler<BackRequestedEventArgs> BackRequested;
private void App_BackRequested(object sender, BackRequestedEventArgs e)
{
BackRequested?.Invoke(sender, e);
Frame rootFrame = Window.Current.Content as Frame;
if (!e.Handled && rootFrame != null && rootFrame.CanGoBack)
{
rootFrame.GoBack();
e.Handled = true;
}
}
Then in MainPage, subscribe and unsubscribe this event like:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
(Application.Current as App).BackRequested += Page_BackRequested;
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
(Application.Current as App).BackRequested -= Page_BackRequested;
}
It doesn't work because you're trying to access the Grid as if it was a static property of the MainPage. You need a reference to the MainPage instance to manipulate the Grid, but all in all it is a really bad practice. You should have a look at the UWP/WP 8.1 navigation events.

ListView Behavior for AutoScrolling that stops at user interaction

i try to use the new Behavior-Feature in UAP. I use this Behavior:
public sealed class AutoScrollToLastItemBehavior : Behavior<ListView>
{
private bool _collectionChangedSubscribed;
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.SelectionChanged += SelectionChanged;
AssociatedObject.DataContextChanged += DataContextChanged;
}
private void SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ScrollToBottom();
}
private void CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
ScrollToBottom();
}
private void DataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args)
{
var collection = AssociatedObject.ItemsSource as INotifyCollectionChanged;
if (collection == null || _collectionChangedSubscribed) return;
collection.CollectionChanged += CollectionChanged;
_collectionChangedSubscribed = true;
}
private void ScrollToBottom()
{
var selectedIndex = AssociatedObject.Items?.Count - 1;
if (!selectedIndex.HasValue || selectedIndex < 0)
return;
AssociatedObject.SelectedIndex = selectedIndex.Value;
AssociatedObject.UpdateLayout();
AssociatedObject.ScrollIntoView(AssociatedObject.SelectedItem);
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.SelectionChanged -= SelectionChanged;
AssociatedObject.DataContextChanged -= DataContextChanged;
var collection = AssociatedObject.ItemsSource as INotifyCollectionChanged;
if (collection == null || !_collectionChangedSubscribed) return;
collection.CollectionChanged -= CollectionChanged;
_collectionChangedSubscribed = false;
}
}
This code works well. But i would like that the AutoScrolling stoped when user interact with the ListView. I found some sampels but most of them based on WPF and some of the functions or properties are not present in UWP.
So actual i dont finde a way to implementate a AutoScroll that stop working if the User scrolls by it self. Have someone maybe an idear for this?
Greetings
It really depends on what you mean by "user interacting with a ListView". If you mean someone scrolling manually - you can check the ViewChanged event or VerticalOffset of the ScrollViewer to see if it's scrolled to the bottom before collection change. Other interactions are likely things that are custom to your app, so you would have to detect them yourself. You need to access the ScrollViewer from the ListView template first before you look at these details.

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.

How to Close a Window in WPF on a escape key

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

WPF : Disable Undo in an editable ComboBox

I have implemented an undo system based on the Memento pattern. I disable the built in Undo on TextBox and was wondering how to do this on a ComboBox. The Combobox I have is editable, so it contains a TextBox, how do I access this to disable the Undo on it as well.
I know I can derive from ComboBox add a property and override the control template and set the property on the TextBox, but I would like a way to do this on the standard ComboBox from the xaml.
You can look it up from the template like this:
public Window1()
{
this.InitializeComponent();
comboBox1.Loaded += new RoutedEventHandler(comboBox1_Loaded);
}
void comboBox1_Loaded(object sender, RoutedEventArgs e)
{
var textBox = comboBox1.Template.FindName("PART_EditableTextBox", comboBox1) as TextBox;
}
I know this is 3+ years old but maybe it'll help someone. It is basically Rick's answer as a Behavoir that decyclone mentioned:
public class ComboBoxDisableUndoBehavoir : Behavior<ComboBox>
{
public ComboBoxDisableUndoBehavoir()
{
}
protected override void OnAttached()
{
if (AssociatedObject != null)
{
AssociatedObject.Loaded += AssociatedObject_Loaded;
}
base.OnAttached();
}
void AssociatedObject_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
var tb = AssociatedObject.Template.FindName("PART_EditableTextBox", AssociatedObject) as TextBox;
if (tb != null)
{
tb.IsUndoEnabled = false;
}
}
}

Categories

Resources