4
votes

Xamarin.Forms 4.0 collection view ItemsSource binding is not working as expected if I set the binding in code behind. The items are displayed based on the initial value of the source collection but, the UI is not updating when the source collection is updated. Same is working if I set the binding in xaml.

Code behind:

    public MainPage()
    {
        this.InitializeComponent();
        this.BindingContext = this.mainViewModel = new MainViewModel();


        CollectionView courseCollectionView = new CollectionView
        {
            ItemSizingStrategy = ItemSizingStrategy.MeasureFirstItem,
            ItemTemplate = new DataTemplate(typeof(ItemView))
        };


     courseCollectionView.SetBinding(CollectionView.ItemsSourceProperty, 
     new Binding() { Source = this.mainViewModel.CountryNames, Mode = 
     BindingMode.TwoWay });

     courseCollectionView.ItemsLayout = new GridItemsLayout(4, 
      ItemsLayoutOrientation.Vertical);

        this.CoursesStack.Children.Clear();
        this.CoursesStack.Children.Add(courseCollectionView);

    }

View Model Property which is using for ItemsSource Binding:

   ObservableCollection<Country> countryNames;

    public ObservableCollection<Country> CountryNames
    {
        get => this.countryNames;
        set
        {
            this.countryNames = value;
            RaisePropertyChanged("CountryNames");
        }
    }

Expected: View should be updated as per the changes made to the ObsevableCollection (add/delete items from collection) which is bound to the ItemsSource Property.

Actual: View is not updated with changes to the ObservableCollection.

3
when you say "not updated with changes", to do you mean add/deleting items from the Collection? Or do you mean changes to the properties of individual items in the Collections?Jason
When I add/delete items from the collection, the UI is not updated.Narender Reddy
You don't need to bind the ItemsSource in the code behind - you can just assign it directly. But this is probably not the root problem. You also should not need a two way binding for a CollectionViewJason
Yes, I can bind it in xaml but, there is a an open bug/task on collection view as mentioned here github.com/xamarin/Xamarin.Forms/issues/5354. And I need to update the ItemsLayout on device orientation change. Anyways @ottermatic solution worked for me. thank you :)Narender Reddy
@Jason how about when you change the Item properties?Nasreddine Galfout

3 Answers

2
votes

About updating UI when using CollectionView, I do one sample that you can take a look:

public partial class MainPage : ContentPage
{
    public mainvidemodel viewmodel { get; set; }
    public MainPage()
    {
        InitializeComponent();
        viewmodel = new mainvidemodel();
        this.BindingContext = viewmodel;

        CollectionView collectionView = new CollectionView();

        collectionView.SetBinding(ItemsView.ItemsSourceProperty, "countries");

        collectionView.ItemTemplate = new DataTemplate(() =>
        {
            StackLayout stacklayout = new StackLayout();

            Label label1 = new Label();
            label1.SetBinding(Label.TextProperty,"Id");

            Label label2 = new Label();
            label2.SetBinding(Label.TextProperty, "Name");

            Label label3 = new Label();
            label3.SetBinding(Label.TextProperty, "caption");


            stacklayout.Children.Add(label1);
            stacklayout.Children.Add(label2);

            stacklayout.Children.Add(label3);
            return stacklayout;

        });
        Button btn = new Button() { Text = "btn", WidthRequest = 200, HeightRequest = 50 };
        btn.Clicked += Btn_Clicked;
        stacklayout1.Children.Add(collectionView);
        stacklayout1.Children.Add(btn);

    }

    private void Btn_Clicked(object sender, EventArgs e)
    {
        viewmodel.countries.Add(new Country()  { Id = 8, Name = "country8", caption = "caption 8" });
    }
}
public class mainvidemodel
{
    public ObservableCollection<Country> countries { get; set; }
    public mainvidemodel()
    {
        countries = new ObservableCollection<Country>()
        {
            new Country(){Id=1,Name="country1",caption="caption 1"},
            new Country(){Id=2,Name="country2",caption="caption 2"},
            new Country(){Id=3,Name="country3",caption="caption 3"},
            new Country(){Id=4,Name="country4",caption="caption 4"},
            new Country(){Id=5,Name="country5",caption="caption 5"},
            new Country(){Id=6,Name="country6",caption="caption 6"},
            new Country(){Id=7,Name="country7",caption="caption 7"},

        };

    }
}
public class Country
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string caption { get; set; }
}

enter image description here

1
votes

I believe your binding is wrong. Try: courseCollectionView.SetBinding(CollectionView.ItemsSourceProperty, nameof(mainViewModel.CountryNames));

You need to specify the Path (mainViewModel.CountryNames) and not the Source

0
votes

In this case try to extend the MainPage from INotifyPropertyChanged and instead of RaisePropertyChanged("CountryNames") in set property, use OnPropertyChanged()