.NET MAUI Shell 瀏覽

Browse sample. 流覽範例

.NET 多平台應用程式 UI (.NET MAUI) Shell 包括 URI 型導覽體驗,而此導覽體驗使用路由來導覽至應用程式中的任何頁面,而不需要遵循設定的導覽階層。 此外,這也可讓您回溯導覽,而不需要造訪導覽堆疊上的所有頁面。

類別 Shell 會定義下列導覽相關屬性:

  • BackButtonBehavior (類型為 BackButtonBehavior):可定義上一頁按鈕行為的附加屬性。
  • CurrentItem (類型 ShellItem):目前選取的項目。
  • CurrentPage (類型為 Page):目前呈現的頁面。
  • CurrentState (類型為 ShellNavigationState):Shell 的目前導覽狀態。
  • Current (類型為 Shell):Application.Current.MainPage 的類型強制型轉別名。

BackButtonBehaviorCurrentItemCurrentState 屬性都以 BindableProperty 物件為後盾,也就是說,這些屬性可以是資料繫結的目標。

導覽是透過從 Shell 類別叫用 GoToAsync 方法來執行。 當導覽即將執行時, Navigating 會引發事件,並在 Navigated 巡覽完成時引發事件。

注意

流覽仍然可以使用 Navigation 屬性在Shell應用程式中的頁面之間執行。 如需詳細資訊,請參閱 執行無模式導覽

路由

導覽透過指定要導覽的目標 URI,以在殼層應用程式中執行。 導覽 URL 可以有三個元件:

  • 「路線」,定義作為殼層視覺階層中現有內容的路徑。
  • 「頁面」。 您可以將殼層視覺階層中不存在的頁面從殼層應用程式的任何位置推入至導覽堆疊。 例如,詳細資料頁面未定義於殼層視覺階層,但可以視需要推送至導覽堆疊。
  • 一或多個「查詢參數」。 查詢參數是可在導覽時傳遞至目的地頁面的參數。

導覽 URI 包括所有這三個元件時,結構為://route/page?queryParameters

註冊路線

路由可以透過其Route屬性定義於 、TabBarTabShellContent 物件上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,並在導覽動畫完成後傳回將會完成的 TaskShellNavigationState 物件是由 GoToAsync 方法,從 stringUri 所建構,且其 Location 屬性設為 stringUri 引數。

重要

從 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 物件的 CurrentItemCurrentState 屬性:

Screenshot of debugger.

在此範例中,CurrentItem 屬性屬於 FlyoutItem 類型,可顯示 FlyoutItem 物件的標題和路由。 同樣地, CurrentState 類型的 ShellNavigationState屬性會顯示Shell應用程式內所顯示路由的URI。

類別 TabStack 定義 型 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 而不是物件。

類別 ShellNavigating 定義事件,因為程式設計導覽或用戶互動而即將執行導覽時引發此事件。 隨附 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 發生導覽的類型。

重要

當事件引發時,NavigatingOnNavigating呼叫 方法。 同樣地, OnNavigated 當事件引發時 Navigated ,會呼叫 方法。 您可以在子類別中 Shell 覆寫這兩種方法,以攔截流覽要求。

例如,ShellNavigatedEventArgsShellNavigatingEventArgs 類別都擁有 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();
    }
// }

您可以根據使用者選擇攔截和完成或取消殼層流覽。 藉由覆寫OnNavigatingShell類別中的方法,以及在 物件上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 型程式設計導覽時,可以將原始資料當成字串型查詢參數來傳遞。 達成方式是在路線後面附加 ?,後面再接著查詢參數識別碼、= 和值:

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 查詢參數的形式傳遞。

傳遞多個使用物件型導覽數據

您可以使用指定自變數的多IDictionary<string, object>載來傳遞GoToAsync多個使用物件型導覽資料:

async void OnCollectionViewSelectionChanged(object sender, SelectionChangedEventArgs e)
{
    Animal animal = e.CurrentSelection.FirstOrDefault() as Animal;
    var navigationParameter = new Dictionary<string, object>
    {
        { "Bear", animal }
    };
    await Shell.Current.GoToAsync($"beardetails", navigationParameter);
}

這個範例會將 中目前選取的 CollectionView熊擷取為 Animal。 物件 Animal 會新增至 Dictionary 具有索引鍵 Bear的 。 然後,會執行巡覽至 beardetails 路由,並將 Dictionary 傳遞為導覽參數。

IDictionary<string, object> 自變數方式傳遞的任何數據都會保留在記憶體中,直到頁面從導覽堆疊移除為止,才會釋放。 這可能會有問題,如下列案例所示:

  1. Page1 巡覽至 Page2 使用 GoToAsync 方法,傳入名為 MyData的物件。 Page2 接著會 MyData 以查詢參數的形式接收 。
  2. Page2 巡覽至 Page3 使用 GoToAsync 方法,而不傳遞任何數據。
  3. Page3 使用 GoToAsync 方法向後巡覽。 Page2 然後再次接收 MyData 作為查詢參數。

雖然這在許多案例中是可取的,但如果不想要的話,您應該在頁面第一次收到自變數之後,使用方法清除 IDictionary<string, object> 自變數 Clear

傳遞單一使用物件型導覽數據

單一 GoToAsync 使用物件型導覽數據可以使用指定自變數的多 ShellNavigationQueryParameters 載來傳遞。 ShellNavigationQueryParameters物件適用於在瀏覽發生之後清除的單一使用導覽數據。 下列範例示範傳遞單一使用資料時流覽:

async void OnCollectionViewSelectionChanged(object sender, SelectionChangedEventArgs e)
{
    Animal animal = e.CurrentSelection.FirstOrDefault() as Animal;
    var navigationParameter = new ShellNavigationQueryParameters
    {
        { "Bear", animal }
    };
    await Shell.Current.GoToAsync($"beardetails", navigationParameter);
}

這個範例會擷取 中 CollectionView目前選取的熊,做為 Animal 加入至 ShellNavigationQueryParameters 物件的 。 然後,會執行巡覽至 beardetails 路由,並將 ShellNavigationQueryParameters 對象當做導覽參數傳遞。 瀏覽之後,對象中的數據 ShellNavigationQueryParameters 會清除。

接收導覽數據

接收導覽資料的方法有兩種:

  1. 類別,表示要巡覽的頁面,或頁面 的 BindingContext類別,可以使用 每個查詢參數的 QueryPropertyAttribute 裝飾。 如需詳細資訊,請參閱 使用查詢屬性處理導覽數據。
  2. 表示所瀏覽頁面的類別,或頁面 的 BindingContext類別,可以實 IQueryAttributable 作 介面。 如需詳細資訊,請參閱 使用單一方法處理導覽數據。

使用查詢屬性屬性處理導覽數據

您可以針對每個字串型查詢參數和物件型導覽參數,使用 QueryPropertyAttribute 來裝飾接收端類別,以接收導覽資料:

您可以針對每個以字串為基礎的查詢參數、以物件為基礎的瀏覽參數或 ShellNavigationQueryParameters 物件來裝飾接收類別QueryPropertyAttribute,以接收資料:

[QueryProperty(nameof(Bear), "Bear")]
public partial class BearDetailPage : ContentPage
{
    Animal bear;
    public Animal Bear
    {
        get => bear;
        set
        {
            bear = value;
            OnPropertyChanged();
        }
    }

    public BearDetailPage()
    {
        InitializeComponent();
        BindingContext = this;
    }
}

在此範例中,的第一個自變數QueryPropertyAttribute會指定將接收數據的屬性名稱,而第二個自變數則指定參數標識符。因此,QueryPropertyAttribute上述範例中的 會Bear指定 屬性將接收方法呼叫中GoToAsync導覽參數中Bear傳遞的數據。

注意

透過 QueryPropertyAttribute 接收的字串型查詢參數值會自動譯碼 URL。

使用單一方法處理導覽數據

藉由在接收類別上實 IQueryAttributable 作 介面,即可接收導覽數據。 介面 IQueryAttributable 指定實作類別必須實 ApplyQueryAttributes 作 方法。 這個方法具有 query 類型 IDictionary<string, object>為 的自變數,其中包含瀏覽期間傳遞的任何數據。 字典中的每個索引鍵都是查詢參數標識碼,其值會對應至代表數據的物件。 使用此方法的優點是,瀏覽數據可以使用單一方法來處理,當您有多個需要整體處理的瀏覽數據專案時,這非常有用。

下列範例顯示實作 介面的 IQueryAttributable 檢視模型類別:

public class MonkeyDetailViewModel : IQueryAttributable, INotifyPropertyChanged
{
    public Animal Monkey { get; private set; }

    public void ApplyQueryAttributes(IDictionary<string, object> query)
    {
        Monkey = query["Monkey"] as Animal;
        OnPropertyChanged("Monkey");
    }
    ...
}

在此範例中, ApplyQueryAttributes 方法會擷取對應至 Monkey 字典中 query 索引鍵的物件,該索引鍵會當做自變數傳遞至 GoToAsync 方法呼叫。

重要

透過介面接收的 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 路由,傳遞 elephantNameelephantLocation 作為查詢參數。

若要接收多個數據項,代表所瀏覽頁面的 類別,或頁面 的 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, object> query)
    {
        string name = HttpUtility.UrlDecode(query["name"].ToString());
        string location = HttpUtility.UrlDecode(query["location"].ToString());
        ...        
    }
    ...
}

在此範例中ApplyQueryAttributes,方法會從方法呼叫中的 GoToAsync URI 擷取 和 location 查詢參數的值name

注意

執行路由式導覽時,可以同時傳遞以字串為基礎的查詢參數和以對象為基礎的導覽參數。

上一頁按鈕行為

您可以將附加屬性設定 BackButtonBehaviorBackButtonBehavior 物件,以重新定義返回按鈕的外觀和行為。 類別 BackButtonBehavior 會定義下列屬性:

  • Command,屬於 ICommand 類型,會在按 [上一頁] 按鈕時執行。
  • CommandParameter,屬於 object 類型,這是傳遞至 Command 的參數。
  • IconOverride,屬於 ImageSource 類型,這是用於上一頁按鈕的圖示。
  • IsEnabled,屬於 boolean 類型,可指出是否啟用上一頁按鈕。 預設值是 true
  • IsVisibleboolean別為 的 ,表示 [上一頁] 按鈕是否可見。 預設值是 true
  • TextOverride,屬於 string 類型,這是用於上一頁按鈕的文字。

所有這些屬性都以 BindableProperty 物件為後盾,也就是說,這些屬性可以是資料繫結的目標。

下列程式代碼顯示重新定義返回按鈕外觀和行為的範例:

<ContentPage ...>    
    <Shell.BackButtonBehavior>
        <BackButtonBehavior Command="{Binding BackCommand}"
                            IconOverride="back.png" />   
    </Shell.BackButtonBehavior>
    ...
</ContentPage>

Command 屬性會設定要按 [上一頁] 按鈕時執行的 ICommand,而 IconOverride 屬性則會設定為用於 [上一頁] 按鈕的圖示:

Screenshot of a Shell back button icon override.