0
votes

We have an Item Details window that uses an instance of a ViewModel as the DataContext. We have a simple string property on the ViewModel called StatusMessage used to give feedback to the user. We've implement INotifyPropertyChanged on this view model and the StatusMessage property is configured to raise the event. The ViewModel has three other members that are classes (or collections) and on all of these work the bindings work like they should.

The problem is that the StatusMessage doesn't get updated in the GUI when it gets changed programmatically. We've debugged and found that the property really is changing and the on the Window code behind we subscribed to the property changed event and can see that the event really is firing.

Any control bound to StatusMessage will display whatever is set on the constructor, but it never updates after that.

I used two different controls, one a textbox with twoway binding and the other a label, both bound to StatusMessage. When I change the value using the TextBox, my label also changes. But any changes made inside the ViewModel never get propagated to the GUI.

There are no binding errors. We've even checked the HasBindingErrors property on the PropertyChanged event handler and it shows false and shows that our control is still bound to StatusMessage.

This problem only affects the base/root ViewModel class. All the members that are classes and have their own members and implement INotifyPropertyChanged work without any problems. We have full two way binding for those and they all work.

I've tried cleaning and rebuilding the project and it doesn't make any difference.

What can possibly cause a binding to fail without producing any error messages?

This is the actual code although I've had to trim out a lot in order to make it fit nicely here:

The XAML:

<Window x:Class="PZESpecsApp.SpecDetails"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
    xmlns:local="clr-namespace:SpecsApp"
    Title="Spec Details" Height="600" Width="1000">
    <Grid>
        <TextBlock Text="{Binding spec.MakeAndModelNo}" FontSize="24" FontWeight="Bold" Margin="16,10,0,0" />
        <TextBox Name="txtStatusMessage" Text="{Binding StatusMessage, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" FontSize="24" FontWeight="Bold" Foreground="Black" />
        <Label Grid.Column="0" Content="{Binding Path=StatusMessage}" Foreground="White" />
    </Grid>
</Window>   

The window code:

public partial class SpecDetails : Window
{
    private SpecDetailsViewModel model;

    public SpecDetails(int id)
    {
        InitializeComponent();
        model = new SpecDetailsViewModel(id);
        model.PropertyChanged += ViewModel_PropertyChanged;
        DataContext = model;        
    }

    private void ViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        //this shows that the property actually changes as expected
        Debug.Print("PropertyChanged=" + e.PropertyName);
    }
}

The ViewModel Code:

public class SpecDetailsViewModel
{
    public event PropertyChangedEventHandler PropertyChanged;

    private Spec _spec;
    public Spec spec
    {
        get { return _spec; }
        set { _spec = value; PropertyChanged.Notify(() => this.spec); }
    }

    private string _statusMessage;
    public string StatusMessage
    {
        get { return _statusMessage; }
        set { _statusMessage = value; PropertyChanged.Notify(() => this.StatusMessage); }
    }

    public SpecDetailsViewModel(int id)
    {
        spec = AppData.SpecsList.FirstOrDefault(s => s.id == id);
        StatusMessage = "Monkey See, Monkey Do!"; //this stays on the GUI
    }

    public void SaveSpec() {
         //this doesn't have any affect on the GUI
        this.StatusMessage = "Changes Saved";
    }
}
1
My understanding is that you should avoid programatically changing data bound values in the code-behind anyway.dub stylee
We're actually doing it in the ViewModel. Not sure if you mean that's a bad idea too?HK1
You can just ignore me :P I think it would be difficult to not change any values in the ViewModel!dub stylee
I'm not sure if this is the problem but you have to be sure that the update to the viewmodel is taking place on the gui thread, this is generally done using the dispatcher.user1618236
There is a similar issue described here that might be of use to you.dub stylee

1 Answers

1
votes

OK, I found the problem. I guess you could say we are implicitly implementing INotifyPropertyChanged and apparently that's not good enough. The fix is in the ViewModel class:

public class SpecDetailsViewModel : INotifyPropertyChanged