views:

842

answers:

10

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.

A: 

Are you sure the binding works? Have you checked the output window for errors? Have you set the DataContext property of the Window or something to make it work? Are you sure the setter is called? I do not think INotifyPropertyChanged is the problem.

Lars Truijens
Please keep clarification questions to the comments. That helps keeping the content readable.
David Schmitt
A: 

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,

Jobi Joy
A: 

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.

casperOne
A: 
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.

Maxim
A: 

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.

Oleg
A: 

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.

Oleg
Are you modifying the value from another thread? If so, it will not work, you have to modify the value on the UI thread, so that the event fires on the UI thread, and the databinding can occur correctly.
casperOne
Please keep clarifications to the comments or edit your post. That helps keeping the content readable.
David Schmitt
A: 

Err, I may be wrong, but shouldn't it be {Binding Path=Name}?

Dmitri Nesteruk
Nope. For property bindings, you can leave out Path as a shortcut.
Ray Booysen
This is because Binding has a constructor that takes path as a parameter.
Ray Booysen
Please keep clarification questions to the comments. That helps keeping the content readable.
David Schmitt
+6  A: 

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.

Bubblewrap
But why asynchronous call works fine?
Oleg
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
Bubblewrap
+1  A: 

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.

Heinzi
A: 

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}"
Outofmem