Search code examples
c#wpfmvvmwpf-controlswcf-binding

ViewModel updates interrupts user interaction with bound TextBox


I'm having a problem where user input is interrupted by updates from the bound ViewModel.

The ViewModel exposes a numerical value which changes each second. This is bound to a TextBox in the view. We want the TextBox to display this value, which it does well.

However, when the user clicks on the TextBox and tries to enter a new value, the value in the model gets updated which causes the user-entered value in the textbox to be overwritten.

How can I most easily solve this issue, supporting user input and regular updates in the same control? I would appreciate code examples (C# / XAML).

If you need more details just ask :)


Solution

  • When TextBox gets focus (IsFocused property equals true) I change binding type so that it would not be updated unless it looses focus.

    ViewModel:

     class MainViewModel : INotifyPropertyChanged
        {
            private string _name;
    
            public string Name
            {
                get { return _name; }
                set
                {
                    _name = value;
                    Debug.WriteLine(value);
                    OnPropertyChanged();
                }
            }
    
            public MainViewModel()
            {
                Task.Factory.StartNew(async () =>
                {
                    for (int i = 0; i < 1000; i++)
                    {
    
                        Name = i.ToString();
                        await Task.Delay(3000);
                    }
                });
            }
    
    
            public void OnPropertyChanged([CallerMemberName] string propertyName = null)
            {
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
    
            public event PropertyChangedEventHandler PropertyChanged;
        }
    

    XAML:

    <StackPanel>
        <TextBox>
            <TextBox.Style>
                <Style TargetType="TextBox">
                    <Setter Property="Text" Value="{Binding Name, Mode=OneWay}"/>
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsFocused}" Value="True">
                            <Setter Property="Text" Value="{Binding Name, Mode=OneWayToSource, UpdateSourceTrigger=LostFocus}"/>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </TextBox.Style>
           </TextBox>
        <Button Content="Click"/>
    </StackPanel>
    

    I got sequence 2,3 then I inputed 56 and clicked Button so textbox lost focus and sent value to Name property. Debug printed following sequence:

    1 2 3 56 4