6

I'm using the MVVM pattern and want to update a ListView using an observable collection. I went through several SO questions, but cannot see what I'm doing wrong. Any help is much appreciated. Thanks.

View.xaml

Namespace: xmlns:local="clr-namespace:MusicPlayer.ViewModel"

DataContext

<UserControl.DataContext>
    <local:AllTracksViewModel/>
</UserControl.DataContext>

ListView

<ListView x:Name="TrackListView" 
                  ItemsSource="{Binding Path=TrackListObservable}">
...
<ListView.View>
   <GridView>
     <GridViewColumn Header="Title" Width="250" DisplayMemberBinding="{Binding Title}" />
      <GridViewColumn Header="Album" Width="200" DisplayMemberBinding="{Binding Album}" />
      <GridViewColumn Header="Artist" Width="150" DisplayMemberBinding="{Binding Artist}" />
      <GridViewColumn Header="Duration" Width="100" DisplayMemberBinding="{Binding FormattedDuration}" />
      </GridView>
    </ListView.View>
</ListView>

ViewModel.cs

public class AllTracksViewModel
{
    public ObservableCollection<Track> TrackListObservable { get; private set; }

    public AllTracksViewModel()
    {
        TrackListObservable = new ObservableCollection<Track>();
    }
}

I verified that items are definitely getting added to the observable. Again, thanks for the help.

M.kazem Akhgary
  • 18,645
  • 8
  • 57
  • 118
HaloMediaz
  • 1,251
  • 2
  • 13
  • 31
  • 1
    Are you implementing INotifyPropertyChanged correctly? – Fang Mar 13 '16 at 20:33
  • Post the code where you update the observablecollection. – Suresh Mar 13 '16 at 20:50
  • @sthotakura I don't have any code. I though the collection does that automatically... – HaloMediaz Mar 13 '16 at 21:17
  • I meant the code where you add items to the observable collection. – Suresh Mar 13 '16 at 21:19
  • You dont need INotifyPropertyChanged. you just have to bind to list correctly. see my answer below – M.kazem Akhgary Mar 13 '16 at 21:21
  • @M.kazemAkhgary the ListView is in a UserControl (I probably should have mentioned that). I thought that there shouldn't be any code behind the view class? – HaloMediaz Mar 13 '16 at 21:26
  • @M.kazemAkhgary You are incorrect. The way the code currently is, a new ObservableCollection is created and assigned to `TrackListObservable`, so INPC is required and `TrackListObservable` needs to notify. Currently it is an autogenerated property so no notification takes place when the collection is replaced. – slugster Mar 13 '16 at 22:34
  • @slugster ahh. Thank you. I will try it out :) – HaloMediaz Mar 13 '16 at 23:20

4 Answers4

7

Change your code to this:

public class AllTracksViewModel : INotifyPropertyChanged
{
    ObservableCollection<Track> trackListObservable;

    public event PropertyChangedEventHandler PropertyChanged;

    public ObservableCollection<Track> TrackListObservable {
      get { return trackListObservable; }
      set {
        trackListObservable = value;
        if(PropertyChanged!=null) {
          PropertyChanged(this, new PropertyChangedEventArgs("TrackListObservable"));
        }
      }
}

    public AllTracksViewModel()
    {
        TrackListObservable = new ObservableCollection<Track>();
    }
}

Just to explain why: every property of your ViewModel should notify of its changes.

Arwin
  • 973
  • 1
  • 10
  • 21
Arnaud Weil
  • 2,324
  • 20
  • 19
  • 1
    Doesn't ObservableCollection already implement INotifyPropertyChanged? – HaloMediaz Mar 13 '16 at 21:17
  • Yes it does, but your TrackListObservable doesn't so it doesn't notify when it is changed. – Arnaud Weil Mar 13 '16 at 21:21
  • 6
    @HaloMediaz Not quite - ObservableCOllection implements INotifyCollectionChanged so that when items are added/removed then anything bound gets updated. You still need to implement INotifyPropertyChanged and have individual properties notify that they've updated otherwise the binding subsystem will never know that the observable collection was assigned to the property. – slugster Mar 13 '16 at 21:22
  • @slugster I don't need to know when individual items are updated. I only need to know when an item was added/removed and that item then reflect the changes in the listview. – HaloMediaz Mar 13 '16 at 21:40
  • ObservableCollection already does this. But I thing you missed INotifyPropertyChanged. Did you try my solution? – Arnaud Weil Mar 14 '16 at 09:37
  • `ObservableCollection` does not have implement `INotifyPropertyChanged`. Model classes should implement `INotifyPropertyChanged`. Please, read this post: http://stackoverflow.com/questions/10246859/what-is-the-difference-between-observablecollection-and-inotifypropertychanged – StepUp Mar 14 '16 at 18:57
  • I'm glad for you HaloMediaz. As I mentioned and StepUp mentioned, a good takeaway is always to notify of property changes in your ViewModel. Don't ever thing about it: notify in every set. :-) – Arnaud Weil Mar 15 '16 at 07:53
  • 2
    @slugster afters hours of searching "You still need to implement INotifyPropertyChanged and have individual properties notify that they've updated otherwise..." was the key phrase. I'm using the Prism library, but once I realized the individual properties also needed to be setup for notification, it all worked. Thanks! – dvdhns Oct 04 '17 at 03:33
  • @dvdhns Thanks for your feedback. If you're still fighting with notification, and generally speaking with MVVM I recomment reading my Learn WPF MVVM book: https://www.amazon.com/dp/1326847996 – Arnaud Weil Oct 06 '17 at 09:31
1

You should write this as itemsource

ItemsSource="{Binding ViewModel.TrackListObservable}"

And also set data context of windows to it self.

<Window DataContext="{Binding RelativeSource={RelativeSource Self}}" ...

With this property in MainWindow.

public AllTracksViewModel ViewModel { get; } = new AllTracksViewModel();

Note that you have to add items to this property. ViewModel.TrackListObservable

You should also remove

<UserControl.DataContext>
    <local:AllTracksViewModel/>
</UserControl.DataContext>

since the data context is the main window it self, thats why itemsource is set to ViewModel.TrackListObservable

M.kazem Akhgary
  • 18,645
  • 8
  • 57
  • 118
1

Your code looks correctly. However, I cannot see the method which populates your TrackListObservable. I suggest to you to call a populating method FillData inside of a constructor. Let me show an example:

public class AllTracksViewModel
{
    private ObservableCollection<Track> _trackListObservable;
    public ObservableCollection<Track> TrackListObservable
    {
       get { return _trackListObservable; }
       set { 
             _trackListObservable = value;                 
           }
    }

    public AllTracksViewModel()
    {
         FillData();
    }

    private void FillData()
    {
       _trackListObservable = new ObservableCollection<Track>();
        for (int i = 0; i < 30; i++)
        {
           TrackListObservable.Add(new Track() { Title = "Ben & Joseph " + i.ToString(), 
                                                                 Artist = "Albahari" });
        }   
    }
}

Please, see a work example of binding ListView using MVVM pattern.

Community
  • 1
  • 1
StepUp
  • 36,391
  • 15
  • 88
  • 148
0

Here your Property TrackListObservable must be a INotify Property or Dependency Property.So that only you can achieve the result what you want.

Refer the below link regards INotify Property: https://msdn.microsoft.com/library/ms743695(v=vs.100).aspx

Vinod Kumar
  • 408
  • 4
  • 18