Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the best way to prevent losing TextBox focus when there is a validation error?

I've messed around with PreviewLostKeyboardFocus which almost gets you there. I've seen a couple of implementations using LostFocus, but that just forces focus back on the TextBox after it's lost focus and you can easily see this shifting on the screen. Basically, I'm just looking for the same type of behavior you could get with using OnValidating in WinForms.

like image 513
AKoran Avatar asked Jan 29 '10 16:01

AKoran


3 Answers

In my opinion, the best way is generally not to do it. It is almost always better to just disable the other controls or prevent saving until the value is valid.

But if your design really needs this ability, here is what you should do:

  1. Intercept the Preview version of keyboard and mouse events at your window level, or whatever scope you want to prevent focus changes within (eg maybe not your menu bar).

  2. When the Tab KeyDown or Return KeyDown is detected in the text box, or when a MouseDown is detected outside the text box while it has the focus, call UpdateSource() on the binding expression, then if the validation has failed set Handled=true to prevent the KeyDown or MouseDown event from being processed further.

  3. Also continue handling PreviewLostKeyboardFocus to catch any causes of focus change that aren't from the keyboard or mouse, or that your other code didn't recognize.

like image 180
Ray Burns Avatar answered Nov 19 '22 06:11

Ray Burns


To add onto Ray's answer:

UpdateSource is called like so:

BindingExpression be = userTextbox.GetBindingExpression(TextBox.TextProperty);
be.UpdateSource();

Also, as an alternative you can set the text box binding to:

UpdateSourceTrigger = "PropertyChanged";

The latter will cause a continuous check, whereas the former will check when needed (performant).

like image 35
Scott Johnstone Avatar answered Nov 19 '22 07:11

Scott Johnstone


If you attempt to focus an element inside its own LostFocus handler you will face a StackOverflowException, I'm not sure about the root cause (I suspect the focus kind of bounces around) but there is an easy workaround: dispatch it.

private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
    var element = (sender as TextBox);
    if (!theTextBoxWasValidated())
    {
        // doing this would cause a StackOverflowException
        // element.Focus();
        var restoreFocus = (System.Threading.ThreadStart)delegate { element.Focus(); };
        Dispatcher.BeginInvoke(restoreFocus);
    }
}

Through Dispatcher.BeginInvoke you make sure that restoring the focus doesn't get in the way of the in-progress loss of focus (and avoid the nasty exception you'd face otherwise)

like image 1
Shimoli Shah Avatar answered Nov 19 '22 06:11

Shimoli Shah