參考鏈接:
Xamarin.Forms頁面代表跨平台的移動應用程序屏幕。
下文描述的所有頁面類型均來自Xamarin.Forms Page類。 這些視覺元素占據了整個或大部分屏幕。 Page對象代表iOS中的ViewController和Universal Windows Platform中的Page。 在Android上,每個頁面都像一個Activity一樣占據屏幕,但是Xamarin.Forms頁面不是Activity對象。

ContentPage
ContentPage是最簡單,最常見的頁面類型,將Content屬性設置為單個View對象,該對象通常是Layout,例如StackLayout,Grid或ScrollView。
MasterDetailPage
MasterDetailPage有兩個ContentPage組成:Master母版頁面和Detail詳情頁面; 將Master屬性設置為通常顯示列表或菜單的頁面。 將Detail屬性設置為顯示從母版頁中選擇的項目的頁面,IsPresented屬性控制母版頁或詳細頁是否可見。

項目列表的位置在每個平台上都是相同的,選擇其中一個項目將導航到相應的詳細信息頁面。
此外,母版頁還具有一個導航欄,其中包含一個按鈕,可用於導航到活動詳細信息頁面:
- 在iOS上,導航欄位於頁面頂部,並且具有一個導航到詳細信息頁面的按鈕。此外,可以通過向左滑動母版頁來導航到活動詳細信息頁。
- 在Android上,導航欄位於頁面頂部,並顯示標題,圖標和導航到詳細信息頁面的按鈕。該圖標在[Activity]屬性中定義,該屬性用於裝飾特定於Android平台的項目中的MainActivity類。此外,可以通過以下方式導航到活動的詳細信息頁面:向左滑動母版頁,點擊屏幕最右側的詳細信息頁面,然后點擊屏幕底部的“后退”按鈕。
- 在通用Windows平台(UWP)上,導航欄位於頁面頂部,並且具有一個導航到詳細信息頁面的按鈕。
注:母版頁的按鈕(Page Button,卻是顯示在詳情頁的頂部)在android和uwp是自帶的,在ios上顯示,必須設置IconImageSource="hamburger.png"才能顯示,否則只顯示成Title。
設置了IconImageSource,則Title不會顯示(但是必須設置Title)。
以下是設置IconImageSource與否在Ios上的表現:

所選擇的主頁上對應於該項目的細節頁面顯示數據,並且將詳細信息頁面的主要部件示於下面的屏幕截圖:

