Forum Xamarin.Forms

Announcement:

The Xamarin Forums have officially moved to the new Microsoft Q&A experience. Microsoft Q&A is the home for technical questions and answers at across all products at Microsoft now including Xamarin!

To create new threads and ask questions head over to Microsoft Q&A for .NET and get involved today.

Simple way to async load options into a picker?

I have a detail edit screen for a record type called viewings, with straight forward entry fields bound to a ViewingDetailViewingModel.
`...BindingContext = new ViewingDetailViewModel(viewModel ?? new ViewingViewModel(), viewingStore, pageService, viewingsRestStore);' in the XAML's codebehind.

In addition to the relatively straight forward entry cells binding to properties of the Viewing's model (forgive me if I don't use the correct nomenclature, new to this), I want to have one of the fields use a picker to save a Client's id based on the user selecting a client.

So I'm guessing, as the SQLite clientstore's get return is async, I should have a command that is triggered from somewhere, I imagine ideally from the codebehind's onAppearing method that updates an IList of Clients that the picker binds to - though I encounter two problems here:
1. I can't figure out how to trigger the command from there so I'm trying to execute it from the end of the viewmodel's constructor for now.
2. Doing that, the app crashes once it tries to add the first client to the list with "...System.NullReferenceException: Object reference not set to an instance of an object
at AgentApp.ViewingDetailViewModel.LoadClients (System.Collections.Generic.IList`1[T] Clients)..."

so in my XAML I have

<EntryCell Label="Agt_description" Text="{Binding Path=Viewing.Agt_description}" />
                <ViewCell >
                    <Picker Title="Select a client" ItemsSource="{Binding Clients}"  ItemDisplayBinding ="{Binding Agt_FirstName}" />
                </ViewCell>

In the view's code-behind:

 public ViewingDetailPage(ViewingViewModel viewModel)
        {
            Console.WriteLine("ViewingDetailPage()");
            InitializeComponent();
            var viewingStore = new SQLiteViewingStore(DependencyService.Get<ISQLiteDb>());
            var viewingsRestStore = new RESTViewingStore();
            var pageService = new PageService();
            bool isNewViewing = (viewModel.Agt_name == " ");
            Title = (isNewViewing) ? "New Viewing" : "Edit Viewing";
            if (isNewViewing)
            {
                var app = Application.Current as App;
                viewModel.Agt_ac = app.AgencyIdInt;
                viewModel.Agt_b = app.BranchIDInt;
                viewModel.Agt_at = app.AccountIdInt;
            }
            BindingContext = new ViewingDetailViewModel(viewModel ?? new ViewingViewModel(), viewingStore, pageService, viewingsRestStore);
        }

and then the ViewingDetailViewModel:

