XAML基礎知識
XAML(eXtensible Application Markup Language)可擴展應用程序標記語言,允許開發者在Xamarin.Forms應用中采用標記而不是代碼來定義用戶界面。XAML在Xamarin.Forms 程序中不是必須的,但通常它比后台等效代碼會更簡潔和更直觀,並可能會非常有用。 XAML 特別適用於常用 MVVM (Model-View-ViewModel)應用程序體系結構:XAML定義了通過基於XAML的數據綁定鏈接到ViewModel代碼的視圖。
XAML是Microsoft創建的一種基於XML的語言,是用於實例化和初始化對象以及在父子層次結構中組織這些對象的編程代碼的替代方法。
常用於微軟的技術:WPF、Silverlight、UWP。
XAML也是Xamarin.Forms的一部分,Xamarin.Forms是適用於iOS,Android和UWP移動設備的跨平台基於本機的編程接口。 在XAML文件中,Xamarin.Forms開發人員可以使用所有Xamarin.Forms視圖,布局和頁面以及自定義類來定義用戶界面。. XAML文件可以被編譯,也可以嵌入到可執行文件中,無論哪種方法,XAML信息在構建時被解析以定位已命名的對象,然后在運行時再次被解析以實例化和初始化對象,並在這些對象和編程代碼之間建立鏈接。
XAML 具有幾大優勢(與等效的后台代碼相比):
- XAML 通常會更簡潔和可讀。
- XML中固有的父-子層次結構允許XAML以更清晰的視覺效果模仿用戶界面對象的父-子層次結構。
- XAML可以很容易地由程序員手工編寫,但也可以通過可視化設計工具生成和使用XAML。
當然,也有缺點,主要與標記語言固有的限制有關:
- XAML 不能包含代碼。 必須在代碼文件中定義所有事件處理程序。
- XAML 不能包含重復處理的循環。 (但是,多個 Xamarin.Forms 視覺對象 — 最值得注意的是
ListView
— 可以生成多個子級中的對象基於其ItemsSource
集合。) - XAML 不能包含條件處理 (但是,數據綁定可以引用,可有效地處理某些條件的代碼基於綁定轉換器。)
- XAML通常不能實例化不定義無參數構造函數的類。(然而,有時有一種方法可以繞過這個限制
- XAML 通常不能調用方法。 (同樣,此限制有時可以克服。)
在 Xamarin.Forms 應用程序中還沒有可視化設計器生成 XAML。 所有 XAML 都必須手動編寫,但是有XAML 預覽程序。剛接觸XAML的程序員可能想要頻繁地構建和運行他們的應用程序,特別是在任何明顯不正確的事情之后。即使是在XAML中有豐富經驗的開發人員也知道實驗是有回報的。
XAML is basically XML, but XAML has some unique syntax features. The most important are:
- Property elements
- Attached properties
- Markup extensions
這些特性不是XML擴展。XAML完全是合法的XML。但是這些XAML語法特性以獨特的方式使用XML。下面的文章將詳細討論它們,最后介紹如何使用XAML實現MVVM。
1、XAML入門
XAML用於定義頁面的可視化內容,並和C#后台代碼文件一起工作。
后台代碼為標記提供代碼支持, 這兩個文件一起定義包含子視圖和屬性初始化的類。 在 XAML 文件中,類和屬性由XML元素和特性引用,並建立標記和代碼之間的鏈接。

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:XamlSamples" x:Class="XamlSamples.MainPage"> <StackLayout> <!-- Place new controls here --> <Label Text="Welcome to Xamarin Forms!" VerticalOptions="Center" HorizontalOptions="Center" /> </StackLayout> </ContentPage>
XAML文件剖析
1、標記
XAML是一種聲明式語言,一個標簽聲明一個元素(每個元素對應內存中的一個對象,對象間的層級關系要么並列要么包含)。標簽特性(Attribute)語法層面,而屬性(property)是對象層面,特性和屬性並不是完全對應,特性一般比屬性多。
以下是ContentPage的Attribute:
xmlns是在聲明或引用命名空間,一般的規則是公司網站的名字+自定義的名字。聲明命名空間的好處:當類重名時,可以用命名空間區分。
法語格式,xmlns:[可選的映射前綴] = “命名空間”
解釋:
- 映射前綴可以無,也可以有(名字任意)
- 如果沒有寫可選映射前綴,則意味着所有來自這個命名空間的標簽都不用加前綴,這個也是默認命名空間(只能只有一個),
XAML中引入命名空間:先要添加dll,如在根元素寫上:xmlns:c=”clr-namespace:命名空間;assembly=程序集的名字” ,后續使用時,就可以<c:Button>...
上述:
第一個XML名稱空間聲明:沒有映射前綴,引用Xamarin.Forms名稱空間,說明在XAML文件中引用Xamarin.Forms中的類的標記是不用前綴,例如ContentPage 。
【注:url地址形式的名稱空間其實是XAML解析器的一個硬性編碼,只要見到這些固定的字符串,就會把一系列必要的程序集和程序集中包含的.NET命名空間引用進來】
第二個名稱空間聲明定義了一個x前綴。它用於XAML本身固有的、由XAML的其他實現支持的幾個元素和屬性。然而,這些元素和屬性根據URI中嵌入的年份略有不同。Xamarin.Forms支持2009年XAML規范,但不是全部。
常用的X:Name="xxx" ,然后在cs代碼中就可以直接使用此控件。
不難看出:第一個是與繪制UI相關的程序集,是表示層面上的東西;第二個是對應XAML語言解析處理相關的程序集,是語言層面上的東西。
第三個 Local命名空間聲明允許您訪問.NET標准庫項目中的其他類。用來讓我們在xaml中引用其他程序集中的類,類似於Using的作用.
最后一個,x前綴用於一個名為Class的屬性,x前綴定義的這個命名空間是用於XAML解析功能的。x:Class 解析成C#類(分部類)。
Class屬性指定一個完全限定的. net類名:XamlSamples命名空間中的MainPage類。這意味着這個XAML文件在XamlSamples命名空間中定義了一個名為MainPage的新類,這個類派生自ContentPage標記,其中顯示了x: class屬性。
2、后台代碼

using Xamarin.Forms; namespace XamlSamples { public partial class MainPage : ContentPage { public MainPage() { InitializeComponent(); } } }
MainPage類派生自ContentPage,但請注意部分類定義。這意味着MainPage應該有另一個局部類定義,但是它在哪里?那個初始化的ecomponent方法是什么
當 Visual Studio 生成項目時,它會分析要生成的 XAML 文件C#代碼文件。 如果查看XamlSamples\XamlSamples\obj\Debug目錄中,您會發現名為的文件XamlSamples.MainPage.xaml.g.cs。 G 表示生成的,這是MainPage的分部類其他定義
,其中包含從MainPage構造函數調用的InitializeComponent方法的定義。 這兩部分會編譯在一起。 根據是否編譯了XAML, XAML文件或XAML文件的二進制形式都嵌入到可執行文件中。
在運行時,在特定平台項目的代碼調用LoadApplication
方法,並向它傳遞.NET Standard 庫中的新實例App
類,App
類構造函數實例化MainPage(
該類的構造函數調用InitializeComponent)
,InitializeComponent調用LoadFromXaml方法
從.NET Standard 庫中提取 XAML 文件 (或其編譯的二進制文件)。LoadFromXaml
初始化 XAML 文件中定義的所有對象、 以父-子關系將它們連接在一起,將代碼中定義的事件處理程序附加到XAML文件中的事件集,並將生成的對象樹設置為頁面的內容。
3、設置頁面內容

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="XamlSamples.HelloXamlPage" Title="Hello XAML Page"> <ContentPage.Content> <Label Text="Hello, XAML!" VerticalOptions="Center" HorizontalTextAlignment="Center" Rotation="-15" IsVisible="true" FontSize="Large" FontAttributes="Bold" TextColor="Blue" /> </ContentPage.Content> </ContentPage>
ContentPage.Content
標記稱為屬性元素標記。 Content
是ContentPage的一個屬性
,並通常設置為單個視圖或帶有子視圖的布局。 通常情況下屬性變為 XAML 中的特性,但是很難將內容屬性設置為復雜對象。 因此,屬性被表示為一個XML元素,由類名和用句點分隔的屬性名組成。 現在可以在ContentPage之間設置Content屬性標簽。
此時,類、屬性和XML之間的關系應該很明顯:Xamarin.Forms類(如ContentPage或Label)作為XML元素出現在XAML文件中。類的屬性(包括ContentPage上的Title)和Label的七個屬性通常作為XML屬性出現。
擴展
上述ContentPage.Content
標記 僅包含單個Label
的頁上,但這種情況很少。大多數ContentPage派生將Content屬性設置為某種布局(不需顯式ContentPage.Content
標記),例如StackLayout。StackLayout的子屬性被定義為類型為IList<View>,但它實際上是類型為ElementCollection<View>的對象,該集合可以用多個視圖或其他布局填充。在XAML中,這些父-子關系是用普通的XML層次結構建立的。下面是一個名為XamlPlusCodePage的新頁面的XAML文件

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="XamlSamples.XamlPlusCodePage" Title="XAML + Code Page"> <StackLayout> <Slider VerticalOptions="CenterAndExpand" /> <Label Text="A simple Label" Font="Large" HorizontalOptions="Center" VerticalOptions="CenterAndExpand" /> <Button Text="Click Me!" HorizontalOptions="Center" VerticalOptions="CenterAndExpand" /> </StackLayout> </ContentPage>
4、生成文件剖析
當xaml中定義了x:Name="" 之后,XamlSamples.MainPage.xaml.g.cs文件中會生成私有字段,如下:
該字段的聲明允許 在您管轄范圍內的分部類文件的任何地方自由使用。在運行時,在解析XAML之后分配字段。這意味着,當分部類 構造函數開始時valueLabel字段為空,但在調用InitializeComponent后它就是可用的。
InitializeComponent將控件返回給構造函數后,頁面的視覺效果就像在代碼中實例化和初始化一樣得到了構造。XAML文件不再在類中扮演任何角色。您可以以任何您想要的方式操作頁面上的這些對象,例如,通過向StackLayout添加視圖,或者將頁面的內容屬性完全設置為其他內容。您可以通過檢查頁面的內容屬性和布局子集合中的項來遍歷樹。您可以通過這種方式設置視圖的屬性等等。
自由地,它是您的頁面,而XAML只是一個構建其內容的工具。
2、基本XAML語法
XAML主要用於實例化和初始化對象。對象屬性賦值的方法:使用字符串進行簡單的復制;復雜對象賦值需要屬性元素和附加屬性的基本XAML語法特性。
1、Property Elements 屬性元素
類的屬性(property)一般設置為xml的特性(attribute):

<Label Text="Hello, XAML!" VerticalOptions="Center" FontAttributes="Bold" FontSize="Large" TextColor="Aqua" />
但是可選用屬性元素來代替它,以TextColor為例:

<Label Text="Hello, XAML!" VerticalOptions="Center" FontAttributes="Bold" FontSize="Large"> <Label.TextColor> Aqua </Label.TextColor> </Label>
這兩種指定TextColor屬性的方法在功能上是等效的,但不要對同一屬性使用這兩種方法,因為這實際上將屬性設置兩次,並且可能會模棱兩可。
Label
is an object element. It is a Xamarin.Forms object expressed as an XML element.Text
,VerticalOptions
,FontAttributes
andFontSize
are property attributes. They are Xamarin.Forms properties expressed as XML attributes.- In that final snippet,
TextColor
has become a property element. It is a Xamarin.Forms property but it is now an XML element.
對於XML解碼器,Label.TextColor只是一個普通的子元素。
然而,在XAML中,這種語法非常特殊。屬性元素的規則之一是 Label.TextColor標簽中不能出現任何其他內容,屬性元素開始和結束標記之間的內容總是定義為屬性的Value。
定義多個這樣的屬性元素,寫法上看起來好像更復雜了。但是有時還是很有用的:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="XamlSamples.GridDemoPage" Title="Grid Demo Page"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> <RowDefinition Height="100" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="100" /> </Grid.ColumnDefinitions> ... </Grid> </ContentPage>
網格有兩個屬性,分別稱為rowdefinition和columndefinition。這兩個屬性屬於RowDefinitionCollection和ColumnDefinitionCollection類型,它們是RowDefinition和ColumnDefinition對象的集合。您需要使用屬性元素語法來設置這些集合。
2、Attached Properties 附加屬性(可綁定的屬性)
上面定義了Grid的行和列,程序員還必須有某種方法來指示網格的每個子元素所在的行和列。
Grid的子元素標簽 使用以下屬性來指定其在哪個格子(行列):Grid.Row、Grid.Column,默認為0
跨多行多列的屬性:Grid.RowSpan、
Grid.ColumnSpan,默認為1

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="XamlSamples.GridDemoPage" Title="Grid Demo Page"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> <RowDefinition Height="100" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="100" /> </Grid.ColumnDefinitions> <Label Text="Autosized cell" Grid.Row="0" Grid.Column="0" TextColor="White" BackgroundColor="Blue" /> <BoxView Color="Silver" HeightRequest="0" Grid.Row="0" Grid.Column="1" /> <BoxView Color="Teal" Grid.Row="1" Grid.Column="0" /> <Label Text="Leftover space" Grid.Row="1" Grid.Column="1" TextColor="Purple" BackgroundColor="Aqua" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" /> <Label Text="Span two rows (or more if you want)" Grid.Row="0" Grid.Column="2" Grid.RowSpan="2" TextColor="Yellow" BackgroundColor="Blue" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" /> <Label Text="Span two columns" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" TextColor="Blue" BackgroundColor="Yellow" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" /> <Label Text="Fixed 100x100" Grid.Row="2" Grid.Column="2" TextColor="Aqua" BackgroundColor="Red" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" /> </Grid> </ContentPage>
僅從語法判斷,這些Grid.Row
, Grid.Column
, Grid.RowSpan
,和Grid.ColumnSpan
似乎是Grid的靜態字段或屬性,但有趣的是,Grid
不定義任何名為Row
, Column
, RowSpan
,或ColumnSpan的屬性
。
相反,Grid定義了四個可綁定屬性,分別是RowProperty、ColumnProperty、RowSpanProperty和ColumnSpanProperty。這些是可綁定屬性的特殊類型,稱為附加屬性。它們由Grid類定義,但設置在網格的子級上。
當您希望在代碼中使用這些附加屬性時,Grid類提供了名為SetRow、GetColumn等的靜態方法。但在XAML中,這些附加屬性使用簡單的屬性名稱設置為網格子元素中的屬性。
為什么叫附加屬性?
附加屬性在XAML文件中始終可識別為屬性,其中包含用句點分隔的類名和屬性名。
它們被稱為附加屬性,因為它們由一個類(在本例中是Grid)定義,但附加到其他對象(在本例中是Grid的子對象)。在布局期間,網格可以查詢這些附加屬性的值,以知道將每個子元素放置在何處。
3、Content Properties 內容屬性
上面也提到,ContentPage標記之間一般用某種布局,而不是content屬性。
查看ContentPage
類的定義:
[Xamarin.Forms.ContentProperty("Content")] public class ContentPage : TemplatedPage
這意味着內容屬性元素標記不是必需的,出現在ContentPage標記開始和結束之間的任何XML內容都假定被分配給content屬性。
4、使用 OnPlatform 平台差異
在單頁應用程序中,通常在頁面上設置Padding屬性,以避免覆蓋iOS狀態欄。 在代碼中,可以為此使用Device.RuntimePlatform屬性:
if (Device.RuntimePlatform == Device.iOS) { Padding = new Thickness(0, 20, 0, 0); }
在XAML中實現:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="..."> <ContentPage.Padding> <OnPlatform x:TypeArguments="Thickness"> <On Platform="iOS" Value="0, 20, 0, 0" /> <On Platform="Android" Value="0, 0, 0, 0" /> <On Platform="UWP" Value="0, 0, 0, 0" /> </OnPlatform> </ContentPage.Padding> ... </ContentPage>
需要使用Xamarin.Forms.OnPlatform
and On
classes,
1、OnPlatform
是一個泛型類,需要指定泛型類型參數,這里,Thinckness是Padding屬性的類型。
幸運的是,有一個XAML屬性專門用於定義泛型參數,稱為x:TypeArguments, 這應該與您要設置的屬性的類型匹配。
OnPlatform
有一個IList<On> Platforms對象。 該屬性使用屬性元素標記,<OnPlatform.Platforms>
2、添加On
元素。 為每個On對象 設置Platform
屬性和Value
屬性,應用於Thickness
屬性
OnPlatform的內容屬性
是Platforms
,因此可以刪除屬性元素標記(<OnPlatform.Platforms>)
Platform
的屬性On
屬於類型IList<string>
,因此,如果值是相同的則可以包含多個平台:<On Platform="Android, UWP" Value="0, 0, 0, 0" />
3、XAML標記擴展
XAML標記擴展是XAML中的重要功能,它允許將屬性設置為從其他源間接引用的對象或值。 XAML標記擴展對於共享對象和引用整個應用程序中使用的常量特別重要,但是它們在數據綁定中找到了最大的用途。
1、概念
通常情況下,在xaml中為對象的屬性設置顯式值,例如字符串、數字、枚舉值或者在后台將值轉為的字符串。但是有時,屬性必須改為引用在其他位置定義的值,或者可能需要在運行時通過代碼進行一些處理,為此,可以使用XAML標記擴展。
XAML標記擴展不是XML的擴展,XAML是完全合法的XML。 之所以稱為“擴展”,是因為它們由實現IMarkupExtension的類中的代碼支持,可以編寫自己的自定義標記擴展。
在許多情況下,XAML標記擴展名在XAML文件中可立即識別,因為它們以大括號{ }分隔的屬性設置出現,但有時標記擴展名作為常規元素出現在標記中。
大多數賦值都是為屬性生成一個新對象,但有時需要把同一個對象賦值給兩個對象的屬性,可以考慮使用標記擴展。
所謂標記擴展,實際上是一種特殊的Attribute=value語法,特殊的地方在與Value字符串由一對花括號及其括起來的內容組成,XAML編譯器會對花括號中的內容作出解析,生成相應的對象。
eg:標記擴展 Text="{Binging ElementName=silder1,path=Value,Mode=OneWay}"
使用Binging類的實例將TextBox的Text屬性 依賴在silder的Value上。
這個語法類似C#3.0中的對象初始化語法。
2、注意
標記擴展是可以嵌套的,eg Text="{Binging Source={staticResource myDdata},Path=PersonName}"
共享的資源
某些XAML頁面包含多個視圖,這些視圖的屬性設置為相同的值。一種受歡迎的解決方案是將此類值或對象存儲為資源字典( resource dictionary)。
VisualElement類定義了一個名為ResourceDictionary類型的Resources的屬性,該屬性是具有字符串類型的鍵和類型object的值的字典。 您可以將對象放入此字典中,然后從標記中引用它們,所有這些都在XAML中。

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="XamlSamples.SharedResourcesPage" Title="Shared Resources Page"> <ContentPage.Resources> <ResourceDictionary> <LayoutOptions x:Key="horzOptions" Alignment="Center" /> <LayoutOptions x:Key="vertOptions" Alignment="Center" Expands="True" /> <x:Double x:Key="borderWidth">3</x:Double> <x:Double x:Key="rotationAngle">-15</x:Double> <OnPlatform x:Key="textColor" x:TypeArguments="Color"> <On Platform="iOS" Value="Red" /> <On Platform="Android" Value="Aqua" /> <On Platform="UWP" Value="#80FF80" /> </OnPlatform> <x:Double x:Key="fontSize">24</x:Double> </ResourceDictionary> </ContentPage.Resources> <StackLayout> <Button Text="Do this!" HorizontalOptions="{StaticResource horzOptions}" VerticalOptions="{StaticResource vertOptions}" BorderWidth="{StaticResource borderWidth}" Rotation="{StaticResource rotationAngle}" TextColor="{StaticResource textColor}" FontSize="{StaticResource fontSize}" /> <Button Text="Do that!" HorizontalOptions="{StaticResource horzOptions}" VerticalOptions="{StaticResource vertOptions}" BorderWidth="{StaticResource borderWidth}" Rotation="{StaticResource rotationAngle}" TextColor="{StaticResource textColor}" FontSize="{StaticResource fontSize}" /> <Button Text="Do the other thing!" HorizontalOptions="{StaticResource horzOptions}" VerticalOptions="{StaticResource vertOptions}" BorderWidth="{StaticResource borderWidth}" Rotation="{StaticResource rotationAngle}" TextColor="{StaticResource textColor}" FontSize="{StaticResource fontSize}" /> </StackLayout> </ContentPage>
使用:執行標記擴展StaticResource(其內容屬性是Key),即"{StaticResource horzOptions}" 等價"{StaticResource Key=horzOptions}"
X:static 標記擴展
Static
vs StaticResource,
StaticResource
從資源字典中返回對象,而x:Static
訪問以下項之一:
- 公共靜態字段
- 公共靜態屬性
- 公共常量字段
- 枚舉成員。
定義資源字典的XAML實現 支持StaticResource標記擴展,然而x:Static是XAML的固有部分,如x前綴所示。
x:Static
標記擴展還可以從你自己的代碼中引用靜態字段或屬性,主要配置xmlns:local,自己定義的類的命名空間。

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:XamlSamples" xmlns:sys="clr-namespace:System;assembly=mscorlib" x:Class="XamlSamples.StaticConstantsPage" Title="Static Constants Page" Padding="{x:Static local:AppConstants.PagePadding}"> <StackLayout> <Label Text="Hello, XAML!" TextColor="{x:Static local:AppConstants.BackgroundColor}" BackgroundColor="{x:Static local:AppConstants.ForegroundColor}" Font="{x:Static local:AppConstants.TitleFont}" HorizontalOptions="Center" /> <BoxView WidthRequest="{x:Static sys:Math.PI}" HeightRequest="{x:Static sys:Math.E}" Color="{x:Static local:AppConstants.ForegroundColor}" HorizontalOptions="Center" VerticalOptions="CenterAndExpand" Scale="100" /> </StackLayout> </ContentPage>
其他標准標記擴展
一些標記擴展是XAML固有的,並且在Xamarin.Forms XAML文件中受支持。 其中一些並不經常使用,但在您需要時必不可少:
- 如果屬性具有非
null
值的默認值,但你想要將其設置為null
,將其設置為{x:Null}
標記擴展。 - 如果某個屬性屬於類型
Type
,你可以將其分配給Type
對象使用標記擴展{x:Type someClass}
。在<Style TargetType="{x:Type Label}">中用的多 - 可以使用x:Array標記擴展在XAML中定義數組,此標記擴展名具有名為Type的必需屬性,該屬性指示數組中元素的類型。
4、數據綁定基礎知識
數據綁定允許鏈接兩個對象的屬性,以便一個對象的更改導致另一個對象的更改。 這是一個非常有價值的工具,雖然可以完全在代碼中定義數據綁定,但是XAML提供了快捷方式和便利性。 因此,Xamarin.Forms中最重要的標記擴展之一是Binding。
數據綁定
數據綁定連接兩個對象的屬性,稱為源和目標。 在cs代碼中,需要兩個步驟:必須將目標對象的BindingContext屬性設置為源對象,並且必須在目標對象上調用SetBinding方法(通常與Binding類結合使用)以綁定該對象的屬性,該對象指向源對象的屬性。
目標屬性必須是可綁定的屬性,這意味着目標對象必須派生自BindableObject。 在線Xamarin.Forms文檔指出了哪些屬性是可綁定屬性。 Label的Text屬性與可綁定屬性TextProperty關聯
在xaml中中,也同樣是兩個步驟,只不過用Binding
標記擴展代替SetBinding
調用和Binding
類。
在XAML中定義數據綁定時,有多種方法可以設置目標對象的BindingContext。 有時,它是從代碼隱藏文件中設置的,有時是使用StaticResource或x:Static標記擴展名,有時是BindingContext屬性元素標記的內容
View-View綁定
可以定義要鏈接的同一頁面上的兩個視圖(控件)的屬性的數據綁定。 在這種情況下,設置BindingContext
的目標對象使用x:Reference
標記擴展。

<ContentPage.Content> <StackLayout> <Label x:Name="label" Text="TEXT" FontSize="40" HorizontalOptions="Center" VerticalOptions="CenterAndExpand" > <!--有多種寫法,參考官網--> <Label.Scale> <Binding Source="{x:Reference slider}" Path="Value"/> </Label.Scale> </Label> <Slider x:Name="slider" Minimum="-2" Maximum="2" VerticalOptions="CenterAndExpand" /> </StackLayout> </ContentPage.Content>
ReferenceExtension的內容屬性是Name,因此Name= 可以省略。
綁定和集合
沒有什么比模板ListView更好地說明XAML和數據綁定的功能了。
ListView定義了IEnumerable類型的ItemsSource屬性,並顯示該集合中的items。 這些項目可以是任何類型的對象。 默認情況下,ListView使用每個項目的ToString方法顯示該項目。 有時,這正是您想要的,但是在許多情況下,ToString僅返回對象的完全合格的類名稱。
但是,可以通過使用模板以所需的任何方式顯示ListView集合中的項目,該模板涉及從Cell派生的類。 將為ListView中的每個項目克隆模板,並將在模板上設置的數據綁定轉移到各個克隆。

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:XamlSamples;assembly=XamlSamples" x:Class="XamlSamples.ListViewDemoPage" Title="ListView Demo Page"> <ListView ItemsSource="{x:Static local:NamedColor.All}" /> </ContentPage>
ListView在處理可能在基礎數據中動態發生的更改方面非常復雜,但前提是您采取了某些步驟。 如果分配給ListView的ItemsSource屬性的項目集合在運行時發生更改(即,可以將項目添加到集合中或從集合中刪除),請對這些項目使用ObservableCollection類。 ObservableCollection實現INotifyCollectionChanged接口,並且ListView將為CollectionChanged事件安裝一個處理程序。
5、從數據綁定到 MVVM
XAML發明了Model-View-ViewModel(MVVM)體系結構模式,該模式在三個軟件層之間進行了分隔-XAML用戶界面,稱為View; 基礎數據,稱為模型; 在視圖和模型之間的中介稱為ViewModel。 View和ViewModel通常通過XAML文件中定義的數據綁定連接。 View的BindingContext通常是ViewModel的實例。
簡單的ViewModel
先來看一下不使用Viewmodel的程序:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:sys="clr-namespace:System;assembly=mscorlib" x:Class="XamlSamples.OneShotDateTimePage" Title="One-Shot DateTime Page"> <StackLayout BindingContext="{x:Static sys:DateTime.Now}" HorizontalOptions="Center" VerticalOptions="Center"> <Label Text="{Binding Year, StringFormat='The year is {0}'}" /> <Label Text="{Binding StringFormat='The month is {0:MMMM}'}" /> <Label Text="{Binding Day, StringFormat='The day is {0}'}" /> <Label Text="{Binding StringFormat='The time is {0:T}'}" /> </StackLayout> </ContentPage>
BindingContext
是一個非常特殊的屬性:在元素BindingContext
上設置時,該元素的所有子級都將繼承該元素。 這意味着StackLayout的所有子級具有此相同BindingContext
,並且它們可以包含簡單綁定到該對象的屬性。
上例,有兩個子級似乎缺少綁定路徑的綁定,這意味着DateTime.Now
本身的值用於StringFormat。
考慮MVVM時,Model和ViewModel是完全用代碼編寫的類。 視圖通常是一個XAML文件,它通過數據綁定引用ViewModel中定義的屬性。
ViewModel通常實現INotifyPropertyChanged接口,這意味着該類的任何一個屬性更改時都會觸發PropertyChanged事件。 Xamarin.Forms中的數據綁定機制將處理程序附加到此PropertyChanged事件,以便可以在屬性更改時得到通知,並使用新值更新目標。
xaml:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:XamlSamples;assembly=XamlSamples" x:Class="XamlSamples.ClockPage" Title="Clock Page"> <Label Text="{Binding DateTime, StringFormat='{0:T}'}" FontSize="Large" HorizontalOptions="Center" VerticalOptions="Center"> <Label.BindingContext> <local:ClockViewModel /> </Label.BindingContext> </Label> </ContentPage>
viewModel:

using System; using System.ComponentModel; using Xamarin.Forms; namespace XamlSamples { class ClockViewModel : INotifyPropertyChanged { DateTime dateTime; public event PropertyChangedEventHandler PropertyChanged; public ClockViewModel() { this.DateTime = DateTime.Now; Device.StartTimer(TimeSpan.FromSeconds(1), () => { this.DateTime = DateTime.Now; return true; }); } public DateTime DateTime { set { if (dateTime != value) { dateTime = value; if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs("DateTime")); } } } get { return dateTime; } } } }
使用ViewModels命令
在許多情況下,MVVM模式僅限於數據項的操作:ViewModel中的數據對象與View中的用戶界面對象並行。
但是,有時View需要包含觸發ViewModel中各種動作的按鈕。 但是ViewModel不能包含按鈕的Clicked處理程序,因為這會將ViewModel綁定到特定的用戶界面范例。
為了使ViewModels更加獨立於特定的用戶界面對象,但仍允許在ViewModel中調用方法,可以用一個命令接口。 Xamarin.Forms中的以下元素支持此命令接口:
Button
MenuItem
ToolbarItem
SearchBar
TextCell
(and hence alsoImageCell
)ListView
TapGestureRecognizer
除了SearchBar和ListView元素外,這些元素定義了兩個屬性(可綁定屬性):
Command
of typeSystem.Windows.Input.ICommand
CommandParameter
of typeObject
SearchBar
定義了SearchCommand和
SearchCommandParameter
屬性,而ListView
定義了 ICommand
類型的屬性RefreshCommand。
ICommand
接口定義兩個方法和一個事件:
void Execute(object arg) 該方法封裝了操作本身
bool CanExecute(object arg) 指示是否可以調用該命令,false則置灰
event EventHandler CanExecuteChanged 在發生影響命令是否應執行的更改時引發
ViewModel可以定義ICommand類型的屬性, 然后,可以將這些屬性綁定到每個Button或其他元素的Command屬性,或者綁定到實現此接口的自定義視圖。 您可以選擇設置CommandParameter屬性以標識綁定到此ViewModel屬性的單個Button對象(或其他元素)。 在內部,每當用戶單擊Button時,Button就會調用Execute方法,並將其CommandParameter傳遞給Execute方法。
CanExecute方法和CanExecuteChanged事件用於按鈕單擊當前可能無效的情況,在這種情況下,按鈕應禁用自身。 首次設置Command屬性時以及每當觸發CanExecuteChanged事件時,Button都會調用CanExecute。 如果CanExecute返回false,則Button會自行禁用並且不會生成Execute調用。
為了幫助向ViewModel添加命令,Xamarin.Forms定義了兩個實現ICommand的類:Command和Command <T>,其中T是Execute和CanExecute的參數類型(可為null)。 這兩個類定義了幾個構造函數,以及一個ChangeCanExecute方法,ViewModel可以調用該方法來強制Command對象觸發CanExecuteChanged事件。
在視圖模型內,對於ICommand類型的視圖模型中的每個公共屬性,應該有一個Command或Command <T>類型的對象。 Command或Command <T>構造函數需要一個Action回調對象,該對象在調用ICommand.Execute方法時被調用。 CanExecute方法是一個可選的構造函數參數,並且是一個返回布爾值的Func。

