WPF - Remove focus when clicking outside of a textbox
Asked Answered
P

14

35

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?

Provocative answered 27/6, 2011 at 5:46 Comment(4)
If you're clicking on a different UI element, the textbox should automatically lose focus.Louiselouisette
@Merlyn Morgan-Graham: No, It doesn't.Veroniqueverras
@Charlie: My comment was in response to "What if the element I'm clicking on will be the new recipient of focus?" I agree that if the user clicks just anywhere outside the box (e.g. the Window) it won't raise the LostFocus event, but another focusable element (e.g. another text box), it will.Louiselouisette
@Merlyn Morgan-Graham: Agree, and its an obvious thing that if you click on another text box, previous will lose focus but thats not the case here OP wants it to lose focus by clicking anywhere out side textBox.Veroniqueverras
V
34

Rather than adding new control to window, I think you should give your Grid a name and react to the MouseDown event on your window, moving the focus to the Grid itself. Something like this:

<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" 
          Background="#01FFFFFF"
          KeyDown="Grid_KeyDown" 
          Name="grid1" 
          Focusable="True">

          <TextBox Width="120" Margin="117,61,0,0" 
                   Name="textBox1" 
                   VerticalAlignment="Top" 
                   HorizontalAlignment="Left"/>
    </Grid>
</Window>

code behind:

private void window1_MouseDown(object sender, MouseButtonEventArgs e)
{
    grid1.Focus();
}
Veroniqueverras answered 27/6, 2011 at 6:22 Comment(10)
Ah, this worked. To expand on the solution, there is no need to name the window either - just override OnMouseDown for a slightly cleaner bit of XAML.Provocative
@JacobJ: oops.. what i wanted to write was add name to your 'grid' not window. Edited. thanks for pointing out!Veroniqueverras
I tried it out too. Not sure why, but this isn't working for me. LostFocus isn't getting called for the text boxes I have in my window.Louiselouisette
@ Merlyn Morgan-Graham: In simple words, what you need to do is, Add Name Property to your grid, make grid focusable, on mouse down event of window: set focus to that grid. is that what you have tried?Veroniqueverras
@Charlie: "make grid focusable" is the part I missed. Thanks! +1Louiselouisette
make sure your grid has non-transparent background color, else it cannot respond to mousedown event.Spam
You can also to omit the name of the grid: ((sender as Window).Content as Grid).Focus();.Malpractice
@user3162662: The background color doesn't need to be non-transparent. But it needs to be specified (i.e. not null) for the mousedown event to be fired. You can use Background="Transparent" and the event will fire.Penelopepeneplain
Unfortuntely, this will only be of limited use when you have the TextBox contained inside a UserControl. The "lose focus" functionality is then limited to the area of the user control.Philistine
This doesn't work when you have other controls in your window that handle the click, because then the click event won't bubble up to the window. So in most cases, this is a limited solution.Muscadel
H
19

I think, better way to solve this problem is adding MouseDown event handler to window with code behind:

private void window_MouseDown(object sender, MouseButtonEventArgs e)
{
    Keyboard.ClearFocus();
}
Haemorrhage answered 5/1, 2014 at 20:55 Comment(4)
I don't have much knowledge regarding this, but won't this also clear the focus when pressing on the textfield itself?Throve
No, because if you click on textbox, the click event is handled by textbox and it is not routed to window. Therefore Windows.MouseDown is never called when you click on textbox.Regardful
This prevents the LostFocus event from being called on the text box.Larine
I know I'm a little late, but this is (approximately) the solution I went with for this very same issue I just wanted to reply to the thing @Larine presented. I was able to get around this by grabbing Keyboard.FocusedElement before clearing focus, then doing element.RaiseEvent(new RoutedEventArgs(UIElement.LostFocusEvent)) -- I had to do this because my TextBox's border depends on focus state.Big
G
5

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>
Gingersnap answered 28/6, 2018 at 9:0 Comment(0)
H
5

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);
    }
}
Heavily answered 4/10, 2018 at 9:58 Comment(1)
This actually works great, and just saved me a bunch of frustration. Thanks!Deegan
S
4

