Udostępnij za pośrednictwem


Xamarin.Forms Powiązania podstawowe

Xamarin.Forms Powiązanie danych łączy parę właściwości między dwoma obiektami, z których co najmniej jeden jest zwykle obiektem interfejsu użytkownika. Te dwa obiekty noszą nazwę obiektu docelowego i obiektu źródłowego:

  • Obiekt docelowy to obiekt (i właściwość), na którym jest ustawione powiązanie danych.
  • Obiekt źródłowy to obiekt (i właściwość), do którego odwołuje się powiązanie danych.

To rozróżnienie może być czasami nieco mylące: w najprostszym przypadku dane przepływa ze źródła do obiektu docelowego, co oznacza, że wartość właściwości docelowej jest ustawiana na podstawie wartości właściwości źródłowej. Jednak w niektórych przypadkach dane mogą przepływać od obiektu docelowego do obiektu źródłowego lub w obu kierunkach. Aby uniknąć pomyłek, pamiętaj, że obiektem docelowym jest zawsze obiekt, na którym jest ustawione powiązanie danych, nawet jeśli dostarcza on dane, a nie je odbiera.

Powiązania z kontekstem powiązania

Chociaż powiązania danych zazwyczaj są określane w całości w języku XAML, przejrzenie powiązań danych w kodzie może być pouczające. Na stronie Podstawowe powiązanie kodu znajduje się plik XAML zawierający obiekty Label i Slider:

<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>

Dla obiektu Slider ustawiono zakres od 0 do 360. Celem tego programu jest obracanie obiektu Label przez manipulowanie obiektem Slider.

Bez powiązań danych należałoby ustawić zdarzenie ValueChanged obiektu Slider na program obsługi zdarzeń, który uzyskuje dostęp do właściwości Value obiektu Slider i ustawia dla niej wartość właściwości Rotation obiektu Label. Powiązanie danych automatyzuje to zadanie — program obsługi zdarzeń i zawarty w nim kod nie są już potrzebne.

Powiązanie można ustawić na wystąpieniu dowolnej klasy, która pochodzi od klasy BindableObject obejmującej pochodne Element, VisualElement, View i View. Powiązanie jest zawsze ustawiane na obiekcie docelowym. Powiązanie odwołuje się do obiektu źródłowego. Aby ustawić powiązanie danych, użyj następujących dwóch elementów członkowskich klasy docelowej:

  • Właściwość BindingContext określa obiekt źródłowy.
  • Metoda SetBinding określa właściwość docelową i właściwość źródłową.

W tym przykładzie Label jest obiektem docelowym powiązania, a Slider — obiektem źródłowym powiązania. Zmiany w obiekcie źródłowym Slider wpływają na obrót obiektu docelowego Label. Dane przepływają od obiektu źródłowego do obiektu docelowego.

Metoda SetBinding zdefiniowana przez klasę BindableObject ma argument typu BindingBase, z którego pochodzi klasa Binding, ale istnieją inne metody SetBinding zdefiniowane przez klasę BindableObjectExtensions. W pliku związanym z kodem w przykładzie Podstawowe powiązanie kodu jest stosowana prostsza metoda rozszerzenia SetBinding z tej klasy.

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

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

Obiekt Label jest obiektem docelowym powiązania, czyli takim, na którym jest ustawiona ta właściwość i na którym jest wywoływana metoda. Właściwość BindingContext wskazuje obiekt źródłowy powiązania, którym jest obiekt Slider.

Metoda SetBinding jest wywoływana w obiekcie docelowym powiązania, ale określa zarówno właściwość obiektu docelowego, jak i właściwość obiektu źródłowego. Właściwość obiektu docelowego jest określona jako obiekt BindableProperty: Label.RotationProperty. Właściwość obiektu jest określona jako ciąg i wskazuje właściwość Value obiektu Slider.

Metoda SetBinding ujawnia jedną z najważniejszych reguł powiązań danych:

Właściwość obiektu docelowego musi być oparta na właściwości, którą można powiązać.

Ta reguła oznacza, że obiekt docelowy musi być wystąpieniem klasy pochodzącej od klasy BindableObject. Aby zapoznać się z omówieniem obiektów i właściwości, które można powiązać, zobacz artykuł Właściwości możliwe do wiązania.

Nie ma takiej reguły dla właściwości źródłowej, która jest określona jako ciąg. Wewnętrznie do uzyskania dostępu do rzeczywistej właściwości służy odbicie. Jednak w tym konkretnym przypadku właściwość Value jest również oparta na właściwości, którą można powiązać.

Kod można uprościć nieco: RotationProperty właściwość powiązana jest definiowana przez , i dziedziczona Label przez VisualElementelement iContentPage, więc nazwa klasy nie jest wymagana w wywołaniuSetBinding:

label.SetBinding(RotationProperty, "Value");

Jednak dołączenie nazwy klasy jest dobrym przypomnieniem obiektu docelowego.

Manipulowanie obiektem Slider powoduje obracanie odpowiednio obiektu Label:

Podstawowe powiązanie kodu

Strona Podstawowe powiązanie XAML jest taka sama jak strona Podstawowe powiązanie kodu z tą różnicą, że definiuje całe powiązanie danych w języku 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>

Tak samo jak w kodzie powiązanie danych jest ustawiane na obiekcie docelowym, którym jest obiekt Label. Używane są dwa rozszerzenia struktury znaczników XAML. Są one natychmiast rozpoznawalne dzięki ogranicznikom w postaci nawiasów klamrowych:

  • Rozszerzenie struktury znaczników x:Reference jest wymagane, aby można było odwoływać się do obiektu źródłowego, którym jest obiekt Slider o nazwie slider.
  • Rozszerzenie struktury znaczników Binding łączy właściwość Rotation obiektu Label z właściwością Value obiektu Slider.

Aby uzyskać więcej informacji o rozszerzeniach struktury znaczników XAML, zobacz artykuł Rozszerzenia struktury znaczników XAML. Rozszerzenie struktury znaczników x:Reference jest obsługiwane przez klasę ReferenceExtension, natomiast rozszerzenie Binding jest obsługiwane przez klasę BindingExtension. Jak wskazują prefiksy przestrzeni nazw XML, x:Reference jest częścią specyfikacji XAML 2009, podczas gdy Binding jest częścią Xamarin.Forms. Zwróć uwagę, że w nawiasach klamrowych nie ma żadnych znaków cudzysłowu.

Podczas ustawiania właściwości BindingContext można łatwo zapomnieć o rozszerzeniu struktury znaczników x:Reference. Często zdarza się omyłkowe ustawienie dla właściwości bezpośrednio nazwy obiektu źródłowego powiązania, na przykład w ten sposób:

BindingContext="slider"

Ale nie jest to właściwe. Ta struktura znaczników ustawia dla właściwości BindingContext obiekt string, którego znaki składają się na słowo „slider”!

Zwróć uwagę, że właściwość źródłowa jest określona za pomocą właściwości Path klasy BindingExtension, która odpowiada właściwości Path klasy Binding.

Znaczniki wyświetlane na stronie Podstawowe powiązania XAML można uprościć: rozszerzenia znaczników XAML, takie jak x:Reference i Binding mogą mieć zdefiniowane atrybuty właściwości zawartości, co dla rozszerzeń znaczników XAML oznacza, że nazwa właściwości nie musi być wyświetlana. Właściwość Name jest właściwością zawartości rozszerzenia x:Reference, a właściwość Path jest właściwością zawartości rozszerzenia Binding, co oznacza, że można je wyeliminować z wyrażeń:

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

Powiązania bez kontekstu powiązania

Właściwość BindingContext jest ważnym składnikiem powiązań danych, ale nie zawsze jest niezbędna. Obiekt źródłowy można określić w wywołaniu SetBinding lub w rozszerzeniu struktury znaczników Binding.

Zademonstrowano to w przykładzie Alternatywne powiązanie kodu. Plik XAML jest podobny do tego z przykładu Podstawowe powiązanie kodu z tą różnicą, że obiekt Slider jest zdefiniowany w celu sterowania właściwością Scale obiektu Label. Z tego powodu Slider parametr jest ustawiony dla zakresu od –2 do 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>

Plik związany z kodem ustawia powiązanie za pomocą metody SetBinding zdefiniowanej przez klasę BindableObject. Argument jest konstruktorem dla klasy Binding:

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

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

Konstruktor Binding ma 6 parametrów, dlatego parametr source jest określony za pomocą nazwanego argumentu. Argumentem jest obiekt slider.

Uruchomienie tego programu może dać nieco zaskakujący efekt:

Alternatywne powiązanie kodu

Ekran systemu iOS po lewej stronie pokazuje, jak wygląda ekran po wyświetleniu strony po raz pierwszy. Gdzie jest obiekt Label?

