簡體   English   中英

如何檢測用戶是否在Xamarin.Forms的TabbedPage上請求了選項卡更改

[英]How to detect if user requested a tab change on TabbedPage in Xamarin.Forms

我有一個使用TabbedPage的Xamarin.Forms應用程序,我們將其稱為T,T由3個ContentPage子項A,B和C組成。由於用戶可以編輯選項卡B上的某些數據,因此我想在離開前通知用戶標簽以允許他取消導航更改並先保存更改,或者放棄更改並離開。 到目前為止,我已經成功重寫了OnBackButtonPressed()方法和導航欄的后退按鈕(它將退出TabbedPage)。 但是我很快注意到,在選項卡之間切換時,我仍在丟失更改。 我想覆蓋對新選項卡的單擊,因此我可以先向用戶顯示離開對話框,然后跳過更改或繼續進行更改。 最好的方法是什么? 我目前僅在Android平台上工作,因此平台級別的解決方案也是可以接受的。

感謝您的建議和反饋:)

我認為沒有簡單的方法可以執行此操作,您可以對頁面使用OnDissappearing和OnAppearing,這很簡單。 但是我認為您使用的設計錯誤。 使用標簽頁可以使在頁面之間導航變得更加容易,如果要在更改標簽頁時通知用戶,那會很煩人。 如果您是我,我將在本地保存每個頁面的數據。 因此,當您返回頁面時,無論如何您將獲得數據。

因此,最后,我遵循了艾哈邁德(Ahmad)的建議,並實現了將數據保留在各個選項卡上,以便在切換選項卡時不會丟失數據。 (調用OnAppearing時,我不再從模型數據中刷新輸入字段)。

但是,為了知道ChildB頁面上是否有未保存的更改,我必須執行以下過程:

  1. 我在ChildB頁面上創建了HandleExit方法,該方法檢查字段中未保存的更改(輸入字段中至少有一個值與存儲的模型中的值不同),並且其中一個提示用戶未保存的更改(如果有的話) ),如果沒有更改,則彈出導航堆棧。

      private async Task HandleExit() { if(HasUnsavedChanges()) { var action = await DisplayAlert("Alert", "There are unsaved changes, do you want to discard them?", "Discard changes", "Cancel"); if(!action) { return; } } await Navigation.PopAsync(); } 
  2. 由於用戶可以通過兩種方式從“選項卡式”頁面返回(按設備上的“后退”按鈕或按導航欄中的“后退”按鈕),因此我必須:

    答:覆蓋我的ChildB頁面上的后退按鈕方法,因此它調用HandleExit方法。 但是由於需要在UI線程上調用Navigation.PopAsync(),因此我必須在UI線程上顯式執行該方法,如下所示:

     protected override bool OnBackButtonPressed() { Device.BeginInvokeOnMainThread(new Action(async () => { await HandleExit(); })); return true; } 

    B:由於無法攔截ContentPage上的導航欄后退按鈕,因此我不得不在平台級別(Android)上攔截事件,然后在必要時通過MessagingCenter將事件傳遞給ContentPage。 因此,首先需要攔截事件,方法是在子頁面之一中按下導航欄按鈕並通過MessagingCenter發送事件。 我們可以這樣做,但是在MainActivity.cs類中添加以下方法:

     public override bool OnOptionsItemSelected(IMenuItem item) { // check if the current item id // is equals to the back button id if (item.ItemId == 16908332) { // retrieve the current xamarin forms page instance var currentpage = Xamarin.Forms.Application.Current.MainPage.Navigation.NavigationStack.LastOrDefault(); var name = currentpage.GetType().Name; if(name == "ChildA" || name == "ChildB" || name == "ChildC") { MessagingCenter.Send("1", "NavigationBack"); return false; } } return base.OnOptionsItemSelected(item); } 

    現在,只要我們在其中一個子頁面(ChildA,ChildB,ChildC)中按導航欄的后退按鈕,都不會發生任何事情。 但是該按鈕將在其余頁面上像以前一樣工作。 對於解決方案的第二部分,我們需要處理來自MessagingCenter的消息,因此我們需要在ChildB頁面中對其進行訂閱。 我們可以在OnAppearing方法中訂閱消息主題,如下所示:

     MessagingCenter.Subscribe<string>(this, "NavigationBack", async (arg) => { await HandleExit(); }); 

    請小心取消訂閱OnDisappearing()中的主題,否則可能會發生奇怪的事情,因為即使從導航堆棧中彈出ContentPage,也將保留對ContentPage的引用。

  3. 現在我們已經在ChildB頁面中處理了兩個向后導航的請求,我們還需要在所有剩余的子頁面(ChildA,ChildC)中處理它們,因此他們將知道ChildB頁面中是否有未保存的更改,即使是當前未選擇。 因此,該解決方案再次經過比較,分別處理了設備的后退按鈕和導航欄的后退按鈕,但是當我們在其余頁面之一上時,我們首先想到了一種方法來檢查ChildB是否存在未保存的更改,因此我們再次編寫HandleExit方法,但是時間如下:

     private async Task HandleExit() { var root = (TabbedPage)this.Parent; var editPage = root.Children.Where(x => x.GetType() == typeof(ChildB)).FirstOrDefault(); if(editPage != null) { var casted = editPage as ChildB; if (casted.HasUnsavedChanges()) { var action = await DisplayAlert("Alert", "There are unsaved changes, do you want to discard them?", "Discard changes", "Cancel"); if (!action) { return; } } } await Navigation.PopAsync(); } 

    現在剩下的唯一事情就是處理其余子頁面中的兩個導航回事件。 它們的代碼與實際的ChildB頁面中的代碼相同。

    答:處理設備后退按鈕。

     protected override bool OnBackButtonPressed() { Device.BeginInvokeOnMainThread(new Action(async () => { await HandleExit(); })); return true; } 

    B:從MessagingCenter訂閱主題

     MessagingCenter.Subscribe<string>(this, "NavigationBack", async (arg) => { await HandleExit(); }); 

如果一切都正確完成,那么如果ChildB頁面上有未保存的更改,現在應該在任何子頁面上提示對話框。 我希望這會在將來對某人有所幫助:)

暫無
暫無

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

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