詳細信息頁面包含一個導航欄,其內容取決於平台:
- 在iOS上,導航欄位於頁面頂部,並顯示標題(Page Title),並具有返回主頁面的按鈕,前提是詳細頁面實例包裝在NavigationPage實例中。 此外,可以通過向右滑動詳細信息頁面來返回母版頁。
- 在Android上,導航欄位於頁面頂部,並顯示標題,圖標和返回主頁面的按鈕。 該圖標在[Activity]屬性中定義,該屬性用於裝飾特定於Android平台的項目中的MainActivity類。
- 在UWP上,導航欄位於頁面頂部,並顯示標題,並具有一個返回主頁面的按鈕。
導航行為
母版頁和詳細頁之間的導航體驗行為取決於平台:
- 在iOS上,詳細信息頁面向右滑動,而母版頁面從左側滑動,並且詳細信息頁面的左側部分仍然可見。
- 在Android上,詳細信息頁和母版頁彼此重疊。
- 在UWP上,如果將MasterBehavior屬性設置為Popover,則母版頁將從詳細信息頁面的左側滑動。 有關更多信息,請參見控制明細頁顯示行為。
在橫向模式下將觀察到類似的行為,除了iOS和Android上的母版頁與縱向模式下的母版頁具有相似的寬度外,因此更多詳細信息頁將可見。
創建MasterDetailPage
注意:MasterDetailPage被設計為根頁面,在其他頁面類型中將其用作子頁面可能會導致意外和不一致的行為。 另外,建議MasterDetailPage的母版頁應始終為ContentPage實例,並且詳細信息頁應僅填充TabbedPage,NavigationPage和ContentPage實例,這將有助於確保在所有平台上的一致用戶體驗。
<MasterDetailPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:MasterDetailPageNavigation;assembly=MasterDetailPageNavigation" x:Class="MasterDetailPageNavigation.MainPage"> <MasterDetailPage.Master> <local:MasterPage x:Name="masterPage" /> </MasterDetailPage.Master> <MasterDetailPage.Detail> <NavigationPage> <x:Arguments> <local:ContactsPage /> </x:Arguments> </NavigationPage> </MasterDetailPage.Detail> </MasterDetailPage>
MasterDetailPage.Master屬性設置為ContentPage實例。 MasterDetailPage.Detail屬性設置為包含ContentPage實例的NavigationPage。
創建母版頁
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="using:MasterDetailPageNavigation" x:Class="MasterDetailPageNavigation.MasterPage" Padding="0,40,0,0" IconImageSource="hamburger.png" Title="Personal Organiser"> <StackLayout> <ListView x:Name="listView" x:FieldModifier="public"> <ListView.ItemsSource> <x:Array Type="{x:Type local:MasterPageItem}"> <local:MasterPageItem Title="Contacts" IconSource="contacts.png" TargetType="{x:Type local:ContactsPage}" /> <local:MasterPageItem Title="TodoList" IconSource="todo.png" TargetType="{x:Type local:TodoListPage}" /> <local:MasterPageItem Title="Reminders" IconSource="reminders.png" TargetType="{x:Type local:ReminderPage}" /> </x:Array> </ListView.ItemsSource> <ListView.ItemTemplate> <DataTemplate> <ViewCell> <Grid Padding="5,10"> <Grid.ColumnDefinitions> <ColumnDefinition Width="30"/> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <Image Source="{Binding IconSource}" /> <Label Grid.Column="1" Text="{Binding Title}" /> </Grid> </ViewCell> </DataTemplate> </ListView.ItemTemplate> </ListView> </StackLayout> </ContentPage>
該頁面設置了Title和IconImageSource屬性,該圖標將顯示在詳細信息頁面上,前提是該詳細信息頁面具有標題欄。 必須在iOS上啟用此功能,方法是將詳細信息頁面實例包裝在NavigationPage實例中。
注:MasterDetailPage.Master頁必須設置其Title屬性,否則將發生異常。
創建詳情頁
MasterPage實例包含一個ListView屬性,該屬性公開其ListView實例,以便MainPage MasterDetailPage實例可以注冊一個事件處理程序來處理ItemSelected事件。 這使MainPage實例可以將Detail屬性設置為表示所選ListView項的頁面。 以下代碼示例顯示了事件處理程序:
public partial class MainPage : MasterDetailPage { public MainPage () { ... masterPage.listView.ItemSelected += OnItemSelected; } void OnItemSelected (object sender, SelectedItemChangedEventArgs e) { var item = e.SelectedItem as MasterPageItem; if (item != null) { Detail = new NavigationPage ((Page)Activator.CreateInstance (item.TargetType)); masterPage.listView.SelectedItem = null; IsPresented = false; } } }
OnItemSelected方法執行以下操作:
- 它從ListView實例檢索SelectedItem,並提供它(如果不為null的話),則將詳細信息頁面設置為存儲在MasterPageItem的TargetType屬性中的頁面類型的新實例,頁面類型包裝在NavigationPage實例中,以確保通過MasterPage上的IconImageSource屬性引用的圖標顯示在iOS的詳細信息頁面中。
- 在母版頁ListView中選定的項目設置為空,以確保下次進入母版頁時沒有ListView的項目將被選中。
- 通過將MasterDetailPage.IsPresented屬性設置為false,可以向用戶顯示詳細信息頁面。 此屬性控制是否顯示母版頁或詳細頁,應該將其設置為true以顯示母版頁,並設置為false以顯示詳細信息頁。
控制詳細信息頁面的顯示行為
MasterDetailPage如何管理母版頁和詳細信息頁取決於應用程序是在手機還是平板電腦上運行,設備的方向以及MasterBehavior屬性的值,此屬性確定詳細信息頁面的顯示方式。 可能的值為:
- Default-使用平台默認顯示頁面。
- Popover–詳細信息頁面覆蓋或部分覆蓋了母版頁。
- Split–主頁面顯示在左側,詳細信息頁面顯示在右側。
- SplitOnLandscape –當設備處於橫向時,將使用拆分屏幕。
- SplitOnPortrait –設備處於縱向時使用拆分屏幕。
<?xml version="1.0" encoding="UTF-8"?> <MasterDetailPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="MasterDetailPageNavigation.MainPage" MasterBehavior="Popover"> ... </MasterDetailPage>
但是,MasterBehavior屬性的值僅影響在平板電腦或台式機上運行的應用程序,手機上運行的應用程序始終具有Popover行為。
NavigationPage
NavigationPage使用基於堆棧的體系結構管理其他頁面之間的導航。 在應用程序中使用頁面導航時,應將主頁的實例傳遞給NavigationPage對象的構造函數。
NavigationPage不可用於Xaml創建。
C#方式
注意:這里只有將ContentPage用NavigationPage包裹起來,才能使用Navigation屬性進行導航。
分層導航
NavigationPage類提供了分層的導航體驗,其中用戶可以根據需要在頁面之間進行導航。 該類將導航實現為Page對象的后進先出(LIFO)堆棧。 本文演示了如何使用NavigationPage類在頁面堆棧中執行導航。
要從一個頁面移動到另一個頁面,應用程序會將新頁面推入導航堆棧,在該頁面中它將變為活動頁面,如下圖所示:

