Xamarin.Forms Základní vazby

Xamarin.Forms Datová vazba propojuje dvojici vlastností mezi dvěma objekty, z nichž nejméně jeden je obvykle objekt uživatelského rozhraní. Tyto dva objekty se označují jako cíl a zdroj:

  • Cíl je objekt (a vlastnost), u kterého se datová vazba nastavuje.
  • Zdroj je objekt (a vlastnost), na který datová vazba odkazuje.

Tento rozdíl může být někdy trochu matoucí: V nejjednodušším případě data proudí ze zdroje do cíle, což znamená, že hodnota cílové vlastnosti je nastavena z hodnoty zdrojové vlastnosti. V některých případech ale můžou data naopak proudit z cíle do zdroje nebo oběma směry. Pro ujasnění pamatujte na to, že cíl je vždy objekt, u kterého se datová vazba nastavuje, i kdyby nepřijímal data, ale poskytoval.

Vazby s kontextem

I když se datové vazby obvykle zadávají zcela v jazyce XAML, je poučné podívat se na ně v kódu. Stránka Basic Code Binding (Základní vazba v kódu) obsahuje soubor XAML s objekty Label a 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>

Objekt Slider se nastavuje na hodnotu v rozsahu od 0 do 360. Účelem tohoto programu je otáčet objektem Label prostřednictvím úprav objektu Slider.

Bez datových vazeb byste nastavili událost ValueChanged objektu Slider na obslužnou rutinu události, která by přistupovala k vlastnosti Value objektu Slider a nastavovala tuto hodnotu pro vlastnost Rotation objektu Label. Datová vazba tuto úlohu automatizuje. Obslužná rutina události a kód v ní už nejsou potřebné.

Vazbu je možné nastavit u instance libovolné třídy odvozené od třídy BindableObject. Patří mezi ně třídy Element, VisualElement nebo View a třídy odvozené z třídy View. Vazba se vždy nastavuje u cílového objektu. Vazba odkazuje na zdrojový objekt. Pokud chcete nastavit datovou vazbu, použijte následující dva členy cílové třídy:

V tomto příkladu je objekt Label cílem vazby a objekt Slider je zdrojem vazby. Změny zdroje Slider ovlivňují otočení cíle Label. Data proudí ze zdroje do cíle.

Metoda SetBinding definovaná ve třídě BindableObject má argument typu BindingBase, ze kterého odvozuje třída Binding, ale existují také další metody SetBinding definované třídou BindableObjectExtensions. Soubor kódu na pozadí v ukázce Basic Code Binding používá jednodušší rozšiřující metodu SetBinding z této třídy.

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

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

Objekt Label je cílem vazby, takže toto je objekt, u kterého se tato vlastnost nastavuje a pro který se volá metoda. Vlastnost BindingContext označuje zdroj vazby, kterým je objekt Slider.

Metoda SetBinding se volá pro cíl vazby, ale určuje jak vlastnost cíle, tak vlastnost zdroje. Vlastnost cíle je zadaná jako objekt BindableProperty: Label.RotationProperty. Vlastnost zdroje je zadaná jako řetězec a určuje vlastnost Value objektu Slider.

Metoda SetBinding odhaluje jedno z nejdůležitějších pravidel datových vazeb:

Vlastnost cíle musí být podpořená vlastností umožňující vazbu.

Toto pravidlo implikuje, že cílový objekt musí být instancí třídy, která je odvozená z třídy BindableObject. Přehled objektů a vlastností umožňujících vazby najdete v článku o vlastnostech s možností vazby.

Pro vlastnost zdroje, která je zadaná jako řetězec, žádné takové pravidlo neplatí. Interně se k přístupu ke skutečné vlastnosti používá reflexe. V tomto konkrétním případě je ale vlastnost Value také podpořená vlastností umožňující vazbu.

Kód lze poněkud zjednodušit: Vlastnost RotationProperty bindable je definována VisualElementa zděděna Label a ContentPage také, takže název třídy není vyžadován ve SetBinding volání:

