Xamarin.Forms 基本系結

Xamarin.Forms數據系結會連結兩個對象之間的一組屬性,其中至少一個通常是使用者介面物件。 這兩個物件稱為「目標」和「來源」

  • 「目標」是資料繫結設定所在的物件 (和屬性)。
  • 「來源」是資料繫結參考的物件 (和屬性)。

這項區別有時可能有點令人困惑:在最簡單的情況下,資料會從來源流向目標,這表示目標屬性值會從來源屬性值設定。 不過,在某些情況下,資料可能會從目標流向來源,或是雙向流動。 為了避免混淆,請記住,目標一律是資料繫結設定所在的物件,即使它是提供資料而不是接收資料。

具有繫結內容的繫結

雖然資料繫結通常會完全以 XAML 指定,但在程式碼中看到資料繫結也很有意義。 [基本程式碼繫結] 頁面包含具有 LabelSlider 的 XAML 檔案:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="DataBindingDemos.BasicCodeBindingPage"
             Title="Basic Code Binding">
    <StackLayout Padding="10, 0">
        <Label x:Name="label"
               Text="TEXT"
               FontSize="48"
               HorizontalOptions="Center"
               VerticalOptions="CenterAndExpand" />

        <Slider x:Name="slider"
                Maximum="360"
                VerticalOptions="CenterAndExpand" />
    </StackLayout>
</ContentPage>

Slider 設定為 0 到 360 的範圍。 此程式的目的是藉由操作 Slider 而旋轉 Label

如果沒有資料繫結,您會將 SliderValueChanged 事件設定為事件處理常式,以存取 SliderValue 屬性,並將該值設定為 LabelRotation 屬性。 資料繫結會自動執行該作業;已不再需要事件處理常式和其內的程式碼。

您可以在衍生自 BindableObject 的任何類別執行個體上設定繫結,其中包括 ElementVisualElementViewView 衍生項目。 繫結一律會在目標物件上設定。 繫結會參考來源物件。 若要設定資料繫結,請使用目標類別的下列兩個成員:

在此範例中,Label 是繫結目標,而 Slider 是繫結來源。 Slider 來源中的變更會影響 Label 目標的旋轉。 資料會從來源流向目標。

BindableObject 所定義的 SetBinding 方法具有 BindingBase 類型的引數,而 Binding 類別衍生自該處,但有 BindableObjectExtensions 類別定義的其他 SetBinding 方法。 基本程式碼繫結範例中的程式碼後置檔案,使用來自此類別的較簡單 SetBinding 延伸模組方法。

public partial class BasicCodeBindingPage : ContentPage
{
    public BasicCodeBindingPage()
    {
        InitializeComponent();

        label.BindingContext = slider;
        label.SetBinding(Label.RotationProperty, "Value");
    }
}

Label 物件是繫結目標,因此它是這個屬性設定所在的物件,也是對其呼叫方法的物件。 BindingContext 屬性指出繫結來源,也就是 Slider

SetBinding 方法在繫結目標上呼叫,但它同時指定目標屬性和來源屬性。 目標屬性指定為 BindableProperty 物件:Label.RotationProperty。 來源屬性指定為字串,並指出 SliderValue 屬性。

SetBinding 方法顯示其中一個最重要的資料繫結規則:

目標屬性都必須受到可繫結屬性的支援。

此規則暗示,目標物件必須是衍生自 BindableObject 的類別執行個體。 請參閱可繫結屬性一文,以了解可繫結物件和可繫結屬性的概觀。

指定為字串的來源屬性沒有這類規則。 就內部而言,會使用反映來存取實際的屬性。 不過在這個特定案例中,Value 屬性也受到可繫結屬性支援。

程式碼可稍微簡化:RotationProperty 可繫結屬性由 VisualElement 定義,且由 LabelContentPage 繼承,因此在 SetBinding 呼叫中不需要類別名稱:

label.SetBinding(RotationProperty, "Value");

不過,包含類別名稱對於目標物件是很好的提醒。

在您操作 Slider 時,Label 會跟著旋轉:

Basic 程式碼繫結

[基本 XAML 繫結] 頁面與 [基本程式碼繫結] 相同,不同之處在於它會在 XAML 中定義整個資料繫結:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="DataBindingDemos.BasicXamlBindingPage"
             Title="Basic XAML Binding">
    <StackLayout Padding="10, 0">
        <Label Text="TEXT"
               FontSize="80"
               HorizontalOptions="Center"
               VerticalOptions="CenterAndExpand"
               BindingContext="{x:Reference Name=slider}"
               Rotation="{Binding Path=Value}" />

        <Slider x:Name="slider"
                Maximum="360"
                VerticalOptions="CenterAndExpand" />
    </StackLayout>
</ContentPage>

如同程式碼,資料繫結是在目標物件上設定,也就是 Label。 牽涉到兩個 XAML 標記延伸。 這些可藉由大括弧分隔符號立即辨識出來:

  • 需要 x:Reference 標記延伸才能參考來源物件,也就是名為 sliderSlider
  • Binding 標記延伸會將 LabelRotation 屬性連結至 SliderValue 屬性。

如需 XAML 標記延伸的詳細資訊,請參閱 XAML 標記延伸x:Reference 標記延伸受到 ReferenceExtension 類別支援;Binding 受到 BindingExtension 類別支援。 如 XML 命名空間前置詞所指出, x:Reference 是 XAML 2009 規格的一部分,而 Binding 是的一 Xamarin.Forms部分。 請注意,在大括弧內沒有出現引號。

在設定 BindingContext 時,很容易就會忘記 x:Reference 標記延伸。 通常會錯誤地將屬性直接設定為如下的繫結來源名稱:

BindingContext="slider"

但那是不對的。 該標記會將 BindingContext 屬性設定為 string 物件,其字元拼起來是 "slider"!

請注意,來源屬性是使用 BindingExtensionPath 屬性所指定,其對應於 Binding 類別的 Path 屬性。

[基本 XAML 繫結] 頁面上所顯示的標記可以簡化:XAML 標記延伸,例如 x:ReferenceBinding 可以定義「內容屬性」屬性,這對於 XAML 標記延伸表示屬性名稱不需要出現。 Name 屬性是 x:Reference 的內容屬性,而 Path 屬性是 Binding 的內容屬性,這表示它們可以從運算式排除:

<Label Text="TEXT"
       FontSize="80"
       HorizontalOptions="Center"
       VerticalOptions="CenterAndExpand"
       BindingContext="{x:Reference slider}"
       Rotation="{Binding Value}" />

沒有繫結內容的繫結

BindingContext 屬性是資料繫結的重要元件,但是它不一定必要。 來源物件可以改為指定在 SetBinding 呼叫或 Binding 標記延伸中。

這示範於替代程式碼繫結範例中。 XAML 檔案類似於繫結基本程式碼範例,不同之處在於定義了 Slider 來控制 LabelScale 屬性。 因此, Slider 會針對 –2 到 2 的範圍設定 :

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="DataBindingDemos.AlternativeCodeBindingPage"
             Title="Alternative Code Binding">
    <StackLayout Padding="10, 0">
        <Label x:Name="label"
               Text="TEXT"
               FontSize="40"
               HorizontalOptions="Center"
               VerticalOptions="CenterAndExpand" />

        <Slider x:Name="slider"
                Minimum="-2"
                Maximum="2"
                VerticalOptions="CenterAndExpand" />
    </StackLayout>
</ContentPage>

程式碼後置檔案會使用 BindableObject 定義的 SetBinding 方法來設定繫結。 引數是 Binding 類別的建構函式

public partial class AlternativeCodeBindingPage : ContentPage
{
    public AlternativeCodeBindingPage()
    {
        InitializeComponent();

        label.SetBinding(Label.ScaleProperty, new Binding("Value", source: slider));
    }
}

Binding 建構函式有 6 個參數,因此會使用具名引數來指定 source 參數。 引數是 slider 物件。

執行此程式可能會有點令人驚訝:

替代程式碼繫結

左側的 iOS 畫面會顯示頁面第一次出現時的螢幕外觀。 Label 在哪裡?

問題在於 Slider 初始值為 0。 這會導致 LabelScale 屬性也設定為 0,覆寫其預設值 1。 這導致一開始看不到 Label。 如 Android 螢幕擷取畫面所示,您可以操作 Slider,讓 Label 再次出現,但其一開始消失是令人不安。

您將在下一篇文章中發現如何藉由從 Scale 屬性的預設值初始化 Slider 來避免這個問題。

注意

VisualElement 類別也會定義 ScaleXScaleY 屬性,它們可以在水平和垂直方向以不同的方式調整 VisualElement

