3
votes

The ListView of my XAML file is being filled with a ViewModel that has an ObservableCollection from a service but the ListView is not showing the information. I already check that the service is returning the correct information.

This is the code of my XML file :

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XAMLUnit1.Views.NetflixRoulettePage" Title="Movie">
    <ContentPage.Content>
         <StackLayout>
         <AbsoluteLayout>
                <BoxView Color="Gray"  AbsoluteLayout.LayoutFlags="All" AbsoluteLayout.LayoutBounds="0,0,1,1"></BoxView>
                <SearchBar x:Name="searchBar"
                           Placeholder="Search By Actor's name" 
                           PlaceholderColor="White"
                           AbsoluteLayout.LayoutFlags="All"
                           AbsoluteLayout.LayoutBounds="0.1,0.1,1,1"  SearchCommand="{Binding SearchMovieCommand}" ></SearchBar>
            </AbsoluteLayout>

       <ListView  x:Name="ListOfMovies"  
            ItemsSource="{ Binding MovieList}" >
            <ListView.ItemTemplate>
                    <DataTemplate>
                        <ViewCell>
                            <StackLayout>
                                <ImageCell 
                                        ImageSource="{Binding poster_path, StringFormat='https://image.tmdb.org/t/p/w500{0}'}">
                                </ImageCell>
                                    <StackLayout Orientation="Horizontal">
                                    <TextCell Detail="{Binding title}"></TextCell>
                                    <TextCell Detail="{Binding release_date}"></TextCell>
                                </StackLayout>
                            </StackLayout>
                        </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

This is the ViewModel that calls the service and it's uses its ObservableCollection as ItemsSource for the ListView :

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using Xamarin.Forms;
using XAMLUnit1.Models;
using XAMLUnit1.ServiceImpl;
using XAMLUnit1.Services;

namespace XAMLUnit1.ViewModels
{
  public  class MovieRouletteViewModel
    {
        IMovieService service;
        public ObservableCollection<Movie> MovieList { get; set; }
        public ICommand SearchMovieCommand { get; set; }
        public MovieRouletteViewModel()
        {
            service = new MovieServiceFinder();
            SearchMovieCommand = new Command(GetMovies);
        }

        private void  GetMovies()
        { var list= service.GetMovies("");
            MovieList = list;

        }

    }
}




public partial class NetflixRoulettePage : ContentPage
    {
        MovieRouletteViewModel viewModel;
        public NetflixRoulettePage()
        {
            InitializeComponent();
            viewModel = new MovieRouletteViewModel();
            BindingContext = viewModel;


        }



        private void SearchBar_TextChanged(object sender, TextChangedEventArgs e)
        {
        }
    }
4
have you set the BindingContext?Jason
Yes I have, its seems that the ListView is not updating dynamically.Look at the GetMovies method, that sets the MovieList property.Jose Agustin Reinoso
@JoseAgustinReinoso Add the code where you are setting the BindingContextSushiHangover
@SushiHangover I posted the code.Jose Agustin Reinoso
GetMovies() is replacing the bound MovieList instance with a new instanceJason

4 Answers

10
votes

Do not set the ObservableCollection to a new List it will break the binding. Clear the items from the list and add the new items to it.

    public MovieRouletteViewModel()
    {
        service = new MovieServiceFinder();
        SearchMovieCommand = new Command(GetMovies);
        MovieList = new ObservableCollection<Movie>();
    }

    private void  GetMovies()
    { 
        var list= service.GetMovies("");
        MovieList.Clear();
        foreach(Movie movie in list)
        {
              MovieList.Add(movie);
        }
    }
4
votes

There is a few things i want to note on this problem, though mainly as the comments rightly illuminate, replacing the collection with a new collection breaks the bindings. hence why its not updating.

However there are several solutions to consider.

  1. Implement INotifyPropertyChanged on the collection and replace the whole collection like you are doing, This will work fine. However it does resets the scroll and basically recalculates everything from scratch. Some would say this defeats the point of an ObservableCollection as they have their own built in notification plumbing, Yeah it does. Though, if you are replacing the whole Collection with a new collection then you are going to save on oodles of Cyclic calculations when compared to cleaRing and adding each item back individually Which will basically fire for every update

  2. Call Clear and Add on each item individually.. If the collection hasn't changed much, you can even take it a step further by just comparing the 2 collections and updating whats needed. However once again, if the collections are dissimilar, then this approach is still expensive, and on a mobile device you want to minimize screen recalculations where ever possible .

  3. Create a subclassed collection, implement your own replace/update, and Add/Remove range methods, and INotifyPropertyChanged giving you the best of both worlds, this will allow atomic modification of the collection and then you can fire property changed event once.

There are many examples of all these approaches online, its just worth noting clearing and adding items sometimes is not the best approach for mobile devices. It depends how big your collections are, how much is changing and why.

  • If it is a completely different list, then replacing the collection is fine in my opinion.
  • If its the same list, then well you may want to compare and modify to save on property changes

Anyway good luck

3
votes

I disagree with other answers. You can set the Movies as many times as you want, this is not the problem.

The problem is just that your viewmodel doesn't implement INotifyPropertyChanged.

Your UI is just not notified when you set your ObservableCollection.

2
votes

Your service command is actually replacing the ObservableCollection, you need to change your GetMovies() method to

var list = service.GetMovies("");
MovieList.clear();
foreach(Movie m in list)
{ MovieList.Add(m);}