要返回上一頁,應用程序將從導航堆棧中彈出當前頁面,新的最頂層頁面變為活動頁面。
導航方法由任何Page派生類型上的Navigation屬性公開,這些方法提供了將頁面推入導航堆棧,從導航堆棧彈出頁面以及執行堆棧操作的能力。
執行導航
在分層導航中,NavigationPage類用於通過ContentPage對象的堆棧來導航。 以下屏幕快照顯示了每個平台上NavigationPage的主要組件:

NavigationPage的布局取決於平台:
- 在iOS上,頁面頂部顯示導航欄,該導航欄顯示標題,並且具有返回上一頁的“后退”按鈕。
- 在Android上,頁面頂部顯示導航欄,其中顯示標題,圖標和返回上一頁的“后退”按鈕。 該圖標在[Activity]屬性中定義,該屬性用於裝飾特定於Android平台的項目中的MainActivity類。
- 在通用Windows平台上,頁面頂部顯示導航欄,顯示標題。
在所有平台上,Page.Title屬性的值將顯示為頁面標題。
注:建議僅用ContentPage實例填充NavigationPage。
創建根頁面
添加到導航堆棧的第一頁稱為應用程序的根頁面,下面的代碼示例顯示了如何完成此操作:
public App () { MainPage = new NavigationPage (new Page1Xaml ()); }
這導致Page1Xaml ContentPage實例被推入導航堆棧,在該堆棧中它成為應用程序的活動頁面和根頁面。
注:NavigationPage實例的RootPage屬性提供對導航堆棧中第一頁的訪問。
將頁面推送到導航堆棧
要導航到Page2Xaml,必須在當前頁面的Navigation屬性上調用PushAsync方法,如以下代碼示例所示:
async void OnNextPageButtonClicked (object sender, EventArgs e) { await Navigation.PushAsync (new Page2Xaml ()); }
這導致Page2Xaml實例被推入導航堆棧,在該堆棧中它成為活動頁面,此頁面頂部會帶有返回按鈕。
調用PushAsync方法時,將發生以下事件:
- 調用PushAsync的頁面將調用其OnDisappearing覆蓋。
- 導航到的頁面將調用其OnAppearing覆蓋。
- PushAsync任務完成。
但是,這些事件發生的確切順序取決於平台。 有關更多信息,請參見Charles Petzold的Xamarin.Forms書的第24章。
從導航堆棧彈出頁面
通過按設備上的“后退”按鈕,可以從導航堆棧中彈出活動頁面,而不管這是設備上的物理按鈕還是屏幕上的按鈕。
要以編程方式返回到原始頁面,Page2Xaml實例必須調用PopAsync方法。
除了PushAsync和PopAsync方法之外,每個頁面的Navigation屬性還提供了PopToRootAsync方法,如以下代碼示例所示:
async void OnRootPageButtonClicked (object sender, EventArgs e) { await Navigation.PopToRootAsync (); }
此方法從導航堆棧中彈出除根頁面以外的所有內容,因此使應用程序的根頁面成為活動頁面。
動畫頁面過渡
每個頁面的Navigation屬性還提供了重載的push和pop方法,其中包括一個布爾參數,該布爾參數控制是否在導航期間顯示頁面動畫,如以下代碼示例所示:
await Navigation.PushAsync (new Page2Xaml (), false);
將boolean參數設置為false將禁用頁面轉換動畫,而將參數設置為true則將啟用頁面轉換動畫,前提是基礎平台支持該動畫。 但是,缺少此參數的push和pop方法默認情況下會啟用動畫。
導航時傳遞數據
有時,頁面在導航期間必須將數據傳遞到另一個頁面。兩種技術實現這一點:通過一個頁面構造函數傳遞數據;通過新的一頁的BindingContext中設置的數據。
通過頁面構造器傳遞數據
在導航期間將數據傳遞到另一個頁面的最簡單技術是通過頁面構造函數參數,
通過BindingContext傳遞數據
將新頁面的BindingContext設置為需要傳遞的數據,如以下代碼示例所示:
async void OnNavigateButtonClicked (object sender, EventArgs e) { var contact = new Contact { Name = "Jane Doe", Age = 30, Occupation = "Developer", Country = "USA" }; var secondPage = new SecondPage (); secondPage.BindingContext = contact; await Navigation.PushAsync (secondPage); }
此代碼將SecondPage實例的BindingContext設置為Contact實例,然后導航到SecondPage。
然后,SecondPage使用數據綁定來顯示Contact實例數據,如以下XAML代碼示例所示:
操縱導航堆棧
Navigation屬性公開了NavigationStack屬性,從中可以獲取導航堆棧中的頁面,Xamarin.Forms保留對導航堆棧的訪問權限,而Navigation屬性提供了InsertPageBefore和RemovePage方法,用於通過插入頁面或刪除頁面來操縱堆棧。
InsertPageBefore方法將指定的頁面在導航堆棧中插入現有的指定頁面之前,如下圖所示:

