ObservableCollection not updating View
Asked Answered
T

3

17

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?

Teapot answered 18/12, 2011 at 23:36 Comment(0)
P
20

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(nameof(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

Peavy answered 18/12, 2011 at 23:38 Comment(2)
i have the exact same problem but mine is not updating even after i added the raisepropertychangedNook
Does one need to bother using an ObservableCollection in this case if it's not updating automatically? Could you just use a list? I am having a similar problem...Like
N
6

You need to make your poco class within the ObservableCollection implement INotifyPropertyChanged.

Example:

<viewModels:LocationsViewModel x:Key="viewModel" />
.
.
.    
<ListView
    DataContext="{StaticResource viewModel}"
    ItemsSource="{Binding Locations}"
    IsItemClickEnabled="True"
    ItemClick="GroupSection_ItemClick"
    ContinuumNavigationTransitionInfo.ExitElementContainer="True">

    <ListView.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Name}" Margin="0,0,10,0" Style="{ThemeResource ListViewItemTextBlockStyle}" />
                <TextBlock Text="{Binding Latitude, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Style="{ThemeResource ListViewItemTextBlockStyle}" Margin="0,0,5,0"/>
                <TextBlock Text="{Binding Longitude, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Style="{ThemeResource ListViewItemTextBlockStyle}" Margin="5,0,0,0" />
            </StackPanel>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

public class LocationViewModel : BaseViewModel
{
    ObservableCollection<Location> _locations = new ObservableCollection<Location>();
    public ObservableCollection<Location> Locations
    {
        get
        {
            return _locations;
        }
        set
        {
            if (_locations != value)
            {
                _locations = value;
                OnNotifyPropertyChanged();
            }
        }
    }
}

public class Location : BaseViewModel
{
    int _locationId = 0;
    public int LocationId
    {
        get
        {
            return _locationId;
        }
        set
        {
            if (_locationId != value)
            {
                _locationId = value;
                OnNotifyPropertyChanged();
            }
        }
    }

    string _name = null;
    public string Name
    {
        get
        {
            return _name;
        }
        set
        {
            if (_name != value)
            {
                _name = value;
                OnNotifyPropertyChanged();
            }
        }
    }

    float _latitude = 0;
    public float Latitude 
    { 
        get
        {
            return _latitude;
        }
        set
        {
            if (_latitude != value)
            {
                _latitude = value;
                OnNotifyPropertyChanged();
            }
        }
    }

    float _longitude = 0;
    public float Longitude
    {
        get
        {
            return _longitude;
        }
        set
        {
            if (_longitude != value)
            {
                _longitude = value;
                OnNotifyPropertyChanged();
            }
        }
    }
}

public class BaseViewModel : INotifyPropertyChanged
{
    #region Events
    public event PropertyChangedEventHandler PropertyChanged;
    #endregion

    protected void OnNotifyPropertyChanged([CallerMemberName] string memberName = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(memberName));
        }
    }
}
Noellenoellyn answered 9/4, 2014 at 13:58 Comment(0)
G
-1

Your Subcategories property should be read-only.

Gerhart answered 18/12, 2011 at 23:39 Comment(2)
How does this address the problem?Nerland
@ChrisF: I suspect he's setting the property.Gerhart

© 2022 - 2024 — McMap. All rights reserved.