ItemsSource doesn't show/bind when ObservableCollection is filled before loading the page

64 views Asked by At

I have a carouselview, in that view I have an ObservableCollection binded as an itemssource. I am able to bind the collection and it would show when I execute the viewmodel's command in the OnAppearing event.

Code that works:

Second Page

public partial class SecondPage : ContentPage
{

     public Coll(bool hard, string subject)
     {
            InitializeComponent();
            var vm = (DataSelectionViewModel)BindingContext;

            vm.Hard = hard;
            vm.Subject = subject;
            /* had to set "hard" and "subject" here again, otherwise data won't load */
     }

     protected override async void OnAppearing()
     {
         var vm = (DataSelectionViewModel)BindingContext;
         base.OnAppearing();
         await vm.LoadData.ExecuteAsync().ConfigureAwait(false);
     }
}

The viewmodel for second page

public class DataSelectionViewModel : BaseViewModel
{
   private string subject;
   public string Subject { get => subject; set => SetProperty(ref subject, value); }

   private bool hard;
   public bool Hard { get => hard; set => SetProperty(ref hard, value); }

   public ObservableCollection<Items> FilteredData { get; set; }

   public UserSelectionViewModel()
   {
            _dataStore = DependencyService.Get<IDataStore>();

            LoadData= new AsyncAwaitBestPractices.MVVM.AsyncCommand(FilterData);

            FilteredData = new ObservableCollection<Items>();
   }
   public async Task FilterData()
   {
            FilteredData.Clear();
            var filtereddata = await _dataStore.SearchData(Hard, Subject).ConfigureAwait(false);
            foreach (var data in filtereddata)
            { 
                FilteredData.Add(data);
            }
   }  
}

First Page where second page gets Hard and Subject values

private async void ButtonClick(object sender, EventArgs e)
{
   var vm = (BaseViewModel)BindingContext;

   vm.Hard = HardButtonSelected == Hard;
   vm.Subject = vm.Subject.ToLower();

   await Navigation.PushAsync(new SecondPage(vm.Hard, vm.Subject));
   
}

So I want to change my code so that if I press the button on the first page, data instantly starts to filter and add to the ObservableCollection and when it's finished, then navigate to the second page. However if I try to load it to the BaseViewModel and then get the data from the second viewmodel it won't show the data.

Code that doesn't work:

Second Page

public partial class SecondPage : ContentPage
{

     public SecondPage()
     {
            InitializeComponent();
     }
}

The viewmodel for second page

public class DataSelectionViewModel : BaseViewModel
{

   public ObservableCollection<Items> FilteredData { get; set; }

   public UserSelectionViewModel()
   {

            FilteredData = new ObservableCollection<Items>();
   }
}

BaseViewModel

public class BaseViewModel : INotifyPropertyChanged
{
   private string subject;
   public string Subject { get => subject; set => SetProperty(ref subject, value); }

   private bool hard;
   public bool Hard { get => hard; set => SetProperty(ref hard, value); }

   public ObservableCollection<Items> FilteredData { get; set; }

   /* BaseViewModel has implementation of SetProperty */
}

First Page where second page gets Hard and Subject values

private async void ButtonClick(object sender, EventArgs e)
{
   var vm = (BaseViewModel)BindingContext;

   vm.Hard = HardButtonSelected == Hard;
   vm.Subject = vm.Subject.ToLower();
}

First Page viewmodel

    public class FirstPageViewModel : BaseViewModel
    {

        public IAsyncCommand MehetButtonClickedCommand { get; }
        readonly IPageService pageService;
        readonly IFeladatokStore _feladatokStore;
        public FeladatValasztoViewModel()
        {
            _dataStore = DependencyService.Get<IDataStore>();
            ButtonClickedCommand = new AsyncCommand(ButtonClicked);

            pageService = DependencyService.Get<IPageService>();
        }

        private async Task ButtonClicked()
        {
            await FilterData();

            await pageService.PushAsync(new SecondPage());
        }

        private async Task FilterData()
        {
            FilteredData.Clear();
            var datas = await _dataStore.SearchData(Subject, Hard).ConfigureAwait(false);
            foreach (var data in datas)
            {
                FilteredData.Add(data);
            }
        }

So basically this gives a null exception error. I also tried giving the ObservableCollection as an argument for SecondPage(ObservableCollection x) and that did work, but because I had to make another ObservableCollection for it and copy from one to another it stopped being async and froze for a couple of seconds. So my question is how can I make this async?

1

There are 1 answers

0
ToolmakerSteve On

To avoid delay, build the new collection in a private variable. Then set the property to that variable:

// Constructor with parameter
public SomeClass(IList<Items> data)
{
    SetFilteredDataCopy(data);
}

public ObservableCollection<Items> FilteredData { get; set; }

private void SetFilteredDataCopy(IList<Items> src)
{
    var copy = new ObservableCollection<Items>();
    foreach (var item in src)
        copy.Add(item);
    FilteredData = copy;
    //MAYBE OnPropertyChanged(nameof(FilteredData));
}