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 VisualElement
element 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
:
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 obiektSlider
o nazwieslider
. - Rozszerzenie struktury znaczników
Binding
łączy właściwośćRotation
obiektuLabel
z właściwościąValue
obiektuSlider
.
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:
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
:
W następnym artykule zobaczysz, jak tryb powiązania może zmienić przepływ danych między obiektem docelowym i obiektem źródłowym.
Linki powiązane
Powiązany film wideo
Więcej filmów na platformie Xamarin można znaleźć w witrynach Channel 9 i YouTube.