Question

At first I want to say that sample below is oversimplification. Suppose you have bound WPF control.

<Window Title="Window1" Height="300" Width="300">
<Grid>
    <StackPanel>
        <TextBox Text="{Binding Name}" Margin="10"/>
        <Button HorizontalAlignment="Center" 
        Content="Click Me" Margin="5" 
        Padding="2" Click="OnButtonClick" />
    </StackPanel>
</Grid>
</Window>

Window is bound to the Person class which implements INotifyPropertyChanged and has Name setter in form

    public string Name 
    {
        get { return _name; }
        set 
        {
            _name = "Some Name";
            OnPropertyChanged("Name");
        }
    }

I.e. _name is assigned "Some Name" whenever user tries to change it from UI. But this sample does not works. I changed name in TextBox to some value press tab forcing focus to move to the Button and value in TextBox remains unchanged although PropertyChanged event was triggered.

Could you please explain me why it happens? As I understand PropertyChanged event forces UI to reread values from properties and display them but in my example value in databound textbox is not updated.


Again. I understand that this is poor implementation of the property and but I want to repeat that this is oversimplification. It is just a sample. But anyway, PropertyChanged signals that property was changed and should be updated but it does not.

Was it helpful?

Solution

The PropertyChanged event is ignored by the TextBox because it is the initiator of the event.

Some clarification:

The TextBox (or the binding on the textbox) knows it is the initiator because it receives the PropertyChanged event in the same call. By doing an asynchronous call, the textbox (or binding) has no way to know that it is the initiator, so it will process the event as if someone else has updated it

If you add a 2nd textbox to your UI, you'll see that the 2nd TextBox does change when you edit the 1st, and the other way around.

OTHER TIPS

The dummy converter workaround suggested by Heinzi (described here) doesn't work when binding's UpdateSourceTrigger is PropertyChanged. But what if this is what we need?

It seems that making the binding asynchrounous does the trick, e.g.:

SelectedIndex="{Binding SelectedIndex, IsAsync=True}"

As Bubblewrap already pointed out, this is by design -- the textbox assumes that if it sets a bound property to some value, the setter will not change the value. According to Microsoft, they won't change this behavior since this would break existing code.

If you want to change the value (yes, there are perfectly good reasons for doing that), you have to use a workaround, for example, by adding a dummy converter. There is a blog entry (not written by me) describing this technique in detail.

The reason is because you have hardcoded the 'Some Name' in the setter. When you changed the textBox value the setter is actually getting called and it again setting "Some Name" as the propertyValue so it doesnt seems to be changed in the UI. Put _name = value and things will just work as you expected,

public string MyField  
{  
    get { return _myField; }  
    set {   
        if (_myField == value)  
            return;  

        _myField = value;   

        OnPropertyChanged("MyField");  
    }  
}  

This is the proper implementation of the property.

When you change the property, make sure that the EXACT same instance of the object is binded to a control. Otherwise, the change will be notified but the control will never get it because the control is not binded properly.

Replacing setter in form

        set 
        {
            _name = "Some Name";
            Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.DataBind, 
                (SendOrPostCallback)delegate { OnPropertyChanged("Name"); },
                null);
        }

resolves the issue but it is still open. Why should I make async call instead of synchronous signaling that my property has been changed.

If I am not mistaken, the default binding behavior of the Text property on the TextBox is TwoWay, so this should work. You can force it to be TwoWay in the XAML like this:

<Window Title="Window1" Height="300" Width="300">
  <Grid>
    <StackPanel>
        <TextBox Text="{Binding Name, Mode=TwoWay}" Margin="10"/>
        <Button HorizontalAlignment="Center" 
        Content="Click Me" Margin="5" 
        Padding="2" Click="OnButtonClick" />
    </StackPanel>
  </Grid>
</Window>

Note the Mode=TwoWay in the Binding declaration.

If that doesn't work, then I suspect that an exception is being thrown in the code that fires the event, or assigns the property and you should look for that.


There seems to be a possibility that you are making the call to change the value on a thread that is not the UI thread. If this is the case, then you either have to marshal the call to fire the property changed event on the UI thread, or make the change to the value on the UI thread.

When an object is bound to a UI element, changes to the object which can affect the UI have to be made on the UI thread.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top