skip to Main Content

I am trying to Laod a List in async manner. But whenever I’m trying to load in async without Task.Wait() result not showing up even I’m raising OnPropertyChanged event as well (even though its an ObservableCollection) but no effect. If I’m loading it without async its working fine. I almost tried all the resources we have over stack overflow and on Microsoft forum but not able to get a solution.

I will share all the approaches I have tried up to now:

  1. I am using Loaded event of ListView and by using EventToCommandBehaviour I am converting that into AsyncCommand so I don’t have to use async void.

ViewModelCode:

public class FilteredPackagesPageViewModel : BaseViewModel
{
    private ObservableCollection<Datum> states;
    private string unitPriceWithDiscount;
    public string UnitPriceWithoutDiscount
    {
        get => unitPriceWithDiscount?? string.Empty;
        set { unitPriceWithDiscount = value; OnPropertyChanged(nameof(UnitPriceWithoutDiscount)); }
    }
    public ObservableCollection<Datum> States
    {
        get { return states; }
        set { SetProperty(ref states, value); }
    }

    public IAsyncCommand<int> FavouriteCommand { get; set; }
    public TaskLoaderNotifier<ObservableCollection<Datum>> Loader { get; }
    public IAsyncCommand<int> PackageDetailCommand { get; set; }
    public AsyncCommand LoadDetailCommand { get; set; }

    public FilteredPackagesPageViewModel()
    {
        FavouriteCommand = new AsyncCommand<int>((arg) => OnLoginClicked(arg));
        PackageDetailCommand = new AsyncCommand<int>((id) => PackageDetailCommandAction(id));
        LoadDetailCommand = new AsyncCommand(LoadPageDetail);
        //States = DataService.GetStates();
    }

    private async Task LoadPageDetail()
    {
        var test = await DataService.GetStates();
        var test1 = new ObservableCollection<Datum>();
        foreach(var state in test)
        {
            test1.Add(state);
        }
        States = test1;
        OnPropertyChanged(nameof(States));
    }
  1. I have tried to call this On Appearing as well in code behind but still no effect List Item source not updating at all.

  2. I have tried to Initailase an async constructor and called it from calling page still no effect.

  3. Working Code: (But not a good approach still UI being blocked)

public FilteredPackagesPageViewModel()
        {
            FavouriteCommand = new AsyncCommand<int>((arg) => OnLoginClicked(arg));
            PackageDetailCommand = new AsyncCommand<int>((id) => PackageDetailCommandAction(id));
            LoadTourPackages();
        }

        public async void LoadTourPackages()
        {
            Task.Run(async () => await LoadToursAsync()).Wait();
        }
        public async Task LoadToursAsync()
        {
            States = await DataService.GetStates();
        }
  1. I have tried to use BeginInvokeOnMainThread() to but that also didn’t work.

enter image description here

I’m currently using Visual Studio 2022, Xamarin forms – 5.0.0

If there is any better, solution please let me know. If there is some issue from my side please let me know that too.

View :

Note : There can’t be Binding issue as binding working perfectly whenever I’m running that code synchronously or not awaiting population of ObservalbleCollection.

 <Grid BackgroundColor="white">
            <Grid RowDefinitions="20,Auto" RowSpacing="15" Padding="8">
                <syncfusion:SfListView x:Name="listView" 
                                           ItemSize="310" 
                                           Grid.Row="1"
                                           ItemSpacing="0,10,0,5"
                                           ItemsSource="{Binding States,Mode=OneWay}">
                    <syncfusion:SfListView.ItemTemplate>
                        <DataTemplate>
                            <ViewCell>
                                <viewcell:PackageOverviewViewCell 
                                        ImageUrl="{Binding tourImage,Mode=OneWay}"
                                        Price="{Binding UnitPriceAfterDiscount,Mode=OneWay}" 
                                        Day="{Binding day,Mode=OneWay}"
                                        TourStartdate="{Binding tourStartdate,Mode=OneWay}"
                                        TourEndDate="{Binding tourEnddate,Mode=OneWay}"
                                        Night="{Binding night,Mode=OneWay}"
                                        Duration="{Binding duration,Mode=OneWay}"
                                        tourPackageId="{Binding tourPackageId,Mode=OneWay}"
                                        name="{Binding name,Mode=OneWay}"
                                        PackageDetailCommand="{Binding Path=BindingContext.PackageDetailCommand, Source={x:Reference filtered},Mode=TwoWay}"
                                        PackageDetailCommandParameter="{Binding tourPackageId}"
                                        FavoriteCommand="{Binding Path=BindingContext.FavouriteCommand, Source={x:Reference filtered},Mode=TwoWay}"/>
                            </ViewCell>
                        </DataTemplate>
                    </syncfusion:SfListView.ItemTemplate>
                    <syncfusion:SfListView.Behaviors>
                        <extensions:EventToCommandBehavior EventName="Loaded" Command="{Binding LoadDetailCommand}"/>
                    </syncfusion:SfListView.Behaviors>
                </syncfusion:SfListView>
            </Grid>
        </Grid>

2

Answers


  1. Chosen as BEST ANSWER

    Thanks @FreakyAli for your suggestion but for some reason Device.BeginInvokeOnMainThread not working but after doing some research I got the solution for that problem as MainThread.BeginInvokeOnMainThread:

    public void LoadTourPackages()
    {
        States = new ObservableCollection<Datum>();
        MainThread.BeginInvokeOnMainThread( async () =>
        {
            await LoadToursAsync();
            OnPropertyChanged(nameof(States));
        });
    }
    
    public async Task LoadToursAsync()
    {
           try
           {
               States = await DataService.GetStates();
           }
           catch (Exception ex)
           {
               await GeneralTask.ShowMessageAsync(this.GetType().Name, ex);
           }
    }
    

    we can find more details on below threads: https://learn.microsoft.com/en-us/xamarin/essentials/main-thread xamarin: difference between Device.BeginInvokeOnMainThread and MainThread.BeginInvokeOnMainThread and according to few resources Its better to use later MainThread.BeginInvokeOnMainThread over Device.BeginInvokeOnMainThread


  2. Your Async method should look like this:

     private async Task LoadPageDetail()
     {
        var test = await DataService.GetStates();
        var test1 = new ObservableCollection<Datum>();
        foreach(var state in test)
        {
            test1.Add(state);
        }
        Device.BeginInvokeOnMainThread(() => States = test1);
     }
    

    In async methods, you are usually on a background thread and they don’t make changes to the UI; to make UI changes always invoke the main thread in async methods.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search