Xamarin.Forms 殼層導覽
Xamarin.Forms Shell 包含 URI 型瀏覽體驗,其使用路由巡覽至應用程式中的任何頁面,而不需要遵循設定的瀏覽階層。 此外,這也可讓您回溯導覽,而不需要造訪導覽堆疊上的所有頁面。
類別 Shell
會定義下列導覽相關屬性:
BackButtonBehavior
(類型為BackButtonBehavior
):可定義上一頁按鈕行為的附加屬性。CurrentItem
(類型ShellItem
):目前選取的項目。CurrentPage
(類型為Page
):目前呈現的頁面。CurrentState
(類型為ShellNavigationState
):Shell
的目前導覽狀態。Current
(類型為Shell
):Application.Current.MainPage
的類型強制型轉別名。
BackButtonBehavior
、CurrentItem
和 CurrentState
屬性都以 BindableProperty
物件為後盾,也就是說,這些屬性可以是資料繫結的目標。
導覽是透過從 Shell
類別叫用 GoToAsync
方法來執行。 當導覽即將執行時, Navigating
會引發事件,並在 Navigated
巡覽完成時引發事件。
注意
流覽仍然可以使用 Navigation 屬性在 Shell 應用程式中的頁面之間執行。 如需詳細資訊,請參閱階層式導覽。
路由
導覽是在 Shell 應用程式中,透過指定要導覽的 URI 執行的。 導覽 URL 可以有三個元件:
- 「路線」,定義作為殼層視覺階層中現有內容的路徑。
- 「頁面」。 Shell 視覺階層中不存在的頁面可以從 Shell 應用程式中的任何位置推送到導覽堆疊上。 例如,詳細資料頁面未定義於殼層視覺階層,但可以視需要推送至導覽堆疊。
- 一或多個「查詢參數」。 查詢參數是可在導覽時傳遞至目的地頁面的參數。
導覽 URI 包括所有這三個元件時,結構為://route/page?queryParameters
註冊路線
路由可以透過其Route
屬性定義於 、TabBar
、 Tab
和 ShellContent
物件上FlyoutItem
:
<Shell ...>
<FlyoutItem ...
Route="animals">
<Tab ...
Route="domestic">
<ShellContent ...
Route="cats" />
<ShellContent ...
Route="dogs" />
</Tab>
<ShellContent ...
Route="monkeys" />
<ShellContent ...
Route="elephants" />
<ShellContent ...
Route="bears" />
</FlyoutItem>
<ShellContent ...
Route="about" />
...
</Shell>
注意
Shell 階層中的所有項目有與其相關聯的路由。 如果您未設定路由,則會在運行時間產生一個路由。 但是,不保證產生的路由在不同的應用程式工作階段之間都一致。
上述範例會建立下列路由階層,可用於程式設計導覽:
animals
domestic
cats
dogs
monkeys
elephants
bears
about
若要導覽至 dogs
路由的 ShellContent
物件,絕對路由 URI 為 //animals/domestic/dogs
。 同樣地,若要導覽至 about
路由的 ShellContent
物件,絕對路由 URI 為 //about
。
警告
如果偵測到重複的路由,將會在應用程式啟動時擲回 ArgumentException
。 如果階層中有兩個以上的同層級路由共用一個路由名稱,也會擲回此例外狀況。
註冊詳細數據頁面路由
在 Shell
子類別建構函式中,或在叫用路線之前執行的任何其他位置中,可以針對任何未在殼層視覺階層中呈現的詳細資料頁面,明確地註冊其他路線。 這會使用 Routing.RegisterRoute
方法來完成:
Routing.RegisterRoute("monkeydetails", typeof(MonkeyDetailPage));
Routing.RegisterRoute("beardetails", typeof(BearDetailPage));
Routing.RegisterRoute("catdetails", typeof(CatDetailPage));
Routing.RegisterRoute("dogdetails", typeof(DogDetailPage));
Routing.RegisterRoute("elephantdetails", typeof(ElephantDetailPage));
本範例會將子類別中 Shell
未定義的詳細數據頁面註冊為路由。 然後,您可以從應用程式內的任何位置瀏覽這些詳細數據頁面,使用 URI 型導覽。 這類頁面的路由稱為全域路由。
警告
如果 Routing.RegisterRoute
方法嘗試向兩個以上的不同類型註冊相同的路由,則會擲回 ArgumentException
。
或者,如果需要,可以在不同的路由階層註冊頁面:
Routing.RegisterRoute("monkeys/details", typeof(MonkeyDetailPage));
Routing.RegisterRoute("bears/details", typeof(BearDetailPage));
Routing.RegisterRoute("cats/details", typeof(CatDetailPage));
Routing.RegisterRoute("dogs/details", typeof(DogDetailPage));
Routing.RegisterRoute("elephants/details", typeof(ElephantDetailPage));
此範例會啟用內容相關式頁面導覽,其中,從 monkeys
路由的頁面導覽至 details
路由會顯示 MonkeyDetailPage
。 同樣地,從 elephants
路由的頁面導覽至 details
路由會顯示 ElephantDetailPage
。 如需詳細資訊,請參閱 內容導覽。
注意
如果需要,已使用 Routing.RegisterRoute
方法註冊其路由的頁面可使用 Routing.UnRegisterRoute
方法取消註冊。
執行導覽
若要執行導覽,必須先取得 Shell
子類別的參考。 將 App.Current.MainPage
屬性轉換為 Shell
物件,或透 過Shell.Current
屬性可以取得這個參考。 接著,可以對 Shell
物件呼叫 GoToAsync
方法來執行導覽。 此方法會導覽至 ShellNavigationState
,並在導覽動畫完成後傳回將會完成的 Task
。 ShellNavigationState
物件是由 GoToAsync
方法,從 string
或 Uri
所建構,且其 Location
屬性設為 string
或 Uri
引數。
重要
從 Shell 視覺階層導覽至路由時,不會建立導覽堆疊。 但是,導覽至不在 Shell 視覺階層中的頁面時,則會建立導覽堆疊。
物件的目前導覽狀態 Shell
可以透過 Shell.Current.CurrentState
屬性擷取,其中包含屬性中顯示路由的 Location
URI。
絕對路由
將有效的絕對 URI 指定為 GoToAsync
方法的引數可執行導覽:
await Shell.Current.GoToAsync("//animals/monkeys");
此範例會導覽至 monkeys
路由的頁面,並在 ShellContent
物件上定義路由。 表示 monkeys
路由的 ShellContent
物件為 FlyoutItem
物件子系,其路由是 animals
。
相對路由
將有效的相對 URI 指定為 GoToAsync
方法的引數也可以執行導覽。 路由系統將會嘗試比對 ShellContent
物件的 URI。 因此,如果應用程式中的所有路由都是唯一的,則只能藉由將唯一路由名稱指定為相對 URI 來執行導覽。
支援下列相對路由格式:
格式 | 描述 |
---|---|
route | 路由階層會從目前的位置向上搜尋指定的路由。 比對頁面將會推送至流覽堆疊。 |
/route | 路由階層會從指定的路由搜尋,從目前位置向下搜尋。 比對頁面將會推送至流覽堆疊。 |
//route | 路由階層會從目前的位置向上搜尋指定的路由。 比對頁面將會取代瀏覽堆疊。 |
///route | 路由階層會從目前的位置向下搜尋指定的路由。 比對頁面將會取代瀏覽堆疊。 |
下列範例會巡覽至路由的頁面 monkeydetails
:
await Shell.Current.GoToAsync("monkeydetails");
在此範例中,路由 monkeyDetails
會搜尋階層,直到找到相符頁面為止。 找到頁面時,會推送至瀏覽堆疊。
關聯式導覽
相對路由可啟用關聯式導覽。 例如,請考慮下列路由階層:
monkeys
details
bears
details
顯示 monkeys
路由已註冊的頁面時,導覽至 details
路由將會顯示 monkeys/details
路由已註冊的頁面。 同樣地,顯示 bears
路由已註冊的頁面時,導覽至 details
路由將會顯示 bears/details
路由已註冊的頁面。 如需如何在此範例中註冊路由的相關資訊,請參閱註冊頁面路由。
回溯導覽
您可以指定 ".." 作為 GoToAsync
方法的引數,以執行回溯導覽:
await Shell.Current.GoToAsync("..");
使用 “..” 的向後流覽也可以與路由結合:
await Shell.Current.GoToAsync("../route");
在此範例中,會執行向後流覽,然後流覽至指定的路由。
重要
只有在向後巡覽將您放在路由階層中目前位置巡覽至指定的路由時,才能往回巡覽至指定的路由。
同樣地,可以多次向後巡覽,然後巡覽至指定的路由:
await Shell.Current.GoToAsync("../../route");
在此範例中,會執行兩次向後流覽,然後巡覽至指定的路由。
此外,在向後巡覽時,可以透過查詢屬性傳遞數據:
await Shell.Current.GoToAsync($"..?parameterToPassBack={parameterValueToPassBack}");
在此範例中,會執行向後流覽,並將查詢參數值傳遞至上一頁的查詢參數。
注意
查詢參數可以附加至任何向後流覽要求。
如需在巡覽時傳遞數據的詳細資訊,請參閱 傳遞數據。
無效的路由
下列路由格式無效:
格式 | 說明 |
---|---|
//page 或 ///page | 全域路由目前不得為導覽堆疊上的唯一頁面。 因此,不支援以絕對路由傳送至全域路由。 |
使用這些路由格式會導致 Exception
擲回 。
警告
嘗試導覽至不存在的路由會導致擲回 ArgumentException
例外狀況。
為導覽偵錯
部分 Shell 類別是以 DebuggerDisplayAttribute
裝飾,以指定偵錯工具如何顯示類別或欄位。 顯示與導覽要求相關的資料有助於為導覽要求偵錯。 例如,下列螢幕擷取畫面顯示 Shell.Current
物件的 CurrentItem
和 CurrentState
屬性:
在此範例中,CurrentItem
屬性屬於 FlyoutItem
類型,可顯示 FlyoutItem
物件的標題和路由。 同樣地,CurrentState
屬性屬於 ShellNavigationState
類型,可顯示 Shell 應用程式內顯示之路由的 URI。
流覽堆疊
類別 Tab
會 Stack
定義 型 IReadOnlyList<Page>
別 的屬性,代表 中的 Tab
目前流覽堆疊。 此類別也會提供下列可覆寫的導覽方法:
GetNavigationStack
會傳IReadOnlyList<Page>
回 ,目前流覽堆疊。OnInsertPageBefore
,會在呼叫INavigation.InsertPageBefore
時呼叫。OnPopAsync
,傳回Task<Page>
,會在呼叫INavigation.PopAsync
時呼叫。OnPopToRootAsync
,傳回Task
,會在呼叫INavigation.OnPopToRootAsync
時呼叫。OnPushAsync
,傳回Task
,會在呼叫INavigation.PushAsync
時呼叫。OnRemovePage
,會在呼叫INavigation.RemovePage
時呼叫。
下列範例示範如何覆寫 OnRemovePage
方法:
public class MyTab : Tab
{
protected override void OnRemovePage(Page page)
{
base.OnRemovePage(page);
// Custom logic
}
}
在此範例中, MyTab
應該在Shell視覺階層中取用物件, Tab
而不是物件。
導覽事件
類別 Shell
會 Navigating
定義事件,因為程式設計導覽或用戶互動而即將執行導覽時引發此事件。 隨附 Navigating
事件的 ShellNavigatingEventArgs
物件會提供下列屬性:
屬性 | 類型 | 描述 |
---|---|---|
Current |
ShellNavigationState |
目前頁面的 URI。 |
Source |
ShellNavigationSource |
發生導覽的類型。 |
Target |
ShellNavigationState |
代表導覽目的地的 URI。 |
CanCancel |
bool |
指出它是否可以取消導覽的值。 |
Cancelled |
bool |
值,指出流覽是否已取消。 |
此外,類別 ShellNavigatingEventArgs
提供 Cancel
可用來取消流覽的方法,以及 GetDeferral
傳回 ShellNavigatingDeferral
可用來完成流覽之令牌的方法。 如需瀏覽延遲的詳細資訊,請參閱 瀏覽延遲。
類別 Shell
也會定義 Navigated
事件,此事件會在流覽完成時引發。 隨附 Navigated
事件的 ShellNavigatedEventArgs
物件會提供下列屬性:
屬性 | 類型 | 描述 |
---|---|---|
Current |
ShellNavigationState |
目前頁面的 URI。 |
Previous |
ShellNavigationState |
上一頁的 URI。 |
Source |
ShellNavigationSource |
發生導覽的類型。 |
重要
當事件引發時,Navigating
會OnNavigating
呼叫 方法。 同樣地, OnNavigated
當事件引發時 Navigated
,會呼叫 方法。 您可以在子類別中 Shell
覆寫這兩種方法,以攔截流覽要求。
例如,ShellNavigatedEventArgs
及 ShellNavigatingEventArgs
類別都擁有 ShellNavigationSource
類型的 Source
屬性。 此列舉提供下列值:
Unknown
Push
Pop
PopToRoot
Insert
Remove
ShellItemChanged
ShellSectionChanged
ShellContentChanged
因此,可以在覆寫中 OnNavigating
攔截流覽,而且可以根據流覽來源執行動作。 例如,下列的程式碼會示範如何在未儲存頁面上的資料時,取消向後導覽:
protected override void OnNavigating(ShellNavigatingEventArgs args)
{
base.OnNavigating(args);
// Cancel any back navigation.
if (args.Source == ShellNavigationSource.Pop)
{
args.Cancel();
}
// }
瀏覽延遲
您可以根據使用者選擇攔截和完成或取消殼層流覽。 藉由覆寫OnNavigating
子Shell
類別中的方法,以及在 物件上ShellNavigatingEventArgs
呼叫 GetDeferral
方法,即可達成此目的。 這個方法會傳 ShellNavigatingDeferral
回具有 Complete
方法的令牌,可用來完成流覽要求:
public MyShell : Shell
{
// ...
protected override async void OnNavigating(ShellNavigatingEventArgs args)
{
base.OnNavigating(args);
ShellNavigatingDeferral token = args.GetDeferral();
var result = await DisplayActionSheet("Navigate?", "Cancel", "Yes", "No");
if (result != "Yes")
{
args.Cancel();
}
token.Complete();
}
}
在此範例中,會顯示一個動作表,邀請使用者完成流覽要求,或取消它。 藉由叫用 Cancel
物件上的 ShellNavigatingEventArgs
方法來取消流覽。 巡覽是藉由 Complete
叫用 物件上 ShellNavigatingDeferral
方法所擷取 GetDeferral
之令牌上的 ShellNavigatingEventArgs
方法來完成。
警告
如果使用者嘗試在有擱置的瀏覽延遲時流覽,方法 GoToAsync
會擲回 InvalidOperationException
。
傳遞資料
執行以 URI 為基礎的程式設計導覽時,數據可以當做查詢參數傳遞。 這是藉由在路由後面附加 ?
,後面接著查詢參數標識碼 和 =
值來達成此目的。 例如,當使用者在 ElephantsPage
上選取大象時,範例應用程式中會執行下列程式碼:
async void OnCollectionViewSelectionChanged(object sender, SelectionChangedEventArgs e)
{
string elephantName = (e.CurrentSelection.FirstOrDefault() as Animal).Name;
await Shell.Current.GoToAsync($"elephantdetails?name={elephantName}");
}
此程式碼範例會在 CollectionView
中擷取目前選取的大象,並導覽至 elephantdetails
路由,將 elephantName
作為查詢參數傳遞。
接收導覽資料的方法有兩種:
- 類別,表示要巡覽的頁面,或頁面 的
BindingContext
類別,可以使用 每個查詢參數的QueryPropertyAttribute
裝飾。 如需詳細資訊,請參閱 使用查詢屬性處理導覽數據。 - 表示所瀏覽頁面的類別,或頁面 的
BindingContext
類別,可以實IQueryAttributable
作 介面。 如需詳細資訊,請參閱 使用單一方法處理導覽數據。
使用查詢屬性屬性處理導覽數據
使用每個查詢參數的 裝飾接收類別 QueryPropertyAttribute
,即可接收導覽數據:
[QueryProperty(nameof(Name), "name")]
public partial class ElephantDetailPage : ContentPage
{
public string Name
{
set
{
LoadAnimal(value);
}
}
...
void LoadAnimal(string name)
{
try
{
Animal animal = ElephantData.Elephants.FirstOrDefault(a => a.Name == name);
BindingContext = animal;
}
catch (Exception)
{
Console.WriteLine("Failed to load animal.");
}
}
}
的第一個自變數QueryPropertyAttribute
會指定將接收數據的屬性名稱,而第二個自變數則指定查詢參數標識符。因此,QueryPropertyAttribute
上述範例中的 會Name
指定 屬性會從方法呼叫中的 GoToAsync
URI 接收查詢參數中name
傳遞的數據。 屬性 setter 會Name
呼叫 LoadAnimal
方法來擷name
取 Animal
的物件,並將它設定為BindingContext
頁面的 。
注意
透過 QueryPropertyAttribute
接收的查詢參數值會自動譯碼 URL。
使用單一方法處理導覽數據
藉由在接收類別上實 IQueryAttributable
作 介面,即可接收導覽數據。 介面 IQueryAttributable
指定實作類別必須實 ApplyQueryAttributes
作 方法。 這個方法具有 query
類型 IDictionary<string, string>
為 的自變數,其中包含瀏覽期間傳遞的任何數據。 字典中的每個索引鍵都是查詢參數標識碼,其值是查詢參數值。 使用此方法的優點是,瀏覽數據可以使用單一方法來處理,當您有多個需要整體處理的瀏覽數據專案時,這非常有用。
下列範例顯示實作 介面的 IQueryAttributable
檢視模型類別:
public class MonkeyDetailViewModel : IQueryAttributable, INotifyPropertyChanged
{
public Animal Monkey { get; private set; }
public void ApplyQueryAttributes(IDictionary<string, string> query)
{
// The query parameter requires URL decoding.
string name = HttpUtility.UrlDecode(query["name"]);
LoadAnimal(name);
}
void LoadAnimal(string name)
{
try
{
Monkey = MonkeyData.Monkeys.FirstOrDefault(a => a.Name == name);
OnPropertyChanged("Monkey");
}
catch (Exception)
{
Console.WriteLine("Failed to load animal.");
}
}
...
}
在此範例中,ApplyQueryAttributes
方法會從方法呼叫中的 GoToAsync
URI 擷取查詢參數的值name
。 然後, LoadAnimal
會呼叫 方法來擷取 Animal
物件,其中其設定為系結數據之 Monkey
屬性的值。
重要
透過介面接收的 IQueryAttributable
查詢參數值不會自動譯碼 URL。
傳遞和處理多個查詢參數
使用連接 &
多個查詢參數,即可傳遞多個查詢參數。 例如,下列程式代碼會傳遞兩個數據項:
async void OnCollectionViewSelectionChanged(object sender, SelectionChangedEventArgs e)
{
string elephantName = (e.CurrentSelection.FirstOrDefault() as Animal).Name;
string elephantLocation = (e.CurrentSelection.FirstOrDefault() as Animal).Location;
await Shell.Current.GoToAsync($"elephantdetails?name={elephantName}&location={elephantLocation}");
}
此程式代碼範例會擷取 中目前選取的 CollectionView
大象,並巡覽至 elephantdetails
路由,傳遞 elephantName
和 elephantLocation
作為查詢參數。
若要接收多個數據項,代表所巡覽頁面的 類別,或頁面 的 BindingContext
類別,可以使用 每個查詢參數的 QueryPropertyAttribute
裝飾:
[QueryProperty(nameof(Name), "name")]
[QueryProperty(nameof(Location), "location")]
public partial class ElephantDetailPage : ContentPage
{
public string Name
{
set
{
// Custom logic
}
}
public string Location
{
set
{
// Custom logic
}
}
...
}
在這裡範例中,類別會針對 QueryPropertyAttribute
每個查詢參數使用 裝飾 。 第一個 QueryPropertyAttribute
指定 Name
屬性將接收傳入 name
查詢參數的數據,而第二個 QueryPropertyAttribute
指定 Location
屬性將接收傳入 location
查詢參數的數據。 在這兩種情況下,查詢參數值都會在方法呼叫的 GoToAsync
URI 中指定。
或者,藉由在代表所瀏覽頁面的 類別上實 IQueryAttributable
作 介面,或頁面 的 BindingContext
類別,即可處理導覽數據:
public class ElephantDetailViewModel : IQueryAttributable, INotifyPropertyChanged
{
public Animal Elephant { get; private set; }
public void ApplyQueryAttributes(IDictionary<string, string> query)
{
string name = HttpUtility.UrlDecode(query["name"]);
string location = HttpUtility.UrlDecode(query["location"]);
...
}
...
}
在此範例中ApplyQueryAttributes
,方法會從方法呼叫中的 GoToAsync
URI 擷取 和 location
查詢參數的值name
。
上一頁按鈕行為
您可以將附加屬性設定 BackButtonBehavior
為 BackButtonBehavior
物件,以重新定義返回按鈕的外觀和行為。 類別 BackButtonBehavior
會定義下列屬性:
Command
,屬於ICommand
類型,會在按 [上一頁] 按鈕時執行。CommandParameter
,屬於object
類型,這是傳遞至Command
的參數。IconOverride
,屬於ImageSource
類型,這是用於上一頁按鈕的圖示。IsEnabled
,屬於boolean
類型,可指出是否啟用上一頁按鈕。 預設值是true
。TextOverride
,屬於string
類型,這是用於上一頁按鈕的文字。
所有這些屬性都以 BindableProperty
物件為後盾,也就是說,這些屬性可以是資料繫結的目標。
下列程式代碼顯示重新定義返回按鈕外觀和行為的範例:
<ContentPage ...>
<Shell.BackButtonBehavior>
<BackButtonBehavior Command="{Binding BackCommand}"
IconOverride="back.png" />
</Shell.BackButtonBehavior>
...
</ContentPage>
對等的 C# 程式碼為:
Shell.SetBackButtonBehavior(this, new BackButtonBehavior
{
Command = new Command(() =>
{
...
}),
IconOverride = "back.png"
});
Command
屬性會設定要按 [上一頁] 按鈕時執行的 ICommand
,而 IconOverride
屬性則會設定為用於 [上一頁] 按鈕的圖示: