I noticed that bindings with UpdateSourceTrigger==LostFocus
do not get updated when the user activates the menu or the toolbar.
This leads to the unfortunate situation that the last change that the user made gets lost when the user selects "Save File" from the menu or toolbar.
Is there an easy way around this or do I have to change all my bindings to UpdateSourceTrigger=PropertyChanged
.
While there are useful answers here, IMHO none are really the best way. For me, the best and second-best options are:
TextBox
both before and after the TextBox
in tab order. This is IMHO the best option, but it should be used only when this is a reasonable user interface choice. I.e. there already are UI elements that would naturally surround the TextBox
.LostKeyboardFocus
event and explicitly update the binding then. Note that while the TextBox
hasn't lost focus in terms of the focus scope, it does always lose the keyboard focus if you tab out of it.The second option looks like this:
<TextBox Text="{Binding SomeProperty}" LostKeyboardFocus="TextBox_LostKeyboardFocus"/>
private void TextBox_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
BindingOperations.GetBindingExpression((DependencyObject)sender, TextBox.TextProperty)?.UpdateSource();
}
To elaborate:
The accepted answer defers updating the bound source property until some specific scenario (such as executing a "Save" command) that is known to need the property value occurs. Unfortunately, this negates one key benefits of the MVVM paradigm, which is one doesn't need to worry about when things happen per se, since the binding engine is supposed to take care of everything.
The highest-voted answer improves on this slightly, but as noted in the comments below it, it's still a scenario-specific solution. It would need to be applied for every element in the UI that has its own focus scope. But even worse is that it actually modifies the UI behavior for an element that is otherwise completely unrelated to the element we actually care about. Good coding practices mean fixing the original problem, rather than applying some unrelated change that just happens to have a side-effect that works in our favor.
If one can simply arrange the tab order in the UI such that there is an element in the same focus scope as the TextBox
that immediately follows the TextBox
, then IMHO that'd be the ideal solution. It means that the UI works predictably for the user and the code is aligned with the simplest implementation possible.
But this may not always be possible. In some cases, the natural tab order demands that the TextBox
is immediately preceded or followed by a Menu
, ToolBar
, or other element that is its own focus scope. In such cases, to me the most direct approach is simply to modify the "focus lost" behavior for the binding by explicitly handling the LostKeyboardFocus
event and updating the binding source when that event happens.
I know this is a bit old, but for any future reader, simply setting the following on my ToolBar
worked for me:
FocusManager.IsFocusScope="False"
The problem is that the TextBox does, in fact, not lose focus when the menu item is activated. Thus, the UpdateSourceTrigger LostFocus
does not fire. Depending on your (view)model, UpdateSourceTrigger PropertyChanged
might or might not be a feasible workaround.
For me, PropertyChanged
was not an option (I need to validate the data after the user finished entering it, not in between), so I used a workaround by calling this method before "Save File" (or any other menu/toolbar entry that requires an up-to-date model):
Public Shared Sub SaveFocusedTextBox()
Dim focusedTextBox = TryCast(Keyboard.FocusedElement, TextBox)
If focusedTextBox IsNot Nothing Then
Dim be = focusedTextBox.GetBindingExpression(TextBox.TextProperty)
If be IsNot Nothing Then be.UpdateSource()
End If
End Sub
A few other approaches for this problem can be found in this related question:
(In fact, credit for this method goes to rudigrobler's answer in that question.)
This works well for me:
Private Sub MenuItem_Click(sender As System.Object, e As System.Windows.RoutedEventArgs)
Keyboard.FocusedElement.RaiseEvent(New RoutedEventArgs With {.RoutedEvent = LostFocusEvent})
End Sub
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With