label.SetBinding(RotationProperty, "Value");

Zahrnutí názvu třídy je ale dobrým připomenutím cílového objektu.

Při úpravách objektu Slider se příslušným způsobem otáčí objekt Label:

Základní vazba v kódu

Stránka Basic Xaml Binding (Základní vazba v Xaml) je prakticky stejná jako stránka Basic Code Binding (Základní vazba v kódu). Rozdíl je jenom v tom, že celá datová vazba je na ní definovaná v jazyce 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>

Stejně jako v kódu se datová vazba nastavuje u cílového objektu, tedy Label. Jsou zapojená dvě rozšíření značek XAML. Ta se dají okamžitě rozpoznat podle svých oddělovačů, kterými jsou složené závorky:

  • Rozšíření značek x:Reference je potřebné pro odkazování na zdrojový objekt, kterým je objekt Slider s názvem slider.
  • Rozšíření značek Binding propojuje vlastnost Rotation objektu Label s vlastností Value objektu Slider.

Další informace o rozšířeních značek XAML najdete v článku Rozšíření značek XAML. Rozšíření značek x:Reference je podporované třídou ReferenceExtension. Rozšíření značek Binding je podporované třídou BindingExtension. Jak označují předpony oboru názvů XML, x:Reference je součástí specifikace XAML 2009, zatímco Binding je součástí Xamarin.Forms. Všimněte si, že ve složených závorkách nejsou žádné uvozovky.

Při nastavování vlastnosti BindingContext je snadné zapomenout na rozšíření značek x:Reference. Je běžné, že se tato vlastnost chybně nastaví přímo na název zdroje vazby, například takto:

BindingContext="slider"

To ale není správně. Tento kód nastaví vlastnost BindingContext na objekt string obsahující znaky slider.

Všimněte si, že vlastnost zdroje je určená vlastností Path třídy BindingExtension, která odpovídá vlastnosti Path třídy Binding.

Kód zobrazený na stránce základní vazby XAML může být zjednodušený: Rozšíření značek XAML, jako x:Reference jsou a Binding mohou mít definované atributy vlastností obsahu, které pro rozšíření značek XAML znamenají, že se název vlastnosti nemusí zobrazovat. Vlastnost Name je vlastností obsahu pro rozšíření x:Reference a vlastnost Path je vlastností obsahu pro Binding, což znamená, že je možné odebrat je z výrazů:

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

Vazby bez kontextu

Vlastnost BindingContext je důležitou součástí datových vazeb, ale není vždy nezbytná. Zdrojový objekt se dá místo toho zadat ve volání SetBinding nebo v rozšíření značek Binding.

To je znázorněné v ukázce Alternative Code Binding (Alternativní vazba v kódu). Soubor XAML je podobný ukázce Basic Code Binding. Rozdílem je, že objekt Slider je definovaný pro řízení vlastnosti Scale objektu Label. Z tohoto důvodu Slider je nastavena pro rozsah -2 až 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>

Soubor kódu na pozadí nastaví vazbu pomocí metody SetBinding definované ve třídě BindableObject. Argument je konstruktorem pro třídu Binding:

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

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

Konstruktor Binding má 6 parametrů, takže parametr source je zadaný pojmenovaným argumentem. Argumentem je objekt slider.

Spuštění tohoto programu může mít trochu překvapivý výsledek:

Alternativní vazba v kódu

Na obrazovce systému iOS na levé straně jde vidět, jak obrazovka vypadá při prvním zobrazení stránky. Kde je objekt Label?

Problémem je, že objekt Slider má počáteční hodnotu 0. To způsobí, že vlastnost Scale objektu Label se také nastaví na hodnotu 0 a přepíše se výchozí hodnota 1. To vede k tomu, že objekt Label je zpočátku neviditelný. Jak ukazuje snímek obrazovky v systému Android, můžete úpravou objektu Slider objekt Label znovu zviditelnit, ale jeho počáteční zmizení je znepokojivé.

