WPF - Remove focus when clicking outside of a textbox

Viewed 48246

I have some textboxes where I would like focus to behave a little differently than normal for a WPF application. Basically, I would like them to behave more like a textbox behaves on a webpage. That is, if I click anywhere outside of the textbox, it will lose its focus. What is the best way to do so?

If the answer is to programmatically remove focus, what is the best way to detect a Mouseclick outside of the bounds? What if the element I'm clicking on will be the new recipient of focus?

14 Answers

To avoid code behind you can use this Behavior The behavior

 public class ClearFocusOnClickBehavior : Behavior<FrameworkElement>
 {
    protected override void OnAttached()
    {
        AssociatedObject.MouseDown += AssociatedObject_MouseDown;
        base.OnAttached();
    }

    private static void AssociatedObject_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
    {
        Keyboard.ClearFocus();
    }

    protected override void OnDetaching()
    {
        AssociatedObject.MouseDown -= AssociatedObject_MouseDown;
    }
}

Useing in XAML:

On any element outside the text box that you want him to clear the focus on click add:

    <i:Interaction.Behaviors>
        <behaviors:ClearFocusOnClickBehavior/>
    </i:Interaction.Behaviors>

I have tried the selected answer in react native WPF application but it was not triggering lost focus of textbox due to which textbox was not losing the focus. Following solution work for me.

Bound a mouse-down event to Window:

<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="Window1" Height="412" Width="569" MouseDown="window_MouseDown" Name="window1" >
    <Grid ShowGridLines="False" KeyDown="Grid_KeyDown" Name="grid1" Focusable="True">
          <TextBox HorizontalAlignment="Left" Margin="117,61,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" />
    </Grid>
</Window>

and event is:

private void window_MouseDown(object sender, MouseButtonEventArgs e)
{
    TextBox textBox = Keyboard.FocusedElement as TextBox;
    if (textBox != null)
    {
        TraversalRequest tRequest = new TraversalRequest(FocusNavigationDirection.Next);
        textBox.MoveFocus(tRequest);
    }
}

It is important to mention that a textbox catches both the mouse and the keyboard events, so in order to move or remove the focus from a text box you have to release these 2 elements.

Let's start with the keyboard, Keyboard.ClearFocus(); will remove the keyboard focus from the textbox, and it's great for hiding the blinking cursor, but won't move the focus from the textbox, in other words the textbox will remain as focused element but without displaying the cursor. You can check that by printing FocusManager.GetFocusedElement(this); after Keyboard.ClearFocus();.

So, if you have a LostFocus event attached to the textbox or similar event, it will not be triggered because the element has not yet lost focus.

PreviewMouseDown += (s, e) => FocusManager.SetFocusedElement(this, null);
PreviewMouseDown += (s, e) => Keyboard.ClearFocus();

As a solution, you should set the Window focused element to null, by calling: System.Windows.Input.FocusManager.SetFocusedElement(this, null); Now if you are wondering if we can get rid of the Keyboard.ClearFocus(); then the answer is no, because we are going to remove the mouse focus, but the keyboard focus will still be there. (you will notice the blinking cursor which is still there, and you will be able to type some text in the textbox).

you cant explicitly loose the focus of a control

you can set the focus to other control instead

**txt.Focusable=true;
label.focus();
Keyboard.Focus(txtPassword);**

try this

You can use the IsKeyboardFocusedChanged event:

myTextBox.IsKeyboardFocusedChanged += myTextBox_IsKeyboardFocusedChanged;

private void SendFileCaptionTextBox_IsKeyboardFocusedChanged(object sender, DependencyPropertyChangedEventArgs e)
{
    if (e.NewValue.ToString() == "True")
    {
        // it's focused
    }
    else
    {
        // it's not focused
    }
}    
public class ClearFocusOnOutsideClickBehavior : Behavior<FrameworkElement>
{
    protected override void OnAttached()
    {

        AssociatedObject.GotFocus += AssociatedObjectOnGotFocus;
        AssociatedObject.LostFocus += AssociatedObjectOnLostFocus;
        base.OnAttached();
    }

    private void AssociatedObjectOnLostFocus(object sender, RoutedEventArgs e)
    {
        App.Current.MainWindow.MouseUp -= _paren_PreviewMouseUp;
    }

    private void AssociatedObjectOnGotFocus(object sender, RoutedEventArgs e)
    {
        App.Current.MainWindow.MouseUp += _paren_PreviewMouseUp;
    }

    private void _paren_PreviewMouseUp(object sender, MouseButtonEventArgs e)
    {
        Keyboard.ClearFocus();
    }

    protected override void OnDetaching()
    {
        AssociatedObject.GotFocus -= AssociatedObjectOnGotFocus;
        AssociatedObject.LostFocus -= AssociatedObjectOnLostFocus;
    }
}

Using in XAML:

<TextBox Height="30" Width="200">
            <i:Interaction.Behaviors>
                <behaviours:ClearFocusOnOutsideClickBehavior/>
            </i:Interaction.Behaviors>
 </TextBox>

You can register the PreviewMouseLeftButtonDownEvent to every type of element in App.xaml.cs on application startup.

EventManager.RegisterClassHandler(typeof(UIElement), UIElement.PreviewMouseLeftButtonDownEvent,
                new MouseButtonEventHandler(FocusElement));

private void FocusElement(object sender, MouseButtonEventArgs e)
{
     var parent = e.OriginalSource as DependencyObject;
     if (parent is UIElement element && !element.Focusable)
     {
         element.Focusable = true;
         element.Focus();
         element.Focusable = false;
     }
}

This could be done with a small amount of code. Just set the window's Focusable="True" where the textboxes or other controls are placed and add this on it's MouseDown event:

private void Window_MouseDown(object sender, MouseButtonEventArgs e)
{
     FocusManager.SetFocusedElement(this, this);
}
Related