class ViewingDetailViewModel : BaseViewModel
    {
        private readonly IViewingStore _viewingStore;
        private readonly IViewingStore _viewingRestStore;
        private readonly IPageService _pageService;
        public Viewing Viewing { get; private set; }
        public ICommand SaveCommand { get; private set; }

        //trying
        public ICommand LoadClientsCommand { get; private set; }
        private IClientStore _clientStore;
        private IList<ClientViewModel> _clients;
        public IList<ClientViewModel> Clients
        {
            get { return _clients; }
            set { SetProperty(ref _clients, value); }   // from BaseViewModel, implements OnPropertyChanged()
        }
        //

        public ViewingDetailViewModel(ViewingViewModel viewModel, IViewingStore viewingStore, IPageService pageService, IViewingStore viewingRestStore)
        {
                        var app = Application.Current as App;
            if (viewModel == null)
                throw new ArgumentNullException(nameof(viewModel));

            _pageService = pageService;
            _viewingStore = viewingStore;
            _viewingRestStore = viewingRestStore;

            LoadClientsCommand = new Command(async () => await LoadClients());

            SaveCommand = new Command(async () =>
            {
                                var SaveDBTask = SaveDB("primary");
                var SaveRESTTask = SaveRest();
                                var saveTasks = new List<Task> { SaveDBTask, SaveRESTTask };
                while (saveTasks.Count > 0)
                {
                    Task finishedTask = await Task.WhenAny(saveTasks);
                    if (finishedTask == SaveDBTask)
                    {
                        Console.WriteLine("SaveDBTask finished:" + SaveDBTask);
                    }
                    else if (finishedTask == SaveRESTTask)
                    {
                        Console.WriteLine("SaveRESTTask finished, do db update:" + SaveRESTTask);
                        await SaveDB("secondary update");
                    }
                    saveTasks.Remove(finishedTask);
                }
                Console.WriteLine("Save tasks complete.");
            });

            Viewing = new Viewing
            {
                Id = viewModel.Id,
                Agt_description = viewModel.Agt_description,
                Agt_name = viewModel.Agt_name,
                RemoteId = viewModel.RemoteId,
                Agt_ac = viewModel.Agt_ac > 0 ? viewModel.Agt_ac : app.AgencyIdInt,
                Agt_b = viewModel.Agt_b > 0 ? viewModel.Agt_b : app.BranchIDInt,
                Agt_at = viewModel.Agt_at > 0 ? viewModel.Agt_at : app.AccountIdInt,
                Agt_pr = viewModel.Agt_pr,
                Agt_datetime_scheduled = viewModel.Agt_datetime_scheduled,
                Agt_datetime_start = viewModel.Agt_datetime_start,
                Agt_datetime_end = viewModel.Agt_datetime_end,
                StatusLocal = viewModel.StatusLocal,
                CreatedLocal = viewModel.CreatedLocal,
                ModifiedLocal = viewModel.ModifiedLocal,
                CreatedRemote = viewModel.CreatedRemote,
                ModifiedRemote = viewModel.ModifiedRemote,
                LastSync = viewModel.LastSync,
            };

            LoadClientsCommand.Execute(null);//doubt this is a good idea
        }

        async Task LoadClients()
        {
            _clientStore = new SQLiteClientStore(DependencyService.Get<ISQLiteDb>());
            var clients = await _clientStore.GetClientsAsync();
            foreach (var client in clients)
            {
                Console.WriteLine("for client:" + client.Agt_FirstName);
                Clients.Add(new ClientViewModel(client)); //crashes around here
            }
        }

        async Task SaveDB(string savetype)
        {
            if (String.IsNullOrWhiteSpace(Viewing.Agt_name))
            {
                await _pageService.DisplayAlert("Error", "Please enter the name.", "OK");
                return;
            }
            if (Viewing.Id == 0)
            {
                await _viewingStore.AddViewing(Viewing);
                MessagingCenter.Send(this, Events.ViewingAdded, Viewing);
            }
            else
            {
                await _viewingStore.UpdateViewing(Viewing);
                MessagingCenter.Send(this, Events.ViewingUpdated, Viewing);
            }
            if (savetype == "primary")
                await _pageService.PopAsync();
        }

        async Task SaveRest()
        {
            if (String.IsNullOrWhiteSpace(Viewing.Agt_name))
            {
                Console.WriteLine("REST save could not, invalid name.");
                return;
            }
            if (Viewing.RemoteId == 0)
            {
                await _viewingRestStore.AddViewing(Viewing);
            }
            else
            {
                await _viewingRestStore.UpdateViewing(Viewing);
                MessagingCenter.Send(this, Events.ViewingUpdated, Viewing);
            }
        }
    }

I'm a complete newb coming from a simple php background and all this OOP complexity is confusing me somewhat; I'm probably making a whole lot of really stupid mistakes - and maybe I'm overcomplicating things - I hope someone can point me at a good simple way of loading the picker's async data.

Thanks in advance!

Tagged:

Best Answers

Answers

  • Andre_S_CAndre_S_C Member ✭✭

    @ColeX thanks, I have all that working, except that the picker remains empty of options

Sign In or Register to comment.