Problem polega na tym, że wartością początkową obiektu Slider jest 0. Powoduje to, że dla właściwości Scale obiektu Label również zostanie ustawiona wartość 0, która zastąpi jej wartość domyślną wynoszącą 1. W rezultacie obiekt Label jest początkowo niewidoczny. Jak pokazano na zrzucie ekranu systemu Android, obiektem Slider można manipulować tak, aby obiekt Label pojawił się ponownie, ale jego początkowa nieobecność jest deprymująca.

W kolejnym artykule odkryjesz, jak uniknąć tego problemu, inicjując obiekt Slider z poziomu wartości domyślnej właściwości Scale.

Uwaga

Klasa VisualElement definiuje również właściwości ScaleX i ScaleY, które umożliwiają skalowanie klasy VisualElement w różny sposób poziomo i pionowo.

Na stronie Alternatywne powiązanie XAML jest wyświetlane równoważne powiązanie utworzone w całości w języku 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>

Teraz rozszerzenie struktury znaczników Binding ma dwie ustawione właściwości — Source i Path — oddzielone przecinkiem. Jeśli wolisz, mogą one znajdować się w tym samym wierszu:

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

Właściwość Source jest ustawiona na osadzone rozszerzenie struktury znaczników x:Reference, które w przeciwnym razie ma taką samą składnię jak ustawienie właściwości BindingContext. Zwróć uwagę, że w nawiasach klamrowych nie ma żadnych znaków cudzysłowu, a obie właściwości muszą być oddzielone przecinkiem.

Właściwością zawartości rozszerzenia struktury znaczników Binding jest właściwość Path, ale fragment Path= rozszerzenia struktury znaczników można wyeliminować tylko wtedy, gdy jest to pierwsza właściwość w wyrażeniu. Aby wyeliminować fragment Path=, musisz zamienić miejscami obie właściwości:

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

Chociaż rozszerzenia struktury znaczników XAML są zwykle ograniczone przez nawiasy klamrowe, można je również wyrazić w postaci elementów obiektów:

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

Source Teraz właściwości i Path są zwykłymi atrybutami XAML: wartości są wyświetlane w cudzysłowie, a atrybuty nie są rozdzielane przecinkami. Rozszerzenie struktury znaczników x:Reference może również stać się elementem obiektu:

<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>

Ta składnia nie jest typowa, ale czasami jest niezbędna, gdy używane są obiekty złożone.

W pokazanych do tej pory przykładach dla właściwości BindingContext i właściwości Source klasy Binding jest ustawione rozszerzenie struktury znaczników x:Reference, aby można było odwoływać się do innego widoku na stronie. Te dwie właściwości są typu Object i można je ustawić na dowolny obiekt, który zawiera właściwości odpowiednie dla obiektów źródłowych powiązania.

W kolejnym artykule odkryjesz, że można ustawić właściwość BindingContext lub Source na rozszerzenie struktury znaczników x:Static, aby można było odwoływać się do wartości właściwości statycznej lub pola, bądź rozszerzenie struktury znaczników StaticResource, aby można było odwoływać się do obiektu przechowywanego w słowniku zasobów lub bezpośrednio do obiektu, który na ogół (choć nie zawsze) jest wystąpieniem klasy ViewModel.

Właściwość BindingContext można również ustawić na obiekt Binding, aby właściwości Source i Path rozszerzenia Binding definiowały kontekst powiązania.

Dziedziczenie kontekstu powiązania

W tym artykule pokazano, że obiekt źródłowy można określić przy użyciu właściwości BindingContext lub właściwości Source obiektu Binding. Jeśli ustawione są obie, właściwość Source obiektu Binding ma pierwszeństwo przed właściwością BindingContext.

Właściwość BindingContext ma niezwykle ważną cechę:

Ustawienie właściwości BindingContext jest dziedziczone za pośrednictwem drzewa wizualnego.

Jak zobaczysz, może to być bardzo przydatne w celu uproszczenia wyrażeń powiązań, a w niektórych przypadkach — szczególnie w scenariuszach Model-View-ViewModel (MVVM) — jest to niezbędne.

Przykład Dziedziczenie kontekstu powiązania jest prostą prezentacją kontekstu powiązania:

<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>

Właściwość BindingContext klasy StackLayout jest ustawiona na obiekt slider. Ten kontekst powiązania jest dziedziczony zarówno przez właściwość Label, jak i właściwość BoxView, których właściwości Rotation są ustawione na właściwość Value obiektu Slider:

Dziedziczenie kontekstu powiązania

W następnym artykule zobaczysz, jak tryb powiązania może zmienić przepływ danych między obiektem docelowym i obiektem źródłowym.

Więcej filmów na platformie Xamarin można znaleźć w witrynach Channel 9 i YouTube.