簡體   English   中英

如何從 MVVM 中的異步 SQLite 數據源填充選擇器選項

[英]How to populate picker options from async SQLite datasource in MVVM

我需要有一個選擇器(或其他一些控件)在 MVVM 實現中提供用戶選項(基於Xamarin.Forms MVVM:如何使用 SQLite DB(C# — Xaml) )但看不到如何將異步選項加載到綁定中.

我有一個名為 viewings 的記錄類型的詳細信息編輯屏幕,其中直接輸入字段綁定到 ViewingDetailViewingModel。 ...BindingContext = new ViewingDetailViewModel(viewModel?? new ViewingViewModel(), viewingStore, pageService, viewingsRestStore); 在 XAML 的代碼隱藏中。

除了綁定到 Viewing 的 model 屬性的相對直接的條目單元格(如果我沒有使用正確的命名法,請原諒我,這是新的),我想讓其中一個字段使用選擇器來保存客戶的 ID基於用戶選擇客戶端。

所以我猜,由於 SQLite clientstore 的 get 返回是異步的,我應該有一個從某處觸發的命令,我理想地想象來自代碼隱藏的 onAppearing 方法,該方法更新了選擇器綁定到的客戶端 IList - 盡管我遇到了兩個這里的問題:

  1. 我不知道如何從那里觸發命令,所以我現在嘗試從視圖模型的構造函數的末尾執行它。
  2. 這樣做(雖然我不認為是因為那個),應用程序在嘗試將第一個客戶端添加到列表時崩潰,同時循環使用“...System.NullReferenceException:Object 引用未設置為實例object 在 AgentApp.ViewingDetailViewModel.LoadClients (System.Collections.Generic.IList`1[T] Clients)...”

所以在我的 XAML 我有

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

在視圖的代碼隱藏中:

 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);
        }

然后是 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);
            }
        }
    }

我是一個完全的新手,來自簡單的 php 背景,所有這些 OOP 的復雜性讓我有些困惑; 我可能犯了很多非常愚蠢的錯誤——也許我把事情搞得太復雜了——我希望有人能給我指出一個加載選擇器異步數據的簡單好方法。

提前致謝!

首先,您似乎沒有在任何地方實例化Clients屬性。 這就是為什么你會得到一個NullReferenceException

其次,如果您要向IList添加項目,則沒有人會收到有關向該集合中添加了某些內容的通知。 相反,您應該考慮將類型更改為ObservableCollection ,這有助於您這樣做。

因此,將Clients的代碼更改為:

public ObservableCollection<ClientViewModel> Clients { get; }
    = new ObservableCollection<ClientViewModel>();

當您嘗試填充它時,這可能會解決 UI 未更新和Clients為 null 的大部分問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM