ObservableCollection not updating View

David Duncan picture David Duncan · Dec 19, 2011 · Viewed 24k times · Source

I am just starting with MVVM and have hit a hurdle that I hope someone can help me with. I am trying to create a simple View with 2 listboxes. A selection from the first listbox will populate the second list box. I have a class created that stores the information I want to bind to.

MyObject Class (Observable Object is just a base class that implements INotifyPopertyChanged)

public class MyObject : ObservableObject
{
    String _name = String.Empty;
    ObservableCollection<MyObject> _subcategories;

    public ObservableCollection<MyObject> SubCategories
    {
        get { return _subcategories; }

        set
        {
            _subcategories = value;
            RaisePropertyChanged("SubCategories");
        }
    }

    public String Name
    {
        get { return _name; }
        set
        {
            _name = value;
            RaisePropertyChanged("Name");
        }
    }


    public MyObject()
    {
        _subcategories = new ObservableCollection<EMSMenuItem>();
    }
}

In my viewmodel I have two ObservableCollections created

public ObservableCollection<EMSMenuItem> Level1MenuItems { get; set; }
public ObservableCollection<EMSMenuItem> Level2MenuItems { get; set; }

In my constructor of the ViewModel I have:

this.Level1MenuItems = new ObservableCollection<EMSMenuItem>();
this.Level2MenuItems = new ObservableCollection<EMSMenuItem>();
this.Level1MenuItems = LoadEMSMenuItems("Sample.Xml");

That works fine for the Level1 items and they correctly show in the View. However I have a command that gets called when the user clicks an item in the listbox, which has the following:

Level2MenuItems = ClickedItem.SubCategories;

For some reason this does not update the UI of the second listbox. If I put a breakpoint at this location I can see that Level2MenuItems has the correct information stored in it. If I write a foreach loop and add them individually to the Level2MenuItems collection then it does display correctly.

Also as a test I added the following to the constructor:

Level2MenuItems = Level1MenuItems[0].SubCategories;

And that updated correctly.

So why would the code work as expected in the constructor, or when looping through, but not when a user clicks on an item in the listbox?

Answer

aqwert picture aqwert · Dec 19, 2011

You need to raise the change notification on the Level2MenuItems property.

Instead of having

public ObservableCollection<EMSMenuItem> Level2MenuItems { get; set; }

you need

private ObservableCollection<EMSMenuItem> _level2MenuItems;
public ObservableCollection<EMSMenuItem> Level2MenuItems
{
    get { return _level2MenuItems; }
    set 
     {
        _level2MenuItems = value; 
        RaisePropertyChanged("Level2MenuItems");
     }
 }

The reason the former works in the constructor is that the Binding has not taken place yet. However since you are changing the reference via a command execute which happens after the binding you need to tell view that it changed