Updating bound WPF control without tabbing out
I've had an opportunity to do WPF development professionally (as opposed to hobby-tier) for the first time over the last year or so, & while the improvements over WinForms are staggering (and as a lifelong web developer, also familiar), there's plenty that's not intuitive.
One thing that bears a little explanation is this scenario, which I expect would be fairly common.
Scenario
I have a data entry form following the MVP pattern (that's Model-View-Presenter, very similar to MVC in the web world), with text boxes & other controls bound to a C# object. It has a toolbar with a Save button & other buttons on it. The user enters some text, clicks Save, & it's sent to the server to be saved. That's it.
Problem
When I enter data into the text boxes, then click Save, it was ignoring my changes to the last text box I typed into. If I tab out of that text box, then clicked Save, it saved it fine, but what user is going to think to do that? My application should work the way the user would expect; entering data into a form & saving it shouldn't require training specific to one application.
Fortunately, I'm
not the only one that had this problem. Like in the question, I knew I could solve this by adding
UpdateSourceTrigger=PropertyChanged to my text
box's binding, but I didn't want to do that--that didn't
play well with the event handler with some fairly complex
validation logic I had for one of the text boxes, nor the
formatting for another. I didn't want to choose between text
formatting & validation working correctly, and save
working intuitively.
The accepted answer states this happens because the Save
button doesn't take focus away from the text box, butĀ only when the Save button is in a toolbar. When I took it out of the toolbar, it received focus
& everything worked great. But the toolbar, and thus the
button within it, has a different
FocusManager.FocusScope, so the last text box
retains focus even though the Save button gets focused as
well. I considered changing this behavior, but again, if
this is the standard users are used to for WPF apps, I can
understand the benefits & wouldn't want to mess with
that streamlined experience.
Solution
The answer lists several ways to fix the behavior. The one I
chose, which felt the least like a hack & worked for
this user control, was to manually force the update. When
the save button is clicked, I call
Keyboard.FocusedElement as TextBox, thenĀ TextBox.GetBindingExpression(TextBox.TextProperty).UpdateSource()
(with all the requisite null checks, of course), then
retrieve the data from the bound object to send to the
server. It worked like a charm, and was easy to centralize
in my base control class, though at first glance it looks
like it'd be specific to each type of control you want to
have this behavior--TextBox, ComboBox, etc. But in my
application, there would only be a handful anyway, so that's
just a different code path to get the BindingExpression for
each--three lines of code for each control type.