using System; using System.ComponentModel; using System.Windows.Input; using Xamarin.Forms; namespace XamlSamples { class KeypadViewModel : INotifyPropertyChanged { string inputString = ""; string displayText = ""; char[] specialChars = { '*', '#' }; public event PropertyChangedEventHandler PropertyChanged; // Constructor public KeypadViewModel() { AddCharCommand = new Command<string>((key) => { // Add the key to the input string. InputString += key; }); DeleteCharCommand = new Command(() => { // Strip a character from the input string. InputString = InputString.Substring(0, InputString.Length - 1); }, () => { // Return true if there's something to delete. return InputString.Length > 0; }); } // Public properties public string InputString { protected set { if (inputString != value) { inputString = value; OnPropertyChanged("InputString"); DisplayText = FormatText(inputString); // Perhaps the delete button must be enabled/disabled. ((Command)DeleteCharCommand).ChangeCanExecute(); } } get { return inputString; } } public string DisplayText { protected set { if (displayText != value) { displayText = value; OnPropertyChanged("DisplayText"); } } get { return displayText; } } // ICommand implementations public ICommand AddCharCommand { protected set; get; } public ICommand DeleteCharCommand { protected set; get; } string FormatText(string str) { bool hasNonNumbers = str.IndexOfAny(specialChars) != -1; string formatted = str; if (hasNonNumbers || str.Length < 4 || str.Length > 10) { } else if (str.Length < 8) { formatted = String.Format("{0}-{1}", str.Substring(0, 3), str.Substring(3)); } else { formatted = String.Format("({0}) {1}-{2}", str.Substring(0, 3), str.Substring(3, 3), str.Substring(6)); } return formatted; } protected void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } }
通過使用Command <T>類實例化命令,可以將參數傳遞給Execute和CanExecute操作。 例如,以下代碼顯示如何使用Command <T>實例指示NavigateAsync方法將需要字符串類型的參數。
public ICommand NavigateCommand => new Command<string>(NavigateAsync);
在Command和Command <T>類中,每個構造函數中CanExecute方法的委托都是可選的。 如果未指定委托,則Command將為CanExecute返回true。 但是,視圖模型可以通過在Command對象上調用ChangeCanExecute方法來指示命令的CanExecute狀態更改。 這將引發CanExecuteChanged事件。 然后,UI中與該命令綁定的所有控件都將更新其啟用狀態,以反映數據綁定命令的可用性。
調用異步方法
命令還可以調用異步方法。 這是通過在指定Execute方法時使用async和await關鍵字來實現的:

DownloadCommand = new Command (async () => await DownloadAsync ()); async Task DownloadAsync () { await Task.Run (() => Download ()); } void Download () { ... }
XAML 控件
視圖是用戶界面對象,例如標簽,按鈕和滑塊,在其他圖形編程環境中通常稱為控件或窗口小部件。 Xamarin.Forms支持的視圖都是從View類派生的。
Xamarin.Forms中定義的所有視圖都可以從XAML文件中引用
XAML編譯
可以選擇使用XAML編譯器(XAMLC)將XAML直接編譯為中間語言(IL)。
XAML編譯具有許多優點:
- 它執行XAML的編譯時檢查,並通知用戶任何錯誤。
- 它消除了XAML元素的某些加載和實例化時間。
- 它不再包含.xaml文件,有助於減小最終程序集的文件大小。
默認情況下,禁用XAML編譯以確保向后兼容。 通過添加XamlCompilation屬性,可以在程序集和類級別啟用它。
以下代碼示例演示了在程序集級別啟用XAML編譯:(AssemblyInfo.cs)
using Xamarin.Forms.Xaml; [assembly: XamlCompilation(XamlCompilationOptions.Compile)]
在此示例中,將對程序集中包含的所有XAML進行編譯時檢查,並在編譯時而不是在運行時報告XAML錯誤。 XamlCompilation屬性的前綴assembly 指定該屬性適用於整個程序集。
下面的代碼示例演示了在類級別啟用 XAML 編譯:
using Xamarin.Forms.Xaml; ... [XamlCompilation (XamlCompilationOptions.Compile)] public class HomePage : ContentPage { ... }
XAML工具
XAML熱重載
XAML Hot Reload可插入您現有的工作流程中,以提高工作效率並節省時間。 如果沒有XAML Hot Reload,則每次看到XAML更改時都必須構建和部署應用程序。 使用熱重裝,當您保存XAML文件時,更改將實時反映在您正在運行的應用程序中。 此外,您的導航狀態和數據將得到維護,使您可以快速在UI上進行迭代,而不會丟失您在應用程序中的位置。 因此,借助XAML Hot Reload,您將花費更少的時間來重建和部署應用程序以驗證UI更改。
啟用
使用 XAML 熱重載不需要額外的安裝或設置。 它內置於 Visual Studio 中,並可在 IDE 設置中啟用。 啟用后,可以通過在模擬器、模擬器或物理設備上調試應用,開始使用 XAML 熱重載。
On Windows, XAML Hot Reload can be enabled by checking the Enable Xamarin Hot Reload checkbox at Tools > Options > Xamarin > Hot Reload.
彈性加載
如果您更改了XAML 但Hot Reload無法重新加載,它將使用IntelliSense向您顯示一條錯誤消息。 這些更改稱為粗魯的編輯,包括錯誤地輸入XAML或將控件連接到不存在的事件處理程序。 即使進行粗魯的編輯,您也可以繼續重新加載而無需重新啟動應用程序-在XAML文件中的其他位置進行其他更改,然后單擊保存。 粗魯的編輯將不會重新加載,但您的其他更改將繼續應用。
已知的限制
- 在 XAML 熱重載會話期間,無法添加、刪除或重命名文件或 NuGet 包。 如果添加或刪除文件或 NuGet 包,請重新生成並重新部署應用,以便繼續使用 XAML 熱重載。
- 將鏈接器設置為 "不鏈接" 以獲得最佳體驗。 僅限鏈接 SDK設置工作正常,但在某些情況下可能會失敗。
- 在物理 iPhone 上調試需要解釋器使用 XAML 熱重載。 向 iOS 生成設置中的 "其他 mtouch 參數" 字段添加 --解釋器以使用 XAML 熱重載。
- 通過使用
x:Name
值將控件分配給另一個字段或屬性而創建的任何引用都不會重新加載。 - 在AppShell中更新 Shell 應用程序的可視層次結構可能會導致應用程序的狀態保持有問題。 重新生成應用程序以繼續重新加載。
- XAML 熱重載無法重新C#加載代碼,包括事件處理程序、自定義控件、頁代碼隱藏以及其他類。
XAML預覽程序
設置:Tools > Options > Xamarin > Forms Previewer
在非程序運行而是在編輯時,XAML 預覽器會顯示你的 Xamarin. Forms XAML 頁面在 iOS 和 Android 上的外觀。 當你對 XAML 進行更改時, 你會立即看到它們與你的代碼一起改變。
設計時數據
沒有數據,某些布局很難可視化。 使用這些技巧可以最大程度地利用XAML預覽器中的數據量繁重的頁面。
設計時數據是為使控件在XAML預覽器中更容易可視化而設置的偽造數據。 首先,將以下代碼行添加到XAML頁面的標題中:
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
添加名稱空間后,可以將d:放在任何屬性或控件的前面,以在XAML預覽器中顯示它。 帶有d:的元素不會在運行時顯示。
例如,您可以將文本添加到通常綁定了數據的標簽。
<Label Text="{Binding Name}" d:Text="Name!" />
可以將d:與Xamarin.Forms控件的任何屬性一起使用,例如顏色,字體大小和間距。 您甚至可以將其添加到控件本身:
<d:Button Text="Design Time Button" />
在此示例中,按鈕僅在設計時出現。 使用此方法將占位符放入XAML預覽器不支持的自定義控件。
如果不想將設計時數據添加到各個控件,可以設置模擬數據存儲以綁定到頁面。
命名空間
其他功能
1、可綁定屬性
在Xamarin.Forms中,可綁定屬性擴展了公共語言運行時(CLR)屬性的功能。 可綁定屬性是一種特殊的屬性,其中Xamarin.Forms屬性系統會跟蹤該屬性的值。 本文提供了可綁定屬性的介紹,並演示了如何創建和使用它們。
可綁定屬性通過備份具有BindableProperty類型的屬性,而不是通過字段支持屬性來擴展CLR屬性功能。
可綁定屬性的目的是提供一個屬性系統,該系統支持通過父子關系設置的數據綁定,樣式,模板和值。 此外,可綁定屬性可以提供默認值,屬性值驗證以及監視屬性更改的回調。
應將屬性實現為可綁定屬性,以支持以下一項或多項功能:
- 充當數據綁定的有效目標屬性。
- 通過樣式設置屬性。
- 提供與屬性類型的默認值不同的默認屬性值。
- 驗證屬性的值。
- 監視屬性更改。
Xamarin.Forms可綁定屬性的示例包括Label.Text,Button.BorderRadius和StackLayout.Orientation。 每個可綁定屬性都有一個對應的BindableProperty類型的公共靜態只讀屬性,該屬性公開在同一類上,並且是可綁定屬性的標識符。 例如,Label.Text屬性的相應可綁定屬性標識符是Label.TextProperty。
1.1、創建和使用可綁定屬性
步驟:
1、使用BindableProperty.Create方法重載之一創建BindableProperty實例。
2、為BindableProperty實例 定義屬性訪問器(get、set)
請注意,必須在UI線程上創建所有BindableProperty實例。 這意味着只有在UI線程上運行的代碼才能獲取或設置可綁定屬性的值。 但是,可以使用Device.BeginInvokeOnMainThread方法將其(BindableProperty)綁定到UI線程,從而從其他線程訪問BindableProperty實例。
1.2、創建屬性
要創建BindableProperty實例,包含類必須從BindableObject類派生。 但是,BindableObject類在類層次結構中較高,因此用於用戶界面功能的大多數類都支持可綁定屬性。
定義如下:
創建BindableProperty時,至少必須指定一個標識符【下面的EventNameProperty】以及以下參數:
public static readonly BindableProperty EventNameProperty = BindableProperty.Create ("EventName", typeof(string), typeof(EventToCommandBehavior), null);
這將創建一個名為EventName的BindableProperty實例,其類型為string,該屬性歸EventToCommandBehavior類所有,其默認值為null。
可綁定屬性的命名約定是,可綁定屬性標識符必須與Create方法中指定的屬性名稱匹配,並在屬性名稱后附加“ Property”。 因此,在上面的示例中,可綁定屬性標識符是EventNameProperty。
當然還可以指定其他參數:
1、檢測屬性更改:propertyChanged參數,可以將靜態屬性已更改的回調方法注冊為可綁定屬性,當bindable屬性的值更改時,將調用指定的回調方法。
以下將OnEventNameChanged方法注冊為屬性更改的回調方法:

public static readonly BindableProperty EventNameProperty = BindableProperty.Create ( "EventName", typeof(string), typeof(EventToCommandBehavior), null, propertyChanged: OnEventNameChanged); ... static void OnEventNameChanged (BindableObject bindable, object oldValue, object newValue) { // Property changed implementation goes here }
2、驗證回調:validateValue參數
指定validateValue參數,可以向可綁定屬性注冊靜態驗證回調方法,設置bindable屬性的值時,將調用指定的回調方法。
下面的代碼示例演示Angle可綁定屬性如何將IsValidValue方法注冊為驗證回調方法:

public static readonly BindableProperty AngleProperty = BindableProperty.Create ("Angle", typeof(double), typeof(HomePage), 0.0, validateValue: IsValidValue); ... static bool IsValidValue (BindableObject view, object value) { double result; bool isDouble = double.TryParse (value.ToString (), out result); return (result >= 0 && result <= 360); }
驗證回調具有一個值,如果該值對該屬性有效,則應返回true,否則返回false。 如果驗證回調返回false,則將引發異常,此異常應由開發人員處理。
驗證回調方法的典型用法是在設置bindable屬性時限制整數或雙精度值。 例如,IsValidValue方法檢查屬性值是否為0到360范圍內的雙精度值。
.......
1.3、創建訪問器
屬性訪問器需要使用屬性語法來訪問可綁定的屬性。
Get
訪問器應返回相應的可綁定屬性中包含的值,這可以通過調用GetValue方法,傳入要獲取值的可綁定屬性標識符,然后將結果轉換為所需的類型來實現。
Set訪問器應設置相應可綁定屬性的值,這可以通過調用SetValue方法,傳入要設置值的可綁定屬性標識符以及要設置的值來實現。
public string EventName { get { return (string)GetValue (EventNameProperty); } set { SetValue (EventNameProperty, value); } }
1.4、使用可綁定屬性
一旦創建可綁定屬性后,可以從 XAML 或代碼使用它。

<ContentPage ... xmlns:local="clr-namespace:EventToCommandBehavior" ...> ... <ListView ...> <ListView.Behaviors> <local:EventToCommandBehavior EventName="ItemSelected" ... /> </ListView.Behaviors> </ListView>
2、附加屬性
附加屬性是一種特殊的可綁定屬性,在一個類中定義但附加到其他對象,並且在XAML中可識別為包含類和以句點分隔的屬性名稱的屬性。
附加屬性 使對象可以為其自己的類未定義的屬性分配值。 例如,子元素可以使用附加的屬性來通知其父元素如何在用戶界面中呈現它們。Grid控件允許通過設置Grid.Row和Grid.Column附加屬性來指定子項的行和列。Grid.Row和Grid.Column是附加屬性,因為它們是在作為Grid的子元素的元素上設置的,而不是在Grid本身上設置的。
在以下情況下,應將可綁定屬性實現為附加屬性:
- 需要除定義類之外的其他類可使用的屬性設置機制。
- 當類表示需要與其他類輕松集成的服務時。
2.1、創建和使用附加的屬性
- 使用CreateAttached方法重載之一創建BindableProperty實例。
- 提供靜態GetPropertyName和SetPropertyName方法作為附加屬性的訪問器。
創建用於其他類型的附加屬性時,創建屬性的類不必從BindableObject派生。 但是,訪問器的目標屬性應該是BindableObject或從BindableObject派生。
可以通過聲明BindableProperty類型的公共靜態只讀屬性來創建附加屬性。 應將bindable屬性設置為BindableProperty.CreateAttached方法重載之一的返回值。 該聲明應在所屬類的主體之內,但不屬於任何成員定義。
以下代碼顯示附加屬性的示例:
public static readonly BindableProperty HasShadowProperty = BindableProperty.CreateAttached ("HasShadow", typeof(bool), typeof(ShadowEffect), false);
訪問器
需要使用靜態GetPropertyName和SetPropertyName方法作為附加屬性的訪問器,否則屬性系統將無法使用附加屬性。 GetPropertyName訪問器應符合以下簽名:
public static valueType GetPropertyName(BindableObject target)
GetPropertyName訪問器應返回附加屬性的相應BindableProperty字段中包含的值,這可以通過調用GetValue方法,傳入要獲取值的可綁定屬性標識符,然后將結果值轉換為所需的類型來實現。
public static bool GetHasShadow (BindableObject view) { return (bool)view.GetValue (HasShadowProperty); } public static void SetHasShadow (BindableObject view, bool value) { view.SetValue (HasShadowProperty, value); }
典型用法,效果。
3、資源字典 ResourceDictionary
XAML資源是可以在整個Xamarin.Forms應用程序中共享和重用的對象的定義,這些資源對象存儲在資源字典中。
ResourceDictionary是Xamarin.Forms應用程序使用的資源的存儲庫,存儲在ResourceDictionary中的典型資源包括樣式、控件模板、數據模板、顏色和轉換器。
在XAML中,然后可以使用StaticResource標記擴展來檢索存儲在ResourceDictionary中的資源並將其應用於元素。
在C#中,也可以在ResourceDictionary中定義資源,然后使用基於字符串的索引器來檢索資源並將其應用於元素,但是,在C#中使用ResourceDictionary幾乎沒有優勢,因為共享對象可以簡單地存儲為字段或屬性,並且可以直接訪問而無需先從字典中檢索它們。
創建和使用 ResourceDictionary
資源在ResourceDictionary中定義,然后將其設置為以下“資源”屬性之一:
- 從Application派生的任何類的Resources屬性
- 從VisualElement派生的任何類的Resources屬性
Xamarin.Forms程序僅包含一個從Application派生的類,但經常使用許多從VisualElement派生的類,包括頁面,布局和控件。這些對象中的任何一個都可以將其Resources屬性設置為ResourceDictionary。選擇放置特定ResourceDictionary的位置會影響可以使用資源的位置:
- 附加到諸如Button或Label之類的視圖的ResourceDictionary中的資源只能應用於該特定對象,因此這不是很有用。
- 附加到諸如StackLayout或Grid之類的布局的ResourceDictionary中的資源可以應用於該布局以及該布局的所有子級。
- 在頁面級別定義的ResourceDictionary中的資源可以應用於頁面及其所有子級。
- 在應用程序級別定義的ResourceDictionary中的資源可以在整個應用程序中應用。
<Application ...> <Application.Resources> <ResourceDictionary> <Color x:Key="PageBackgroundColor">Yellow</Color> <Color x:Key="HeadingTextColor">Black</Color> <Color x:Key="NormalTextColor">Blue</Color> <Style x:Key="LabelPageHeadingStyle" TargetType="Label"> <Setter Property="FontAttributes" Value="Bold" /> <Setter Property="HorizontalOptions" Value="Center" /> <Setter Property="TextColor" Value="{StaticResource HeadingTextColor}" /> </Style> </ResourceDictionary> </Application.Resources> </Application>
此ResourceDictionary定義了三個Color資源和一個Style資源。
注:特定於單個頁面的資源不應包含在應用程序級資源字典中,因為這樣的資源將在應用程序啟動時進行解析,而不是在頁面需要時進行解析。 有關更多信息,請參見減小應用程序資源字典大小。
資源覆蓋
當ResourceDictionary資源共享x:Key屬性值時,在視圖層次結構中定義較低的資源將優先於定義較高的資源。 例如,在應用程序級別將PageBackgroundColor資源設置為Blue 將被設置為Yellow的頁面級別PageBackgroundColor資源覆蓋。 同樣,頁面級別的PageBackgroundColor資源將被控件級別的PageBackgroundColor資源覆蓋。
考慮ResourceDictionary優先級的另一種方法:當XAML解析器遇到StaticResource時,它將使用找到的第一個匹配項,通過在可視樹中向上移動來搜索匹配項。 如果此搜索在頁面結束,並且仍未找到鍵,則XAML解析器將搜索附加到App對象的ResourceDictionary。 如果仍然找不到該鍵,則會引發異常。
獨立的資源字典
從ResourceDictionary派生的類也可以位於單獨的獨立文件中。 (更確切地說,從ResourceDictionary派生的類通常需要一對文件,因為資源是在XAML文件中定義的,但是還需要一個帶有InitializeComponent調用的代碼隱藏文件。)然后,可以在應用程序之間共享結果文件。
要創建這樣的文件,請將新的“內容視圖”或“內容頁面”項添加到項目中(而不是僅包含C#文件的“內容視圖”或“內容頁面”)。 在XAML文件和C#文件中,將基類的名稱從ContentView或ContentPage更改為ResourceDictionary。 在XAML文件中,基類的名稱是頂級元素。
合並資源字典到xaml文件
合並的資源字典將一個或多個ResourceDictionary對象組合到另一個ResourceDictionary中。
合並本地資源字典
通過將Source屬性設置為帶有資源的XAML文件的文件名,可以將本地ResourceDictionary合並到另一個ResourceDictionary中:
<ContentPage ...> <ContentPage.Resources> <!-- Add more resources here --> <ResourceDictionary Source="MyResourceDictionary.xaml" /> <!-- Add more resources here --> </ContentPage.Resources> ... </ContentPage>
此語法不會實例化MyResourceDictionary類。 而是,它引用XAML文件。 因此,在設置Source屬性時,不需要代碼隱藏文件(MyResourceDictionary.xaml.cs),並且可以從MyResourceDictionary.xaml文件的根標記中刪除x:Class屬性。 另外,當使用這種方法合並資源字典時,Xamarin.Forms將自動實例化ResourceDictionary,因此不需要外部ResourceDictionary標記。