[替代 XAML 繫結] 頁面會顯示完全使用 XAML 的相等繫結:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="DataBindingDemos.AlternativeXamlBindingPage"
             Title="Alternative XAML Binding">
    <StackLayout Padding="10, 0">
        <Label Text="TEXT"
               FontSize="40"
               HorizontalOptions="Center"
               VerticalOptions="CenterAndExpand"
               Scale="{Binding Source={x:Reference slider},
                               Path=Value}" />

        <Slider x:Name="slider"
                Minimum="-2"
                Maximum="2"
                VerticalOptions="CenterAndExpand" />
    </StackLayout>
</ContentPage>

現在 Binding 標記延伸已設定兩個屬性,分別是 SourcePath,並以逗號分隔。 如果您偏好,它們可以出現在同一行:

Scale="{Binding Source={x:Reference slider}, Path=Value}" />

Source 屬性設定為內嵌的 x:Reference 標記延伸,這在其他方面具有與設定 BindingContext 相同的語法。 請注意,大括弧中未出現引號,而且必須以逗號分隔兩個屬性。

Binding 標記延伸的內容屬性是 Path,但只有在它是運算式中的第一個屬性時,才能去除標記延伸的 Path= 部分。 若要去除 Path= 部分,您需要交換兩個屬性:

Scale="{Binding Value, Source={x:Reference slider}}" />

雖然 XAML 標記延伸通常以大括弧分隔,它們也可以用物件項目來表示:

<Label Text="TEXT"
       FontSize="40"
       HorizontalOptions="Center"
       VerticalOptions="CenterAndExpand">
    <Label.Scale>
        <Binding Source="{x:Reference slider}"
                 Path="Value" />
    </Label.Scale>
</Label>

現在 SourcePath 屬性是一般的 XAML 屬性:值出現在引號內,且屬性不以逗號分隔。 x:Reference 標記延伸也可以成為物件項目:

<Label Text="TEXT"
       FontSize="40"
       HorizontalOptions="Center"
       VerticalOptions="CenterAndExpand">
    <Label.Scale>
        <Binding Path="Value">
            <Binding.Source>
                <x:Reference Name="slider" />
            </Binding.Source>
        </Binding>
    </Label.Scale>
</Label>

此語法不常見,但有時候涉及複雜物件時會需要。

到目前為止所顯示的範例,會將 BindingContext 屬性和 BindingSource 屬性設定為 x:Reference 標記延伸,以參考頁面上的另一個檢視。 這兩個屬性都屬於類型 Object,並可以設定為任何物件,物件內包含適合繫結來源的屬性。

在接下來的文章中,您將發現可以將 BindingContextSource 屬性設定為 x:Static 標記延伸,以便參考靜態屬性或欄位的值;或是設定為 StaticResource 標記延伸,以便參考儲存在資源字典中的物件,或是直接設定為物件,這個物件通常 (但不一定) 是 ViewModel 的執行個體。

BindingContext 屬性也可以設定為 Binding 物件,以便 BindingSourcePath 屬性能定義繫結內容。

繫結內容繼承

在本文中,您已了解,您可以使用 BindingContext 屬性或 Binding 物件的 Source 屬性來指定來源物件。 如果同時設定,BindingSource 屬性會優先於 BindingContext

BindingContext 屬性有個極重要的特性:

BindingContext 屬性的設定會透過視覺化樹狀結構繼承。

如您所見,這非常方便簡化系結運算式,而且在某些情況下,特別是在Model-View-ViewModel (MVVM) 案例中,這是不可或缺的。

繫結內容繼承範例是繫結內容繼承的簡單示範:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="DataBindingDemos.BindingContextInheritancePage"
             Title="BindingContext Inheritance">
    <StackLayout Padding="10">

        <StackLayout VerticalOptions="FillAndExpand"
                     BindingContext="{x:Reference slider}">

            <Label Text="TEXT"
                   FontSize="80"
                   HorizontalOptions="Center"
                   VerticalOptions="EndAndExpand"
                   Rotation="{Binding Value}" />

            <BoxView Color="#800000FF"
                     WidthRequest="180"
                     HeightRequest="40"
                     HorizontalOptions="Center"
                     VerticalOptions="StartAndExpand"
                     Rotation="{Binding Value}" />
        </StackLayout>

        <Slider x:Name="slider"
                Maximum="360" />

    </StackLayout>
</ContentPage>

StackLayoutBindingContext 屬性設定為 slider 物件。 這個繫結內容同時由 LabelBoxView 繼承,這兩者的 Rotation 屬性都設定為 SliderValue 屬性:

繫結內容繼承

下一篇文章中,您會看到「繫結模式」如何變更目標和來源物件之間的資料流程。

Channel 9YouTube 上尋找更多 Xamarin 影片。