shadowCODE shadowCODE - 3 months ago 71
C# Question

UWP Binding to Observable Collection of UserControl not working

I have this page:

NewsPage


<Page
x:Class="TouchTypeRacing.Views.NewsPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:TouchTypeRacing.Views"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="using:TouchTypeRacing.Controls"
xmlns:models="using:TouchTypeRacing.Models"
DataContext="{Binding}"
mc:Ignorable="d">

<Grid Background="White">
....
<ScrollViewer Grid.Row="1"
VerticalScrollBarVisibility="Auto"
Margin="5,10,5,0">
<ItemsControl ItemsSource="{Binding Posts}"
x:Name="itemsControl">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="models:Post">
<controls:Post/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
</Page>


The page's datacontext is bound to a view model.
PageViewmModel


The
ItemsControl
datatemplate is a
Post
control.

The
ItemsSource
of the
ItemsControl
on the page is bound to the
Posts
property of the viewmodel.

public NewsPage()
{
this.InitializeComponent();

_viewModel = new NewsPageViewModel();
DataContext = _viewModel;
}


Then, the view model:

public class NewsPageViewModel
{
private ObservableCollection<Post> _posts = new ObservableCollection<Post>();
public ObservableCollection<Post> Posts { get { return _posts; } }
public NewsPageViewModel()
{
GetPosts(_posts);
}
public static void GetPosts(ObservableCollection<Post> posts)
{
posts.Clear();
posts = new ObservableCollection<Post>
{
new Post
{
Id = "1",
DateTime = DateTime.Today,
User = Application.CurrentUser,
Likes = 10,
ImagePath = Application.CurrentUser.ImagePath,
Message = "Test message",
Comments = new ObservableCollection<Comment>
{
new Comment {Id= "1", Content="Comment1", User = Application.CurrentUser },
new Comment {Id= "2", Content="Comment2", User = Application.CurrentUser },
new Comment {Id= "3", Content="Comment3", User = Application.CurrentUser },
new Comment {Id= "4", Content="Comment4", User = Application.CurrentUser },
},
Last2Comments = new List<Comment>
{
new Comment {Id= "3", Content="Comment3", User = Application.CurrentUser },
new Comment {Id= "4", Content="Comment4", User = Application.CurrentUser },
}
}
};
}


The
ItemsControl
shows up empty.
What am I doing wrong?

Answer

If you want to keep switching collections, NewsPageViewModel must implement INotifyPropertyChanged and raise PropertyChanged when you change collection instances. Here's how that goes. I've also included a much simpler "quick fix" solution below.

public class VIewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public void OnPropertyChanged([CallerMemberName] String propName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
    }
}

public class NewsPageViewModel : ViewModelBase
{
    public NewsPageViewModel()
    {
        Posts = GetPosts();
    }

    private ObservableCollection<Post> _posts;
    public ObservableCollection<Post> Posts
    {
        get { return _posts; }
        set {
            _posts = value;
            OnPropertyChanged();
        }
    }

    protected ObservableCollection<Post> GetPosts()
    {
        //  do stuff to get posts, return new ObservableCollection<Post>
    }
}

Quick Fix

Easier is to start out with a collection instance so the UI gets something the first (and in this case, ONLY) time it grabs the value of Posts, keep the same collection. However, without INotifyPropertyChanged, you're going to run into no end of issues like this. Trying to use WPF without it is really an exercise in self-punishment.

ObservableCollection<Post> _posts = new ObservableCollection<Post>();

public ObservableCollection<Post> Posts { get { return _posts; } }
public NewsPageViewModel()
{
    GetPosts(_posts);
}
public static void GetPosts(ObservableCollection<Post> posts)
{
    posts.Clear();
    //  ...get posts
}

The special thing about ObservableCollection is that it implements INotifyCollectionChanged, which will notify the UI when you add or remove items from the collection -- but the UI has to know which collection to listen to for changes.

Comments