Another way that worked for me was using

Mouse.AddPreviewMouseDownOutsideCapturedElementHandler

For example, say you had a TextBlock that when clicked, should become editable by showing a focused TextBox. Then when the user clicked outside the TextBox, it should be hidden again. Here's how you can do it:

private void YourTextBlock_OnMouseDown(object sender, MouseButtonEventArgs e)
{
    YourTextBox.Visibility = Visibility.Visible;
    YourTextBox.Focus();
    CaptureMouse();
    Mouse.AddPreviewMouseDownOutsideCapturedElementHandler(this, OnMouseDownOutsideElement);
}

private void OnMouseDownOutsideElement(object sender, MouseButtonEventArgs e)
{
    Mouse.RemovePreviewMouseDownOutsideCapturedElementHandler(this, OnMouseDownOutsideElement);
    ReleaseMouseCapture();
    YourTextBox.Visibility = Visibility.Hidden;
}
Suttles answered 16/4, 2016 at 1:19 Comment(4)
Not working unless having a MessageBox.Show within YourTextBlock_OnMouseDown. CaptureMouse returns true and OnMouseDownOutsideElement won't be called.Aldwin
This might work under certain situations, but I found it extremely finicky. YMMV.Muscadel
I liked the approach, because in contrast to the other answers it doesn't require having control over the element receiving the click. But, unfortunately, it also fired when I clicked in the TextBox which lead to annoying side effects, because the event handler was implemented to handle events outside. And I found no way - within the handler - to distinguish between the two scenarios..,Burgess
Another good approach can be found here: stackoverflow.com/a/39275232Burgess
M
4

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).

Medullated answered 25/9, 2020 at 3:9 Comment(2)
While this code snippet may be the solution, including an explanation helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion.Shroff
This is an important point and should be upvoted!Spine
C
1

I'm not 100% sure, but if you set Focusable to true on the container element (Grid, StackPanel, etc) then it should take the focus away from the text box.

Chrisoula answered 27/6, 2011 at 5:54 Comment(1)
I tried this before asking the question, and it didn't quite do it. In addition to setting Focusable on the container element, I needed to actually manually set focus on mousedown as per Charlie Sharma's solution.Provocative
A
1

If you clicked on the element, which can grab the focus, you get what you need. if, for example, you have some panel, you can handle panel's mouseClick event to achive your needs, or use Richard Szalay advice.

Archenteron answered 27/6, 2011 at 5:56 Comment(0)
M
1

I was running into a similar issue, but when I wrapped a ScrollViewer control around my textboxes , all the textboxes would automatically lose focus when clicking anywhere outside the texboxes.

Matriarch answered 19/6, 2017 at 18:55 Comment(1)
I think you mean wrapping a scrollviewer around your textboxes not the other way around. Suggested an edit. Wrapping a scrollviewer around my textbox solved the issueSilvereye
U
0

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;
     }
}
Uncrowned answered 27/4, 2020 at 22:46 Comment(0)
A
0

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);
}
Auscultation answered 15/11, 2021 at 21:41 Comment(0)
S
-1

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

Shoal answered 3/12, 2013 at 8:35 Comment(0)
S
-1

Another approach, you can use the IsKeyboardFocusedChanged event:

MyTextBox.IsKeyboardFocusedChanged += MyTextBox_IsKeyboardFocusedChanged;

private void MyTextBox_IsKeyboardFocusedChanged(object sender, DependencyPropertyChangedEventArgs e)
{
    if (Convert.ToBoolean(e.NewValue))
    {
        // it's focused
    }
    else
    {
        // it's not focused
    }
}    
Store answered 18/1, 2018 at 0:0 Comment(1)
You should cast e.NewValue to bool insteadTrilateration
H
-1
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>
Heyer answered 5/4, 2019 at 9:45 Comment(1)
This is only going to work in a very specific context, where your Behavior has access to this unexplained App.Current.MainWindow.Sugihara

© 2022 - 2024 — McMap. All rights reserved.