V dalším článku zjistíte, jak se můžete tomuto problému vyhnout inicializací objektu Slider z výchozí hodnoty vlastnosti Scale.

Poznámka:

Třída VisualElement definuje také vlastnosti ScaleX a ScaleY, které můžou měnit měřítko objektu VisualElement odlišně ve vodorovném a svislém směru.

Stránka Alternative XAML Binding (Alternativní vazba v XAML) ukazuje ekvivalentní vazbu zcela v jazyce 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>

Rozšíření značek Binding má teď nastavené dvě vlastnosti oddělené čárkou: Source a Path. Pokud chcete, můžou být na stejném řádku:

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

Vlastnost Source je nastavená na vložené rozšíření značek x:Reference, které má jinak stejnou syntaxi jako nastavení BindingContext. Všimněte si, že ve složených závorkách nejsou žádné uvozovky a že tyto dvě vlastnosti musí být oddělené čárkou.

Vlastností obsahu rozšíření značek Binding je vlastnost Path, ale část Path= v rozšíření značek je možné odebrat jenom v případě, že se jedná o první vlastnost ve výrazu. Pokud chcete část Path= odebrat, musíte prohodit pořadí těchto dvou vlastností:

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

Přestože rozšíření značek XAML jsou obvykle oddělená složenými závorkami, můžou být také vyjádřená jako objektové elementy:

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

Nyní jsou Source vlastnosti Path běžné atributy XAML: Hodnoty se zobrazí v uvozovkách a atributy nejsou oddělené čárkou. Rozšíření značek x:Reference se může také stát objektovým elementem:

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

Tato syntaxe není obvyklá, ale v některých případech je potřebná při práci s komplexními objekty.

Prozatím uvedené příklady nastavují vlastnost BindingContext a vlastnost Source objektu Binding na rozšíření značek x:Reference a slouží jako odkaz na jiné zobrazení na stránce. Tyto dvě vlastnosti jsou typu Object a můžou se nastavit na libovolný objekt, který obsahuje vlastnosti vhodné pro zdroje vazeb.

V dalších článcích zjistíte, že můžete nastavit vlastnost BindingContext nebo Source na rozšíření značek x:Static, aby odkazovalo na hodnotu statické vlastnosti nebo pole, nebo na rozšíření značek StaticResource pro odkazování na objekt uložený ve slovníku prostředků nebo přímo na objekt, který je obvykle (ale ne vždy) instancí třídy ViewModel.

Vlastnost BindingContext je také možné nastavit na objekt Binding, aby vlastnosti Source a Path objektu Binding definovaly kontext vazby.

Dědičnost kontextu vazby

V tomto článku jste viděli, že zdrojový objekt můžete zadat pomocí vlastnosti BindingContext nebo pomocí vlastnosti Source objektu Binding. Pokud jsou nastavené obě dvě, má vlastnost Source objektu Binding přednost před vlastností BindingContext.

Vlastnost BindingContext má velmi důležitou charakteristiku:

Nastavení vlastnosti BindingContext se dědí prostřednictvím vizuálního stromu.

Jak vidíte, může to být velmi užitečné pro zjednodušení vazeb výrazů, a v některých případech – zejména ve scénářích MVVM (Model-View-ViewModel) – je nezbytné.

Ukázka Binding Context Inheritance (Dědičnost kontextu vazby) je jednoduchou ukázkou dědičnosti kontextu vazby:

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

Vlastnost BindingContext objektu StackLayout je nastavená na objekt slider. Tento kontext vazby dědí objekt Label i BoxView a oba mají své vlastnosti Rotation nastavené na vlastnost Value objektu Slider:

Dědičnost kontextu vazby

V dalším článku uvidíte, jak může režim vazby změnit tok dat mezi cílovými a zdrojovými objekty.

Další videa o Xamarinu najdete na Channel 9 a YouTube.