Xamarin.Forms 編譯的系結

Download Sample 下載範例

編譯的系結比傳統系結更快速地解析,因此可改善應用程式中的數據系結效能 Xamarin.Forms 。

資料繫結有兩個主要問題:

  1. 無法在編譯時間驗證繫結運算式。 相反地,繫結是在執行階段進行解析。 因此,當應用程式不如預期般運作或出現錯誤訊息時,要到執行階段才會偵測到任何無效的繫結。
  2. 不符合成本效益。 繫結使用一般用途物件檢查 (反射) 在執行階段進行解析,而執行此作業的成本會因平台而異。

編譯的系結可藉由在編譯時期解析系結表達式,而不是運行時間,來改善應用程式中的數據系結效能 Xamarin.Forms 。 此外,在編譯時間驗證繫結運算式可改善開發人員的疑難排解體驗,因為無效的繫結會回報為建置錯誤。

編譯繫結的使用流程包括:

  1. 啟用 XAML 編譯。 如需 XAML 編譯的詳細資訊,請參閱 XAML 編譯
  2. 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 屬性。

BoxViewLabel 項目和 Slider 檢視會繼承 StackLayout 的繫結內容。 這些檢視都是參考 viewmodel 中來源屬性的系結目標。 BoxView.Color針對 屬性和 Label.Text 屬性,數據系結為 OneWay – 檢視中的屬性會從 viewmodel 中的屬性設定。 不過,Slider.Value 屬性使用 TwoWay 繫結。 這可讓每一個 Slider 都從 viewmodel 進行設定,也允許從每個 Slider設定 viewmodel。

第一次執行應用程式時,BoxViewLabel會根據 ViewModel 具現化時所設定的初始Color屬性,從 viewmodel 設定 、 元素和Slider元素。 如下列螢幕擷取畫面所示:

Compiled Color Selector

當您操作滑桿時,BoxViewLabel 項目會隨之更新。

如需此色彩選取器的詳細資訊,請參閱 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 中的 BoxViewLabel 項目會繫結至 NamedColor 屬性。

請注意,DataTemplate 會將 x:DataType 屬性定義為 NamedColor 類型,表示 DataTemplate 檢視階層架構中的任何繫結運算式都會經過編譯。 若要進行驗證,請將任何繫結運算式變更為繫結至不存在的 NamedColor 屬性,這會導致建置錯誤。 雖然這個範例會將 x:DataType 屬性設定為字串常值,但它也可以設定為具有標記延伸的類型 x:Type 。 如需標記延伸的詳細資訊 x:Type ,請參閱 x:Type 標記延伸

第一次執行應用程式時,ListView 會填入 NamedColor 執行個體。 當選取 ListView 中的項目時,BoxView.Color 屬性會設定為 ListView 中所選項目的色彩:

Compiled Color List

選取 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 標記延伸

效能

編譯的繫結可提升資料繫結效能,但效能優點各異。 單元測試顯示:

  • 使用屬性變更通知之編譯繫結 (例如 OneWayOneWayToSourceTwoWay 繫結) 的解析速度,大致上比傳統繫結快 8 倍。
  • 不使用屬性變更通知之編譯繫結 (例如 OneTime 繫結) 的解析速度,大致上比傳統繫結快 20 倍。
  • 在使用屬性變更通知的編譯繫結 (例如 OneWayOneWayToSourceTwoWay 繫結) 上設定 BindingContext,大致上比在傳統繫結上設定 BindingContext 快 5 倍。
  • 在不使用屬性變更通知的編譯繫結 (例如 OneTime 繫結) 上設定 BindingContext,大致上比在傳統繫結上設定 BindingContext 快 7 倍。

這些效能差異在行動裝置上可能會更大,視所使用的平台、所使用的作業系統版本,以及執行應用程式的裝置而定。