Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF TextBox value doesn't change on OnPropertyChanged

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

like image 305
jpsstavares Avatar asked Jun 16 '10 17:06

jpsstavares


3 Answers

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}"/>
like image 75
Abe Heidebrecht Avatar answered Oct 28 '22 00:10

Abe Heidebrecht


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!

like image 3
Eric Olsson Avatar answered Oct 28 '22 00:10

Eric Olsson


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");
        }
    }
like image 1
Horizon Avatar answered Oct 27 '22 23:10

Horizon