Listbox not updating when items added or removed from ObservableCollection

Ray Brennan picture Ray Brennan · Aug 14, 2013 · Viewed 7.2k times · Source

I have a "Hello World" type application.
A listbox.ItemsSource = Players. Players is an ObservableCollection of Player that implements INotifyPropertyChanged.

When I add items in the Players constructor the items are displayed fine. When I update an existing item the changes are reflected. But when I add or remove items for the ObserveableCollection the listbox does not reflect the Items added / removed.

xaml in MainPage:

    <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
        <ListBox Name="lstPlayers" ItemsSource="{Binding}" Margin="12,66,13,24" Grid.Row="1">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <!--<StackPanel Background="#FFD1D1D1">-->
                    <Grid Margin="0,0,0,0" Grid.RowSpan="2">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="60"/>
                            <ColumnDefinition Width="*"/>
                        </Grid.ColumnDefinitions>
                        <TextBlock Grid.Column="0" Text="{Binding Number}" FontSize="48"  />
                        <TextBlock Grid.Column="1" Text="{Binding Name}" FontSize="48" />
                    </Grid>
                    <!--</StackPanel>-->
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
        <Button Content="Button" Height="95" HorizontalAlignment="Left" Margin="183,350,0,0" Name="button1" VerticalAlignment="Top" Width="260" Click="button1_Click" />
        <Button Content="Button" Height="72" HorizontalAlignment="Left" Margin="198,472,0,0" Name="button3" VerticalAlignment="Top" Width="232" Click="button3_Click" />
    </Grid>

The listbox is bound in the code behind:

  lstPlayers.ItemsSource = plys;

Player Class:

using System.ComponentModel;
namespace DatabindList
{
public class TcrPlayer : INotifyPropertyChanged
{


    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    private string _name;
    public string Name
    {
        get { return _name; }
        set
        {

            if (_name != value)
                _name = value;

            NotifyPropertyChanged("Name");

        }
    }
   }
}

Players class:

using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace DatabindList
{

public class TcrPlayers : List<TcrPlayer> 
{
    public ObservableCollection<TcrPlayers> Items { get; private set; }



    public TcrPlayers()
    {
        //these are displayed
        Add(new TcrPlayer { Name = "Christine"});
        Add(new TcrPlayer { Name = "Janet"});
        Add(new TcrPlayer { Name = "Ian"});
    }


    public void AddPlayer()
    {
        //THIS HAS NO EFFECT ON THE LISTBOX
        Add(new TcrPlayer { Name = "Newbie", Number = "99" });

    }


   }
}

I can UPDATE an existing item in the list by using the code behind in the MainPage with

            var itm = lstPlayers.SelectedItem as TcrPlayer;
            itm.Name = "Fred";

but I can't reflect any changes made to the number of items in the Players collection.

Answer

Matthew Walton picture Matthew Walton · Aug 14, 2013

TcrPlayers derives from List<TcrPlayer> and adds items to itself. However, the ObservableCollection<TcrPlayer> property isn't connected to that list in any way whatsoever. Assuming that plys is a TcrPlayers you're not binding to an ObservableCollection at all - you're binding to a non-observable List.

Inheriting from ObservableCollection<TcrPlayer> instead of List<TcrPlayer> and removing the unused property might get you closer to where you want to be.

As an aside, you don't see this pattern used very often because inheriting from a collection class isn't generally considered very useful unless you're adding new functionality. Usually you'd just have an ObservableCollection<TcrPlayer> property of the parent viewmodel and bind the listbox's ItemsSource to that.

Speaking of which, you're also setting ItemsSource twice - in your XAML to {Binding} i.e. the current DataContext, and in the codebehind.