WPF: Data bound TabControl doesn't commit changes when new tab is selected
Asked Answered
S

4

15

I've got a TabControl where each Tab and it's contents are databound to an ObservableCollection:

<TabControl ItemsSource="{Binding Path=.}">
    <TabControl.ContentTemplate>
        <DataTemplate>
            <TextBox Text="{Binding Path=propertyValue}" />
        </DataTemplate>
    </TabControl>
</TabControl>

If I were to click on Tab 1, then type something into the text box and hit tab so that the TextBox loses focus, the new data that I typed into the textbox would be committed to the ObservableCollection item.

However, if I type data into the TestBox and then immediately click on another tab, the data is never committed. Plus, when I go back to the data, it's no longer set to what I had typed in.

Anyone know a way to force the data to get committed before the current tab is changed?

UPDATE & FIX

What I did was wired up the SelectionChanged event:

private void tabData_SelectionChanged(object sender, SelectionChangedEventArgs e) {
    theTabControl.Focus();         
}

Calling Focus() on the TabControl makes the TextBox lose focus and commit data. I did this because I have other controls -- such as DatePicker -- which exhibit a similar behavior. This is sort-of a catch all.

Second answered 18/4, 2012 at 11:56 Comment(5)
It should be commited on focus lost. Try add another TextBox to template and switch focus to it. Is it works?Finer
@Finer You are correct, it does commit when focus is lost. However, the focus is not lost (or the event is not fired) when I select a new tab.Second
Thank you so much. It saved me after 5h of search - TabControl.Focus() saved my ass. Good edit :)Lynettalynette
Has this been changed in newer .net versions? Because the fix doesn't work for me... (tried .NET Core 3.1 and .NET Framework 4.7.2) Or is there something I missed in the fix?Rozella
The solution based on SelectionChanged doesn't work for me too. Seems like it fires after the control has already lost its content.Thersathersites
F
15

This issue is well described here: WPF Binding: Use LostKeyboardFocus instead of LostFocus as UpdateSourceTrigger Very interesting to see that guys from Microsoft knows about this problem for several years but still not fixed it. Also a big discussing here: WPF Databind Before Saving

This hack works:

    <TabControl SelectionChanged="OnSelectionChanged">

And codebehind:

    private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (Keyboard.FocusedElement is TextBox)
            Keyboard.FocusedElement.RaiseEvent(new RoutedEventArgs(LostFocusEvent));
    }
Finer answered 18/4, 2012 at 12:31 Comment(2)
The first document you quoted "WPF Binding: Use LostKeyboardFocus instead of LostFocus as UpdateSourceTrigger" is not available anymore since they retired microsoft connect. I couldn't find it either on the web or on the microsoft support site.Thersathersites
Doesn't work for me. The FoucsedElement seems to be the new TextBox, so the LostFocusEvent is never risen on the old TextBox.Thersathersites
H
2

If you select a new tab the old one will be unloaded from visual tree. I assume that's why the change is not commited. You could try to stop this behaviour or as a workaround you could set UpdateSourceTrigger to PropertyChanged:

<TextBox Text="{Binding Path=propertyValue, UpdateSourceTrigger="PropertyChanged"}" />
Hauser answered 18/4, 2012 at 12:20 Comment(2)
That could potentially work, though the problem is that I'm saving the data when the the ObservableCollection is changed. I'm guessing that would mean each keystroke would cause data to be saved?Second
It would indeed be saved on every keystroke. Alternatively you can change it to UpdateSourceTrigger=Explicit and fire the update yourself when you want it too, but I think PropertyChanged is the best optionEvante
L
1

This may the neatest answer if you have other controls or ways of transitioning from the textbox apart from a tab control. If the textbox loses keyboard focus in any way, it gets upgraded to lost focus.

<TextBox PreviewLostKeyboardFocus="commentTextBox_PreviewLostKeyboardFocus"  Name="commentTextBox" Text="{Binding Comment, UpdateSourceTrigger=LostFocus}"/>

and in the event handler in codebehind:-

    private void commentTextBox_PreviewLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
    {
        (sender as TextBox).RaiseEvent(new RoutedEventArgs(LostFocusEvent));
    }

This has the effect of upgrading the 'lost keyboard focus' to a fully fledged 'lost focus' event at preview stage.

Lorenzo answered 5/10, 2017 at 14:59 Comment(0)
P
-1

maybe you can try this

<TabControl ItemsSource="{Binding Path=.}">
<TabControl.ContentTemplate>
    <DataTemplate>
        <TextBox Text="{Binding Path=propertyValue,UpdateSourceTrigger=LostFocus}" />
    </DataTemplate>
</TabControl>

Phytogenesis answered 18/4, 2012 at 12:22 Comment(2)
This is Default for TextBoxes.Hauser
Thanks - though the problem is that LostFocus isn't being triggered when I select a new tab.Second

© 2022 - 2024 — McMap. All rights reserved.