Xamarin.Forms 編譯的系結
編譯的系結比傳統系結更快速地解析,因此可改善應用程式中的數據系結效能 Xamarin.Forms 。
資料繫結有兩個主要問題:
- 無法在編譯時間驗證繫結運算式。 相反地,繫結是在執行階段進行解析。 因此,當應用程式不如預期般運作或出現錯誤訊息時,要到執行階段才會偵測到任何無效的繫結。
- 不符合成本效益。 繫結使用一般用途物件檢查 (反射) 在執行階段進行解析,而執行此作業的成本會因平台而異。
編譯的系結可藉由在編譯時期解析系結表達式,而不是運行時間,來改善應用程式中的數據系結效能 Xamarin.Forms 。 此外,在編譯時間驗證繫結運算式可改善開發人員的疑難排解體驗,因為無效的繫結會回報為建置錯誤。
編譯繫結的使用流程包括:
- 啟用 XAML 編譯。 如需 XAML 編譯的詳細資訊,請參閱 XAML 編譯。
- 將
VisualElement
上的x:DataType
屬性設定為VisualElement
及其子系將要繫結的物件類型。
注意
建議在檢視階層架構中與設定 BindingContext
相同的層級設定 x:DataType
屬性。 不過,這個屬性可以在檢視階層中的任何位置重新定義。
若要使用編譯的系結, x:DataType
屬性必須設定為字串常值,或使用標記延伸的類型 x:Type
。 在 XAML 編譯期間,任何無效的繫結運算式都會回報為建置錯誤。 不過,XAML 編譯器只會在第一次遇到無效的繫結運算式時回報建置錯誤。 定義於 VisualElement
或其子系的任何有效繫結運算式都會經過編譯,無論 BindingContext
是在 XAML 或程式碼中設定。 編譯繫結運算式會產生經過編譯的程式碼,該程式碼會從「來源」屬性取得一個值,並在標記中指定的「目標」屬性上進行設定。 此外,視繫結運算式而定,產生的程式碼可以在「來源」屬性值中觀察變更並重新整理「目標」屬性,也可以將變更從「目標」推送回到「來源」。
重要
定義 Source
屬性的任何繫結運算式目前已停用編譯的繫結。 這是因為 Source
屬性一律使用 x:Reference
標記延伸設定,因此無法在編譯時間進行解析。
使用編譯的繫結
[ 編譯的色彩選取器 ] 頁面示範在檢視和 viewmodel 屬性之間 Xamarin.Forms 使用已編譯的系結:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:DataBindingDemos"
x:Class="DataBindingDemos.CompiledColorSelectorPage"
Title="Compiled Color Selector">
...
<StackLayout x:DataType="local:HslColorViewModel">
<StackLayout.BindingContext>
<local:HslColorViewModel Color="Sienna" />
</StackLayout.BindingContext>
<BoxView Color="{Binding Color}"
... />
<StackLayout Margin="10, 0">
<Label Text="{Binding Name}" />
<Slider Value="{Binding Hue}" />
<Label Text="{Binding Hue, StringFormat='Hue = {0:F2}'}" />
<Slider Value="{Binding Saturation}" />
<Label Text="{Binding Saturation, StringFormat='Saturation = {0:F2}'}" />
<Slider Value="{Binding Luminosity}" />
<Label Text="{Binding Luminosity, StringFormat='Luminosity = {0:F2}'}" />
</StackLayout>
</StackLayout>
</ContentPage>
根 StackLayout
會在 BindingContext
屬性的屬性項目標記中具現化 HslColorViewModel
並初始化 Color
屬性。 這個根目錄 StackLayout
也會將 屬性定義為 x:DataType
viewmodel 類型,表示將編譯根 StackLayout
檢視階層中的任何系結運算式。 您可以藉由變更任何系結表達式以系結至不存在的 viewmodel 屬性來驗證此情況,這會導致建置錯誤。 雖然這個範例會將 x:DataType
屬性設定為字串常值,但它也可以設定為具有標記延伸的類型 x:Type
。 如需標記延伸的詳細資訊 x:Type
,請參閱 x:Type 標記延伸。
重要
您可以在檢視階層架構中的任何位置重新定義 x:DataType
屬性。
BoxView
、Label
項目和 Slider
檢視會繼承 StackLayout
的繫結內容。 這些檢視都是參考 viewmodel 中來源屬性的系結目標。 BoxView.Color
針對 屬性和 Label.Text
屬性,數據系結為 OneWay
– 檢視中的屬性會從 viewmodel 中的屬性設定。 不過,Slider.Value
屬性使用 TwoWay
繫結。 這可讓每一個 Slider
都從 viewmodel 進行設定,也允許從每個 Slider
設定 viewmodel。
第一次執行應用程式時,BoxView
Label
會根據 ViewModel 具現化時所設定的初始Color
屬性,從 viewmodel 設定 、 元素和Slider
元素。 如下列螢幕擷取畫面所示:
當您操作滑桿時,BoxView
和 Label
項目會隨之更新。
如需此色彩選取器的詳細資訊,請參閱 ViewModel 和屬性變更通知。
在 DataTemplate 中使用已編譯的系結
DataTemplate
中的繫結是在所要樣板化的物件內容中進行解譯。 因此,在 DataTemplate
中使用編譯的繫結時,DataTemplate
必須使用 x:DataType
屬性來宣告其資料物件類型。
[Compiled Color List] \(編譯的色彩清單\) 頁面示範如何在 DataTemplate
中使用編譯的繫結:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:DataBindingDemos"
x:Class="DataBindingDemos.CompiledColorListPage"
Title="Compiled Color List">
<Grid>
...
<ListView x:Name="colorListView"
ItemsSource="{x:Static local:NamedColor.All}"
... >
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:NamedColor">
<ViewCell>
<StackLayout Orientation="Horizontal">
<BoxView Color="{Binding Color}"
... />
<Label Text="{Binding FriendlyName}"
... />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<!-- The BoxView doesn't use compiled bindings -->
<BoxView Color="{Binding Source={x:Reference colorListView}, Path=SelectedItem.Color}"
... />
</Grid>
</ContentPage>
ListView.ItemsSource
屬性設定為靜態 NamedColor.All
屬性。 NamedColor
類別使用 .NET 反射來列舉 Color
結構中所有的靜態公用欄位,並使用其名稱將它們儲存在可供靜態 All
屬性存取的集合中。 因此,ListView
會填入所有的 NamedColor
執行個體。 針對 ListView
中的每個項目,項目的繫結內容會設定為 NamedColor
物件。 ViewCell
中的 BoxView
和 Label
項目會繫結至 NamedColor
屬性。
請注意,DataTemplate
會將 x:DataType
屬性定義為 NamedColor
類型,表示 DataTemplate
檢視階層架構中的任何繫結運算式都會經過編譯。 若要進行驗證,請將任何繫結運算式變更為繫結至不存在的 NamedColor
屬性,這會導致建置錯誤。 雖然這個範例會將 x:DataType
屬性設定為字串常值,但它也可以設定為具有標記延伸的類型 x:Type
。 如需標記延伸的詳細資訊 x:Type
,請參閱 x:Type 標記延伸。
第一次執行應用程式時,ListView
會填入 NamedColor
執行個體。 當選取 ListView
中的項目時,BoxView.Color
屬性會設定為 ListView
中所選項目的色彩:
選取 ListView
中其他項目會更新 BoxView
的色彩。
結合編譯的系結與傳統系結
只有在已定義 x:DataType
屬性的檢視階層架構中才能編譯繫結運算式。 相反地,未定義 x:DataType
屬性之階層架構中的任何檢視則會使用傳統繫結。 因此,您可以將編譯的繫結與傳統繫結合併成一頁。 例如,在上一節中,DataTemplate
中的檢視使用所編譯繫結,而設定為 ListView
中所選色彩的 BoxView
則否。
因此,您可以謹慎建構 x:DataType
屬性,來產生同時使用編譯繫結和傳統繫結的頁面。 或者,您可以在檢視階層架構中的任何位置,使用 x:Null
標記延伸將 x:DataType
屬性重新定義為 null
。 這樣做表示檢視階層架構中的任何繫結運算式都會使用傳統繫結。 [Mixed Bindings] \(混合繫結\) 頁面會示範此方法:
<StackLayout x:DataType="local:HslColorViewModel">
<StackLayout.BindingContext>
<local:HslColorViewModel Color="Sienna" />
</StackLayout.BindingContext>
<BoxView Color="{Binding Color}"
VerticalOptions="FillAndExpand" />
<StackLayout x:DataType="{x:Null}"
Margin="10, 0">
<Label Text="{Binding Name}" />
<Slider Value="{Binding Hue}" />
<Label Text="{Binding Hue, StringFormat='Hue = {0:F2}'}" />
<Slider Value="{Binding Saturation}" />
<Label Text="{Binding Saturation, StringFormat='Saturation = {0:F2}'}" />
<Slider Value="{Binding Luminosity}" />
<Label Text="{Binding Luminosity, StringFormat='Luminosity = {0:F2}'}" />
</StackLayout>
</StackLayout>
根 StackLayout
會將 x:DataType
屬性設定為 HslColorViewModel
類型,表示根 StackLayout
檢視階層架構中的任何繫結運算式都會經過編譯。 不過,內部 StackLayout
會使用 x:Null
標記延伸將 x:DataType
屬性重新定義為 null
。 因此,內部 StackLayout
中的繫結運算式會使用傳統繫結。 只有根 StackLayout
檢視階層架構中的 BoxView
會使用所編譯繫結。
如需 x:Null
標記延伸的詳細資訊,請參閱 x:Null 標記延伸。
效能
編譯的繫結可提升資料繫結效能,但效能優點各異。 單元測試顯示:
- 使用屬性變更通知之編譯繫結 (例如
OneWay
、OneWayToSource
或TwoWay
繫結) 的解析速度,大致上比傳統繫結快 8 倍。 - 不使用屬性變更通知之編譯繫結 (例如
OneTime
繫結) 的解析速度,大致上比傳統繫結快 20 倍。 - 在使用屬性變更通知的編譯繫結 (例如
OneWay
、OneWayToSource
或TwoWay
繫結) 上設定BindingContext
,大致上比在傳統繫結上設定BindingContext
快 5 倍。 - 在不使用屬性變更通知的編譯繫結 (例如
OneTime
繫結) 上設定BindingContext
,大致上比在傳統繫結上設定BindingContext
快 7 倍。
這些效能差異在行動裝置上可能會更大,視所使用的平台、所使用的作業系統版本,以及執行應用程式的裝置而定。