這些方法可實現自定義導航體驗,例如在成功登錄后用新頁面替換登錄頁面。 下面的代碼示例演示了這種情況:
async void OnLoginButtonClicked (object sender, EventArgs e) { ... var isValid = AreCredentialsCorrect (user); if (isValid) { App.IsUserLoggedIn = true; Navigation.InsertPageBefore (new MainPage (), this); await Navigation.PopAsync (); } else { // Login failed } }
如果用戶的憑據正確,則將MainPage實例插入當前頁面之前的導航堆棧中。 然后,PopAsync方法從導航堆棧中刪除當前頁面,而MainPage實例成為活動頁面。【此種方式 可以將登陸頁面從堆棧中刪除,而不是壓入棧里】
在導航欄中顯示視圖
任何Xamarin.Forms視圖都可以顯示在NavigationPage的導航欄中,這是通過將NavigationPage.TitleView附加屬性設置為View來完成的。 可以在任何頁面上設置此附加屬性,並且當將頁面推入NavigationPage時,NavigationPage將遵循該屬性的值。
以下示例摘自Title View示例,顯示了如何從XAML設置NavigationPage.TitleView附加屬性:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="NavigationPageTitleView.TitleViewPage"> <NavigationPage.TitleView> <Slider HeightRequest="44" WidthRequest="300" /> </NavigationPage.TitleView> ... </ContentPage>
注:除非使用WidthRequest和HeightRequest屬性指定視圖的大小,否則許多視圖不會出現在導航欄中。 另外,也可以將View包裹在StackLayout中,並將HorizontalOptions和VerticalOptions屬性設置為適當的值。
請注意,由於Layout類是從View類派生的,因此可以將TitleView附加屬性設置為顯示包含多個視圖的布局類。在iOS和通用Windows平台(UWP)上,無法更改導航欄的高度,因此,如果導航欄中顯示的視圖大於導航欄的默認大小,則會發生裁剪。但是,在Android上,可以通過將NavigationPage.BarHeight可綁定屬性設置為表示新高度的double來更改導航欄的高度。 有關更多信息,請參見在NavigationPage上設置導航欄高度。
或者,可以通過將某些內容放置在導航欄中,而將某些內容放置在頁面頂部的視圖中(顏色與導航欄顏色匹配)來擴展導航欄。 另外,在iOS上,可以通過將NavigationPage.HideNavigationBarSeparator bindable屬性設置為true來刪除導航欄底部的分隔線和陰影。 有關更多信息,請參見將導航欄分隔符隱藏在NavigationPage上。
注:BackButtonTitle,Title,TitleIcon和TitleView屬性都可以定義占用導航欄上空間的值。 盡管導航欄的大小隨平台和屏幕的大小而變化,但由於可用空間有限,所有這些屬性的設置都會導致沖突。 您可能會發現,僅設置TitleView屬性,可以更好地實現所需的導航欄設計,而不是嘗試使用這些屬性的組合。
也就是說在TabbedPage中若放置的是包含的內容頁的NavigationPage,則在內容頁中設置Title 會顯示在導航欄中;若放置的是內容頁,設置Title則不會顯示。
局限性
在NavigationPage的導航欄中顯示View時,要注意許多限制:
- 在iOS上,放置在NavigationPage導航欄中的視圖會以不同的位置顯示,這取決於是否啟用大標題。有關啟用大標題的更多信息,請參見顯示大標題。
- 在Android上,只能在使用app-compat的應用程序中將視圖放置在NavigationPage的導航欄中。
- 不建議將大而復雜的視圖(例如ListView和TableView)放置在NavigationPage的導航欄中。
TabbedPage
TabbedPage派生自抽象的MultiPage類,並允許使用選項卡在子頁面之間導航,將Children屬性設置為頁面的集合,或者將ItemsSource屬性設置為數據對象的集合,並將ItemTemplate屬性設置為DataTemplate,以描述如何直觀地表示每個對象。
Xamarin.Forms TabbedPage由一個選項卡列表和一個較大的詳細區域組成,每個選項卡都將內容加載到詳細區域。 本文演示了如何使用TabbedPage瀏覽頁面集合。

TabbedPage及其選項卡的布局取決於平台:
- 在iOS上,標簽列表顯示在屏幕底部,詳細信息區域在上方。 每個選項卡還具有一個圖標圖像,該圖像應為30x30 PNG,正常分辨率為透明,高分辨率為60x60,iPhone 6 Plus分辨率為90x90。 如果有五個以上的選項卡,則會出現一個“更多”選項卡,可用於訪問其他選項卡。 有關圖標要求的更多信息,請參見創建選項卡式應用程序。
注:iOS版TabbedRenderer具有可重寫的GetIcon方法,可用於從指定源加載選項卡圖標。通過此覆蓋,可以將SVG圖像用作TabbedPage上的圖標。另外,可以提供圖標的選定和未選定版本。
- 在Android上,默認情況下,標簽列表顯示在屏幕頂部,而詳細信息區域在下面。 但是,可以將特定於平台的選項卡列表移動到屏幕底部。 有關更多信息,請參見設置TabbedPage工具欄的位置和顏色。
【注:測試中發現,若不設置在底部時On<Android>().SetToolbarPlacement(ToolbarPlacement.Bottom),xaml中設置的BarBackgroundColor不會生效,此時單擊tab圖標不會上下跳動(沒設置Title情況下);而設置在底部時,BarBackgroundColor會生效,此時單擊tab會上下跳動(沒設置Title情況下),解決這個問題需要特定平台重新渲染)】
注:在Android上使用AppCompat 時,每個選項卡也將顯示一個圖標。 此外,Android AppCompat的TabbedPageRenderer具有可重寫的GetIconDrawable方法,該方法可用於從自定義Drawable加載選項卡圖標。 通過此覆蓋,可以將SVG圖像用作TabbedPage上的圖標,並且可以與頂部和底部的標簽欄一起使用。 另外,可重寫的SetTabIcon方法可用於從自定義Drawable中為頂部標簽欄加載標簽圖標。
- 在Windows平板電腦上,選項卡並非始終可見,用戶需要向下滑動(或單擊鼠標右鍵(如果已連接鼠標),以查看TabbedPage中的選項卡(如下所示)。

創建TabbedPage
TabbedPage定義以下屬性:
BarBackgroundColorof typeColor, the background color of the tab bar.- BackgroundColor 設置的大塊詳情區域的背景顏色
BarTextColorof typeColor, the color of text on the tab bar.SelectedTabColorof typeColor, the color of the tab when it's selected.UnselectedTabColorof typeColor, the color of the tab when it's unselected.
所有這些屬性都由BindableProperty對象支持,這意味着可以對它們進行樣式設置,並且這些屬性可以成為數據綁定的目標。
可以使用兩種方法來創建TabbedPage:
- 用子頁面對象的集合(例如ContentPage實例的集合)填充TabbedPage。
- 將一個集合分配給ItemsSource屬性,將一個DataTemplate分配給ItemTemplate屬性,以返回該集合中對象的頁面。
通過這兩種方法,TabbedPage將在用戶選擇每個選項卡時顯示每個頁面。
注:建議僅使用NavigationPage和ContentPage實例填充TabbedPage。 這將有助於確保在所有平台上的一致用戶體驗。
1、第一種方式:
添加了兩個子頁面,一個是ContentPage,一個是包含了內容頁的NavigationPage
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:TabbedPageWithNavigationPage;assembly=TabbedPageWithNavigationPage" x:Class="TabbedPageWithNavigationPage.MainPage"> <local:TodayPage /> <NavigationPage Title="Schedule" IconImageSource="schedule.png"> <x:Arguments> <local:SchedulePage /> </x:Arguments> </NavigationPage> </TabbedPage>
注:TabbedPage不支持UI虛擬化。 因此,如果TabbedPage包含太多子元素,則性能可能會受到影響。
注:盡管可以將NavigationPage放置在TabbedPage中,但不建議在NavigationPage中放置TabbedPage。 這是因為,在iOS上,UITabBarController始終充當UINavigationController的包裝器。 有關更多信息,請參見iOS開發者庫中的組合視圖控制器接口。
Tab選項內的導航
可以通過調用ContentPage實例的Navigation屬性上的PushAsync方法從第二個選項卡執行導航,如以下代碼示例所示:
async void OnUpcomingAppointmentsButtonClicked (object sender, EventArgs e) { await Navigation.PushAsync (new UpcomingAppointmentsPage ()); }
這導致UpcomingAppointmentsPage實例被推到導航堆棧上,在該堆棧中成為活動頁面。
2、第二種方式:用模板填充TabbedPage
以下XAML代碼示例顯示通過將DataTemplate分配給ItemTemplate屬性以返回集合中對象的頁面而構造的TabbedPage:
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:TabbedPageDemo;assembly=TabbedPageDemo" x:Class="TabbedPageDemo.TabbedPageDemoPage"> <TabbedPage.Resources> <ResourceDictionary> <local:NonNullToBooleanConverter x:Key="booleanConverter" /> </ResourceDictionary> </TabbedPage.Resources> <TabbedPage.ItemTemplate> <DataTemplate> <ContentPage Title="{Binding Name}" IconImageSource="monkeyicon.png"> <StackLayout Padding="5, 25"> <Label Text="{Binding Name}" Font="Bold,Large" HorizontalOptions="Center" /> <Image Source="{Binding PhotoUrl}" WidthRequest="200" HeightRequest="200" /> <StackLayout Padding="50, 10"> <StackLayout Orientation="Horizontal"> <Label Text="Family:" HorizontalOptions="FillAndExpand" /> <Label Text="{Binding Family}" Font="Bold,Medium" /> </StackLayout> ... </StackLayout> </StackLayout> </ContentPage> </DataTemplate> </TabbedPage.ItemTemplate> </TabbedPage>
通過在代碼隱藏文件的構造函數中設置ItemsSource屬性,用數據填充TabbedPage。
TabbedPage的重寫文章參考:用Xamarin實現選項卡應用框架
TemplatedPage
TemplatedPage顯示帶有控件模板的全屏內容,並且是ContentPage的基類。參考控件模板。
使用控件模板可清晰地將頁面外觀及其內容分離,從而能夠創建主題明確的頁面。
介紹
Xamarin.Forms控件模板提供了在運行時輕松地對應用程序頁面進行主題設置和重新主題設置的功能。
控件具有不同的屬性,例如BackgroundColor和TextColor,可以定義控件外觀的各個方面,可以使用樣式設置這些屬性,可以在運行時更改樣式以實現基本主題。但是,樣式不能在頁面的外觀與其內容之間保持清晰的分隔,並且通過設置此類屬性可以進行的更改受到限制。
控件模板在頁面外觀及其內容之間提供了清晰的分隔,因此可以創建易於主題化的頁面。例如,一個應用程序可能包含提供深色主題和淺色主題的應用程序級控制模板。可以通過應用控制模板之一來主題化應用程序中的每個ContentPage,而無需更改每個頁面顯示的內容。此外,控件模板提供的主題不僅限於更改控件的屬性,他們還可以更改用於實現主題的控件。
ControlTemplate指定頁面或視圖的外觀,並包含根布局,在布局內還包含實現模板的控件。 通常,ControlTemplate將利用ContentPresenter來標記要在頁面或視圖中顯示的內容出現的位置(占位符)。 然后,使用ControlTemplate的頁面或視圖將定義要由ContentPresenter顯示的內容(替換那個占位符)。
不同顏色主題設置多個ControlTemplate,使用這個模板的內容頁中放置主題不變的部分。
通過設置控件模板的ControlTemplate屬性,可以將它們應用於以下類型:
創建ControlTemplate並將其分配給這些類型后,所有現有外觀都會替換為ControlTemplate中定義的外觀。 此外,除了通過使用ControlTemplate屬性設置外觀外,還可以通過使用樣式來應用控件模板以進一步擴展主題功能。
注:什么是TemplatedPage和TemplatedView類型?
TemplatedPage是ContentPage的基類,並且是Xamarin.Forms提供的最基本的頁面類型。 與ContentPage不同,TemplatedPage沒有Content屬性。 因此,內容不能直接添加到TemplatedPage實例,而是通過為TemplatedPage實例設置控件模板來添加內容。 同樣,TemplatedView是ContentView的基類,與ContentView不同,TemplatedView沒有Content屬性,因此,內容不能直接添加到TemplatedView實例,而是通過為TemplatedView實例設置控件模板來添加內容。
可以在XAML和C#中創建控件模板:
- 在XAML中創建的控件模板是在ResourceDictionary中定義的,該ResourceDictionary分配給頁面的Resources集合,或更常見的是分配給應用程序的Resources集合。
- 用C#創建的控件模板通常在頁面的類中定義,或者在可以全局訪問的類中定義。
選擇在哪里定義ControlTemplate實例會影響可以在哪里使用它:
- 在頁面級別定義的ControlTemplate實例只能應用於頁面。
- 在應用程序級別定義的ControlTemplate實例可以應用於整個應用程序的頁面。
視圖層次結構中較低的控件模板優先於較高的控件模板。例如,在頁面級別定義的名為DarkTheme的ControlTemplate將優先於在應用程序級別定義的同名模板。因此,應該在應用程序級別定義一個定義要應用於應用程序中每個頁面的主題的控件模板。
創建控件模板
可以在應用程序級別或頁面級別定義控件模板。
在XAML中創建ControlTemplate
若要在應用程序級別定義ControlTemplate,必須將ResourceDictionary添加到App類。 默認情況下,從模板創建的所有Xamarin.Forms應用程序都使用App類來實現Application子類。 若要在應用程序級別聲明ControlTemplate,在使用XAML的應用程序的ResourceDictionary中,必須將默認的App類替換為XAML App類並在其后面添加相關代碼,如以下代碼示例所示:
<Application xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="SimpleTheme.App"> <Application.Resources> <ResourceDictionary> <ControlTemplate x:Key="TealTemplate"> <Grid> ... <BoxView ... /> <Label Text="Control Template Demo App" TextColor="White" VerticalOptions="Center" ... /> <ContentPresenter ... /> <BoxView Color="Teal" ... /> <Label Text="(c) Xamarin 2016" TextColor="White" VerticalOptions="Center" ... /> </Grid> </ControlTemplate> <ControlTemplate x:Key="AquaTemplate"> ... </ControlTemplate> </ResourceDictionary> </Application.Resources> </Application>
每個ControlTemplate實例都在ResourceDictionary中創建為可重用對象,這是通過為每個聲明賦予唯一的x:Key屬性來實現的,該屬性在ResourceDictionary中為其提供了描述性的鍵。
以下代碼示例顯示了將TealTemplate應用於ContentView的ContentPage:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="SimpleTheme.HomePage"> <ContentView x:Name="contentView" Padding="0,20,0,0" ControlTemplate="{StaticResource TealTemplate}"> <StackLayout VerticalOptions="CenterAndExpand"> <Label Text="Welcome to the app!" HorizontalOptions="Center" /> <Button Text="Change Theme" Clicked="OnButtonClicked" /> </StackLayout> </ContentView> </ContentPage>
通過使用StaticResource標記擴展,將TealTemplate分配給ContentView.ControlTemplate屬性。 ContentView.Content屬性設置為StackLayout,用於定義要在ContentPage上顯示的內容,該內容將由TealTemplate中包含的ContentPresenter顯示。
在運行時重新定義應用程序
單擊“更改主題”按鈕將執行OnButtonClicked方法,如以下代碼示例所示:
void OnButtonClicked (object sender, EventArgs e) { originalTemplate = !originalTemplate; contentView.ControlTemplate = (originalTemplate) ? tealTemplate : aquaTemplate; }
注:在ContentPage上,可以分配Content屬性,也可以設置ControlTemplate屬性。 發生這種情況時,如果ControlTemplate包含ContentPresenter實例,則ContentPresenter將在ControlTemplate中呈現分配給Content屬性的內容。
Set a ControlTemplate with a style
還可以通過樣式應用ControlTemplate來進一步擴展主題功能。 這可以通過在ResourceDictionary中為目標視圖創建隱式或顯式樣式,並在Style實例中設置目標視圖的ControlTemplate屬性來實現。 以下代碼示例顯示了已添加到應用程序級別ResourceDictionary的隱式樣式:
<Style TargetType="ContentView"> <Setter Property="ControlTemplate" Value="{StaticResource TealTemplate}" /> </Style>
由於Style實例是隱式的,因此它將應用於應用程序中的所有ContentView實例。 因此,不再需要設置ContentView.ControlTemplate屬性。
從模板獲取命名元素
實例化模板后,可以在控件模板中檢索命名元素,這可以通過GetTemplateChild方法實現,該方法返回實例化的ControlTemplate可視樹中的命名元素。
實例化控件模板后,將調用模板的OnApplyTemplate方法。 因此,應該從TemplatedPage派生頁面(例如ContentPage)或TemplatedView派生視圖(例如ContentView)中的OnApplyTemplate重寫中調用GetTemplateChild方法。
注:僅在調用OnApplyTemplate方法之后才應調用GetTemplateChild方法。
以下示例顯示了自定義控件的控件模板:
<controls:MyCustomControl ...> <controls:MyCustomControl.ControlTemplate> <ControlTemplate> <Label x:Name="myLabel" /> </ControlTemplate> <controls:MyCustomControl.ControlTemplate> </controls:MyCustomControl>
Label元素已命名,因此可以在自定義控件的代碼隱藏區中進行檢索。 這是通過從OnApplyTemplate重寫中的自定義控件調用GetTemplateChild方法實現的:
class MyCustomControl : ContentView { Label myLabel; protected override void OnApplyTemplate() { myLabel = GetTemplateChild("myLabel"); } //... }
模板綁定
模板綁定允許控件模板中的控件將數據綁定到公共屬性,從而可以輕松更改控件模板中控件的屬性值。 本文演示了如何使用模板綁定從控件模板執行數據綁定。
TemplateBinding用於將控件模板中的控件屬性綁定到擁有該控件模板的目標視圖的父級上的可綁定屬性。 例如,您可以使用模板綁定將Label.Text屬性綁定到定義要顯示的文本的可綁定屬性,而不是定義ControlTemplate內部Label實例顯示的文本。
TemplateBinding與現有的Binding類似,不同之處在於TemplateBinding的源始終自動設置為擁有控件模板的目標視圖的父級。 但是,請注意,不支持在ControlTemplate之外使用TemplateBinding。
在XAML中創建TemplateBinding
在XAML中,使用TemplateBinding標記擴展來創建TemplateBinding,如以下代碼示例所示:
<ControlTemplate x:Key="TealTemplate"> <Grid> ... <Label Text="{TemplateBinding Parent.HeaderText}" ... /> ... <Label Text="{TemplateBinding Parent.FooterText}" ... /> </Grid> </ControlTemplate>
除了將Label.Text屬性設置為靜態文本外,這些屬性還可以使用模板綁定來綁定到擁有ControlTemplate的目標視圖的父級上的可綁定屬性。 但是,請注意,模板綁定綁定到Parent.HeaderText和Parent.FooterText,而不是HeaderText和FooterText。 這是因為在此示例中,可綁定屬性是在目標視圖的祖父母(而不是父視圖)上定義的,查看官網示例。
模板綁定的源總是自動設置為擁有控件模板的目標視圖的父級,這里是ContentView實例。 模板綁定使用Parent屬性返回ContentView實例(即ContentPage實例)的父元素。 因此,在ControlTemplate中使用TemplateBinding綁定到Parent.HeaderText和Parent.FooterText可以找到在ContentPage上定義的可綁定屬性。
CarouselPage
CarouselPage派生自抽象的MultiPage類,並允許通過手指滑動在子頁面之間導航,將Children屬性設置為ContentPage對象的集合,或者將ItemsSource屬性設置為數據對象的集合,並將ItemTemplate屬性設置為DataTemplate,以描述如何直觀地表示每個對象。
