WPF TextBox value doesn't change on OnPropertyChanged

10,987

Solution 1

The problem is that you are updating the source for the Binding while the Binding is updating your property. WPF won't actually check your property value when it raises the PropertyChanged event in response to a Binding update. You can solve this by using the Dispatcher to delay the propagation of the event in that branch:

set
{
    int val = int.Parse(value);

    if (_runAfter != val)
    {
        if (val < _order)
        {
            _runAfter = val;
            OnPropertyChanged("RunAfter");
        }
        else
        {
            _runAfter = 0;
            Dispatcher.CurrentDispatcher.BeginInvoke(
                new Action<String>(OnPropertyChanged), 
                DispatcherPriority.DataBind, "RunAfter");
        }
    }
}

Update:

The other thing I noticed is that the Binding on your TextBox is using the default UpdateSourceTrigger, which happens when the TextBox loses focus. You won't see the text change back to 0 until after the TextBox loses focus with this mode. If you change it to PropertyChanged, you will see this happen immediately. Otherwise, your property won't get set until your TextBox loses focus:

<TextBox Name="txtRunAfter" Grid.Column="4" Text="{Binding RunAfter, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource TestStepTextBox}"/>

Solution 2

A few things I noticed here.

Unless you have a compelling reason to expose the RunAfter property as a string, there's no reason why it can't be an int. That would save you the cast in the setter (as well as a lurking possible InvalidCastException if the user enters something non-integer in the field).

Secondly, the OnPropertyChanged() call should occur outside of the inner if statement, like the following:

if(_runAfter != val)
{
    if(val < _order)
        _runAfter = val;
    else
        _runAfter = 0;
    OnPropertyChanged("RunAfter");
}

Since the _runAfter local is being updated in both paths of the conditional, the OnPropertyChanged() has to be called regardless of the branch taken. I hope that helps point you in the right direction!

Solution 3

I had the same situation. I wrote the following and it worked.

<TextBox Grid.Column="3" Grid.Row="0" HorizontalAlignment="Left" VerticalAlignment="Center" Width="100" Text="{Binding Path=FirstName}"></TextBox>

And

public string FirstName
    {
        get { return _client.FirstName; }
        set
        {
            if (value == _client.FirstName)
                return;
            else
                _client.FirstName = value;
            OnPropertyChanged("FirstName");
        }
    }
Share:
10,987

Related videos on Youtube

jpsstavares
Author by

jpsstavares

Working as QA Engineer at Blip, Portugal. An avid reader about everything tech related from inovation to software testing. Kindle fan.

Updated on April 25, 2022

Comments

  • jpsstavares
    jpsstavares about 2 years

    I have a TextBox whose Value is binded to a ViewModel property:

            <TextBox Name="txtRunAfter" Grid.Column="4" Text="{Binding Mode=TwoWay, Path=RunAfter}" Style="{StaticResource TestStepTextBox}"/>
    

    The set and get were working fine until I tried to add some validation when the Value is set:

        private int _runAfter = 0;
        public string RunAfter
        {
            get
            {
                return _runAfter.ToString();
            }
    
            set
            {
                int val = int.Parse(value);
    
                if (_runAfter != val)
                {
                    if (val < _order)
                        _runAfter = val;
                    else
                    {
                        _runAfter = 0;
                        OnPropertyChanged("RunAfter");
                    }
                }
            }
        }
    

    Although the OnPropertyChanged is reached (I have dubugged that), the View is not changed. How can I make this work?

    Thanks, José Tavares

  • Robaticus
    Robaticus almost 14 years
    +1 for noting that you need to move the OnPropertyChanged outside of the else.
  • jpsstavares
    jpsstavares almost 14 years
    Well, the set is called by the View (via binding) so I thought it would only be necessary to call OnPropertyChanged if the value would be changed from the one setted in the View.
  • Eric Olsson
    Eric Olsson almost 14 years
    Regardless of how the setter is called, any change must raise the PropertyChanged event to let listeners know that they need to refresh their value. Have you looked at using ValidationRules for doing validation rather than embedding it in your setter code? msdn.microsoft.com/en-us/library/…
  • jpsstavares
    jpsstavares almost 14 years
    I guess your assessment of the problem is correct, but the dispatcher call doesn't work. My UserControl is used inside a WinForm application using an ElementHost. May this be affecting the Dispatcher call?
  • Abe Heidebrecht
    Abe Heidebrecht almost 14 years
    Okay, I tested this out and it worked fine (when you tab away from the TextBox as the default mode of the binding won't update the property until the TextBox loses focus). I updated the answer to explain about the UpdateSourceTrigger on the bindings, in case that is the behavior that you are seeing. I don't deal a lot with WPF hosted in WinForms, but I don't see why that would affect the Binding or Dispatcher in your context.