2
votes

I am rendering this ListView

<StackLayout>
<Label Text="Test"/> //This Label is getting rendered
<ListView  ItemsSource="{Binding MusicList,Mode=TwoWay}"
                HasUnevenRows="True"
                IsPullToRefreshEnabled="True"
                CachingStrategy="RecycleElement"
                SeparatorColor="DarkGray">
      <ListView.ItemTemplate>
            <DataTemplate>
                <ViewCell>
                    <Label Text="{Binding Name}" TextColor="Black"/>
                </ViewCell>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</StackLayout>

Whose ItemsSource is bound to an ObservableCollection<Music> which is available in the ViewModel.This is how i am binding the ViewModel

public MainPage()
{
    BindingContext = new MainViewModel();
    InitializeComponent();
}

This is my ViewModel and here im calling the API methods to get the data.and

    private ObservableCollection<Music> _MusicList=new ObservableCollection<Music>();
    public ObservableCollection<Music> MusicList
    {
        get { return _MusicList; }
        set
        {
            if (_MusicList != value)
            {
                _MusicList = value;
                OnPropertyChanged("_MusicList");
            }
        }
    }
    public MainViewModel()
    {
        api = new ApiHelper();
        GetSongs();
    }

    public async void GetSongs()
    {
        _MusicList = new ObservableCollection<Music>(await api.GetMusicListAsync());
    }

The data is coming in the ObservableCollection<Music> enter image description here

UPDATE

Just for testing I tried binding a simple string List ListStrings = new List<string> {"A","B","C","D","E","F" }; but it also didnt show up.

UPDATE Now i override onAppearing method and initialized my list and set ListView ItemsSource to it. and its working now..but dont know why its not working with binding.

4
Replace this MusicList = new ObservableCollection<Music>(await api.GetMusicListAsync()); with this _MusicList = new ObservableCollection<Music>(await api.GetMusicListAsync());MShah
Do you have a Property Name in your Music class?Dennis Schröer

4 Answers

4
votes

There's a couple of things that could be problematic.

  1. ItemsSource="{Binding MusicList,Mode=TwoWay}" is there a good reason to make it TwoWay? If not, don't do it.
  2. Set your BindingContext after the InitializeComponent();
  3. Do not create a new ObservableCollection when you get data. By creating a new collection, you lose the data binding. Instead, change it like this:

    public async void GetSongs()
    {
        var result = await api.GetMusicListAsync();
    
        foreach (var r in result)
            _MusicList.Add(r);
    }
    
1
votes

I think you made mistake Raising Property Changed :

private ObservableCollection<Music> _MusicList=new 
ObservableCollection<Music>();
public ObservableCollection<Music> MusicList
{
    get { return _MusicList; }
    set
    {

            _MusicList = value;
            OnPropertyChanged("MusicList");  //You should raise property change on your Binding variable.

    }
}

Just like @Gerald said, Follow his advice and I'm adding to give you idea solving the 3rd problem he mentioned.

If you create your Observable Collection every time when you get data, you loose the data binding. If you use foreach method that will raise collection change event multiple times, if you have large list it might be problem for you.

Following Extended collection supports different types of collection and has property to Reset observable collection.

On your View Model Constructor you can Initialize your collection once like :

public MusicPageViewModel()
{
     MusicsData=new ExtendedCollection<Music>();
}

Then whenever you will get data from your API you will do something like :

public GetMusicsData()
{
     MusicsData.Reset(YourApi.GetMusics());
}

I hope it helps you implementing your Observable collection. This is the Extended Observable collection you can use and extend it further more if you need to:

public class ExtendedCollection<T> : ObservableCollection<T>
{
    public ExtendedCollection()
        : base()
    {
    }
    public ExtendedCollection(IEnumerable<T> collection)
        : base(collection)
    {
    }

    public ExtendedCollection(List<T> list)
        : base(list)
    {
    }

    /// <summary>
    /// Reset the existings items and fills the new items provided.
    /// </summary>
    /// <param name="range"></param>
    public void Reset(IEnumerable<T> range)
    {
        this.Items.Clear();

        if (range != null)
        {
            AddRange(range);
        }

    }

    /// <summary>
    /// Add all items in the range;
    /// </summary>
    /// <param name="range"></param>
    public void AddRange(IEnumerable<T> range)
    {
        foreach (var item in range)
        {

            Items.Add(item);
        }

        //Raise the property change!
        this.OnPropertyChanged(new PropertyChangedEventArgs("Count"));
        this.OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
        this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }
}
1
votes

Today I found the solution.

    public MainPage()
    {
        InitializeComponent();
        BindingContext = this; //Added this 
    }

Just Added BindingContext = this; in the constructor.I don't know whats wrong with Xamarin think its a bug.

0
votes

This problem was solved changing the way of declaring properties in the observed object. Example:

This failed

public class MusicItem
{
     public string Name;
}

This worked sucessfully:

public class MusicItem
{
     public string Name {get; set;}
}

The Binding fails to find the property Name in the first example, but worked in the second one.