Keithin8a
Keithin8a

Reputation: 961

How to update binding of Textbox when clicking off the textbox

In my application I have a Usercontrol which represents some configuration for an object. Inside this is a Texblock which contains the name of said object. I wanted to be able to click on this Textblock and turn it into a Textbox so you were able to edit the name.

I want it to have the following 3 behaviours

  1. Sets the value in the ViewModel when enter is pressed
  2. Gain Keyboard focus when it becomes visible
  3. Sets the value in the ViewModel when the mouse is clicked outside of the textbox (like a combobox does)

and I am currently stuck on 3. How can I get my textbox to satisfy requirement 3 - Clicking off the textbox sets the value I have implemented some attachment behaviours to make the other 2 work but can't get the mouse one to work.

I have tried the following

  1. Added PreviewMouseDown event - This only seems to get detected when mouse down is in the textbox
  2. Added PreviewMouseDown even in the usercontrol - this seems to get hit even when the textbox is clicked. Also it isn't the only usercontrol inside my window so It won't work on the grander scale.
  3. Added a PreviewMouseDownOutsideCapturedElement - but can never get this to fire.
  4. Lost focus on both UpdateSourceTrigger and as an event - This works but only when you click a different input field rather than just clicking off

The code for my textbox is as follows.

            <TextBox x:Name="text" VerticalAlignment="Center"
                 Margin="5,2,0,0"
                 Text="{Binding Name, Mode=TwoWay}"
                 Visibility="{Binding IsEditingName, Converter={StaticResource VisConverter}, FallbackValue=Collapsed}"
                 b:FocusBehaviours.ShouldFocusWhenVisible="True"
                 b:InputBehaviours.UpdatePropertySourceWhenEnterPressed="TextBox.Text"/>

I've looked at countless SO posts and none of them seems to be able to help me with this, they all recommend adding it to the window/usercontrol which I can't do.

Upvotes: 1

Views: 1966

Answers (2)

Meow Cat 2012
Meow Cat 2012

Reputation: 1008

I was doing this in a WPF Window for a built-in TextBox in order to do something whenever user clicks outsides. The method may be easily applied to WPF/UWP Window or Page.

// In your `XxxWindow.xaml.cs`(or corresponding class in VB):
public partial class XxxWindow : Window {

    ...

    //  The event handler delegate for handling click outsides a TextBox.
    private readonly MouseButtonEventHandler windowWideMouseButtonEventHandler;

    // Add something in the constructor or any initialization methods.
    public MainWindow() {
        ...
        // Initialize the handler.
       windowWideMouseButtonEventHandler = new MouseButtonEventHandler(OnWindowWideMouseEvent);//+
    }

    ...
    ...
    // Whenever the text box got focus, begin listening to window-wide clicks.
    // This method shall be bounded in xaml or elsewhere.
    // Added method
    private void SearchBox_GotFocus(object sender, RoutedEventArgs e) {
        AddHandler(Window.PreviewMouseDownEvent, windowWideMouseButtonEventHandler);
    }

    // The handler method
    // Added method
    private void OnWindowWideMouseEvent(object sender, MouseButtonEventArgs e) {
        // Exclude the text box itself.
        if (!FocusManager.GetFocusedElement(this).IsMouseOver) {
            // Unregister the already-used handler.
            RemoveHandler(Window.PreviewMouseDownEvent, windowWideMouseButtonEventHandler);
            // Do something, save text or cancel focus.
        }
    }

    ...
}

And in XxxWindow.xaml set GotFocus property for the TextBox.

<TextBox ... GotFocus="SearchBox_GotFocus">
...
</TextBox>

Done.

About this:

Added a PreviewMouseDownOutsideCapturedElement - but can never get this to fire.

I only got it fired if a MessageBox was shown whenever CaptureMouse was called, which was not supposed to happen in the real world. Maybe that's designed for popups.

Upvotes: 1

Keithin8a
Keithin8a

Reputation: 961

I managed to find a solution.

Basically I bound an event in my code behind for OnGotFocus of the textbox. When it hits this method it adds an event for MouseDown and MouseLeave to the usercontrol. So when you click anywhere in the usercontrol it updates the contents of the textbox.

If you leave the bounds of the usercontrol then you want to shift the mouse down event to the window itself. Similarly when you move back into the usercontrol you want to remove the mouse down event on the window or else when you click back into the textbox after leaving, it sets the value.

I have added my code here, hopefully it will be clear to whoever may have a similar problem.

I am sure there must be a better way of doing it than using the code behind but I don't think I am breaking MVVM principles here so I think I am safe.

        private void View_OnMouseDown(object sender, MouseButtonEventArgs e)
    {
        updateTextbox();
    }

    private void View_OnMouseLeave(object sender, MouseEventArgs e)
    {
        Window parentWindow = Window.GetWindow(this);

        if (parentWindow != null)
        {
            Mouse.AddPreviewMouseDownHandler(parentWindow, ParentWindow_OnMouseDown);
        }
    }

    private void View_OnMouseEnter(object sender, MouseEventArgs e)
    {
        Window parentWindow = Window.GetWindow(this);

        if (parentWindow != null)
        {
            Mouse.RemovePreviewMouseDownHandler(parentWindow, ParentWindow_OnMouseDown);
        }
    }

    private void ParentWindow_OnMouseDown(object sender, MouseButtonEventArgs mouseButtonEventArgs)
    {
        Window parentWindow = Window.GetWindow(this);

        if (parentWindow != null)
        {
            Mouse.RemovePreviewMouseDownHandler(parentWindow, ParentWindow_OnMouseDown);
        }
        updateTextbox();
    }

    private void updateTextbox()
    {
        Keyboard.Focus(this);
    }

    private void Text_OnGotFocus(object sender, RoutedEventArgs e)
    {
        MouseDown += View_OnMouseDown;
        MouseLeave += View_OnMouseLeave;
        MouseEnter += View_OnMouseEnter;
    }

    private void Text_OnLostFocus(object sender, RoutedEventArgs e)
    {
        BindingOperations.GetBindingExpression(text, TextBox.TextProperty).UpdateSource();
        MouseDown -= View_OnMouseDown;
        MouseLeave -= View_OnMouseLeave;
        MouseEnter -= View_OnMouseEnter;

    }

Upvotes: 2

Related Questions