4. část: Základy datových vazeb
Datové vazby umožňují propojení vlastností dvou objektů tak, aby změna v jednom způsobí změnu v druhém objektu. Jedná se o velmi cenný nástroj a zatímco datové vazby je možné definovat zcela v kódu, XAML poskytuje klávesové zkratky a pohodlí. V důsledku toho je jedním z nejdůležitějších rozšíření značek v Xamarin.Forms binding.
Datové vazby
Datové vazby spojují vlastnosti dvou objektů, označovaných jako zdroj a cíl. V kódu jsou vyžadovány dva kroky: Vlastnost BindingContext
cílového objektu musí být nastavena na zdrojový objekt a SetBinding
metoda (často použitá ve spojení s Binding
třídou) musí být volána v cílovém objektu, aby bylo možné svázat vlastnost tohoto objektu s vlastností zdrojového objektu.
Cílová vlastnost musí být vlastnost svázatelná, což znamená, že cílový objekt musí být odvozen od BindableObject
. Online Xamarin.Forms dokumentace označuje, které vlastnosti jsou vlastnosti s možností vazby. Vlastnost Label
, jako Text
je přidružena k vázání vlastnosti TextProperty
.
V kódu musíte také provést stejné dva kroky, které jsou požadovány v kódu, s tím rozdílem, že Binding
rozšíření revizí provádí místo SetBinding
volání a Binding
třídy.
Když však definujete datové vazby v XAML, existuje několik způsobů, jak nastavit BindingContext
cílový objekt. Někdy se nastavuje ze souboru kódu, někdy používá příponu StaticResource
nebo x:Static
značku a někdy jako obsah BindingContext
značek vlastností.
Vazby se nejčastěji používají k propojení vizuálů programu s podkladovým datovým modelem, obvykle při realizaci architektury aplikace MVVM (Model-View-ViewModel), jak je popsáno v části 5. Z datových vazeb na MVVM, ale další scénáře jsou možné.
Vazby zobrazení na zobrazení
Datové vazby můžete definovat pro propojení vlastností dvou zobrazení na stejné stránce. V tomto případě nastavíte BindingContext
cílový objekt pomocí x:Reference
rozšíření značek.
Tady je soubor XAML, který obsahuje Slider
dvě Label
zobrazení, z nichž jedna je otočena Slider
hodnotou a jinou, která zobrazuje Slider
hodnotu:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.SliderBindingsPage"
Title="Slider Bindings Page">
<StackLayout>
<Label Text="ROTATION"
BindingContext="{x:Reference Name=slider}"
Rotation="{Binding Path=Value}"
FontAttributes="Bold"
FontSize="Large"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
<Slider x:Name="slider"
Maximum="360"
VerticalOptions="CenterAndExpand" />
<Label BindingContext="{x:Reference slider}"
Text="{Binding Value, StringFormat='The angle is {0:F0} degrees'}"
FontAttributes="Bold"
FontSize="Large"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage>
Obsahuje Slider
x:Name
atribut odkazovaný dvěma Label
zobrazeními pomocí x:Reference
rozšíření značek.
Rozšíření x:Reference
vazby definuje vlastnost pojmenovanou Name
pro nastavení na název odkazovaného prvku, v tomto případě slider
. Nicméně třída ReferenceExtension
, která definuje x:Reference
rozšíření značek také definuje ContentProperty
atribut pro Name
, což znamená, že není explicitně požadován. Jen pro řadu, první x:Reference
obsahuje "Name=", ale druhý ne:
BindingContext="{x:Reference Name=slider}"
…
BindingContext="{x:Reference slider}"
Samotné Binding
rozšíření značek může mít několik vlastností, stejně jako u BindingBase
třídy a Binding
třídy. Hodnota ContentProperty
for Binding
je Path
, ale část "Path=" rozšíření značek může být vynechána, pokud je cesta první položkou Binding
v rozšíření značek. První příklad obsahuje "Path=", ale druhý příklad ho vynechá:
Rotation="{Binding Path=Value}"
…
Text="{Binding Value, StringFormat='The angle is {0:F0} degrees'}"
Všechny vlastnosti můžou být na jednom řádku nebo oddělené na více řádcích:
Text="{Binding Value,
StringFormat='The angle is {0:F0} degrees'}"
Udělejte to, co je pohodlné.
StringFormat
Všimněte si vlastnosti ve druhém Binding
rozšíření značek. Vazby Xamarin.Formsneprovádějí žádné implicitní převody typů, a pokud potřebujete zobrazit neřetězcový objekt jako řetězec, musíte zadat převaděč typů nebo použít StringFormat
. Na pozadí se statická String.Format
metoda používá k implementaci StringFormat
. To může být problém, protože specifikace formátování .NET zahrnují složené závorky, které se také používají k oddělovači rozšíření značek. To vytváří riziko matoucího analyzátoru XAML. Abyste tomu předešli, vložte celý formátovací řetězec do jednoduchých uvozovek:
Text="{Binding Value, StringFormat='The angle is {0:F0} degrees'}"
Tady je spuštěný program:
Režim vazby
Jedno zobrazení může mít datové vazby na několik jeho vlastností. Každé zobrazení však může mít pouze jedno BindingContext
, takže více datových vazeb v daném zobrazení musí všechny odkazovat vlastnosti stejného objektu.
Řešení tohoto a dalších problémů zahrnuje Mode
vlastnost, která je nastavena na člen výčtu BindingMode
:
Default
OneWay
— hodnoty se přenesou ze zdroje do cíle.OneWayToSource
— hodnoty se přenesou z cíle do zdroje.TwoWay
— hodnoty jsou přeneseny oběma způsoby mezi zdrojem a cílem.OneTime
— data pocházejí ze zdroje do cíle, ale pouze v případě, že seBindingContext
změny změní
Následující program ukazuje jedno běžné použití OneWayToSource
režimů vazby.TwoWay
Čtyři Slider
zobrazení jsou určena k řízení Scale
, Rotate
, RotateX
a RotateY
vlastnosti Label
. Zpočátku se zdá, že tyto čtyři vlastnosti Label
by měly být cíle datové vazby, protože každá z nich je nastavena pomocí Slider
. Může BindingContext
Label
však být pouze jeden objekt a existují čtyři různé posuvníky.
Z tohoto důvodu jsou všechny vazby nastaveny zdánlivě zpětně: BindingContext
Každý ze čtyř posuvníků je nastaven na Label
hodnotu a vazby jsou nastaveny na Value
vlastnosti posuvníků. Pomocí a OneWayToSource
TwoWay
režimů mohou tyto Value
vlastnosti nastavit zdrojové vlastnosti, které jsou Scale
, Rotate
, RotateX
a RotateY
vlastnosti Label
:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.SliderTransformsPage"
Padding="5"
Title="Slider Transforms Page">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<!-- Scaled and rotated Label -->
<Label x:Name="label"
Text="TEXT"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
<!-- Slider and identifying Label for Scale -->
<Slider x:Name="scaleSlider"
BindingContext="{x:Reference label}"
Grid.Row="1" Grid.Column="0"
Maximum="10"
Value="{Binding Scale, Mode=TwoWay}" />
<Label BindingContext="{x:Reference scaleSlider}"
Text="{Binding Value, StringFormat='Scale = {0:F1}'}"
Grid.Row="1" Grid.Column="1"
VerticalTextAlignment="Center" />
<!-- Slider and identifying Label for Rotation -->
<Slider x:Name="rotationSlider"
BindingContext="{x:Reference label}"
Grid.Row="2" Grid.Column="0"
Maximum="360"
Value="{Binding Rotation, Mode=OneWayToSource}" />
<Label BindingContext="{x:Reference rotationSlider}"
Text="{Binding Value, StringFormat='Rotation = {0:F0}'}"
Grid.Row="2" Grid.Column="1"
VerticalTextAlignment="Center" />
<!-- Slider and identifying Label for RotationX -->
<Slider x:Name="rotationXSlider"
BindingContext="{x:Reference label}"
Grid.Row="3" Grid.Column="0"
Maximum="360"
Value="{Binding RotationX, Mode=OneWayToSource}" />
<Label BindingContext="{x:Reference rotationXSlider}"
Text="{Binding Value, StringFormat='RotationX = {0:F0}'}"
Grid.Row="3" Grid.Column="1"
VerticalTextAlignment="Center" />
<!-- Slider and identifying Label for RotationY -->
<Slider x:Name="rotationYSlider"
BindingContext="{x:Reference label}"
Grid.Row="4" Grid.Column="0"
Maximum="360"
Value="{Binding RotationY, Mode=OneWayToSource}" />
<Label BindingContext="{x:Reference rotationYSlider}"
Text="{Binding Value, StringFormat='RotationY = {0:F0}'}"
Grid.Row="4" Grid.Column="1"
VerticalTextAlignment="Center" />
</Grid>
</ContentPage>
Vazby na třech zobrazeních jsou OneWayToSource
, což znamená, že Slider
hodnota způsobí změnu vlastnosti jeho BindingContext
, což je pojmenovaný Label
label
.Slider
Tato tři Slider
zobrazení způsobují změny v objektu Rotate
, RotateX
a RotateY
vlastnosti Label
.
Vazba pro Scale
vlastnost je TwoWay
však . Důvodem je to, že Scale
vlastnost má výchozí hodnotu 1 a použití vazby TwoWay
způsobí Slider
, že počáteční hodnota bude nastavena na hodnotu 1 místo 0. Pokud by tato vazba byla OneWayToSource
, Scale
vlastnost by byla zpočátku nastavena na hodnotu 0 z Slider
výchozí hodnoty. Tato Label
možnost by nebyla viditelná a to může uživateli způsobit nejasnost.
Poznámka:
Třída VisualElement
má také vlastnostiScaleY
, které škáluje VisualElement
na ose ScaleX
x a y.
Vazby a kolekce
Nic ilustruje výkon XAML a datových vazeb lépe než šablona .ListView
ListView
ItemsSource
definuje vlastnost typu IEnumerable
a zobrazí položky v této kolekci. Tyto položky mohou být objekty libovolného typu. Ve výchozím nastavení ListView
používá metodu ToString
každé položky k zobrazení této položky. Někdy je to jen to, ToString
co chcete, ale v mnoha případech vrátí pouze plně kvalifikovaný název třídy objektu.
Položky v ListView
kolekci lze však zobrazit libovolným způsobem prostřednictvím šablony, která zahrnuje třídu odvozenou z Cell
. Šablona se naklonuje pro každou položku v objektu ListView
a datové vazby nastavené v šabloně se přenesou do jednotlivých klonů.
Velmi často budete chtít vytvořit vlastní buňku ViewCell
pro tyto položky pomocí třídy. Tento proces je v kódu poněkud nepořádný, ale v XAML se stává velmi jednoduchým.
Součástí projektu XamlSamples je třída s názvem NamedColor
. Každý NamedColor
objekt má Name
a FriendlyName
vlastnosti typu string
a Color
vlastnost typu Color
. Kromě toho NamedColor
má 141 statických polí typu Color
jen pro čtení odpovídajících barvám definovaným Xamarin.FormsColor
ve třídě. Statický konstruktor vytvoří IEnumerable<NamedColor>
kolekci obsahující NamedColor
objekty odpovídající těmto statickým polím a přiřadí ji k jeho veřejné statické All
vlastnosti.
Nastavení statické NamedColor.All
vlastnosti na ItemsSource
značek ListView
je snadné pomocí x:Static
rozšíření značek:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:XamlSamples;assembly=XamlSamples"
x:Class="XamlSamples.ListViewDemoPage"
Title="ListView Demo Page">
<ListView ItemsSource="{x:Static local:NamedColor.All}" />
</ContentPage>
Výsledný displej určuje, že položky jsou skutečně typu XamlSamples.NamedColor
:
Není to moc informací, ale je možné ho ListView
posunout a vybrat.
Chcete-li definovat šablonu pro položky, budete chtít rozdělit ItemTemplate
vlastnost jako prvek vlastnosti a nastavit ji na hodnotu DataTemplate
, která pak odkazuje na ViewCell
. View
Pro vlastnost ViewCell
můžete definovat rozložení jednoho nebo více zobrazení pro zobrazení každé položky. Tady je jednoduchý příklad:
<ListView ItemsSource="{x:Static local:NamedColor.All}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<Label Text="{Binding FriendlyName}" />
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Poznámka:
Zdroj vazby pro buňky a podřízené buňky je ListView.ItemsSource
kolekce.
Element Label
je nastaven na View
vlastnost ViewCell
. (Značky ViewCell.View
nejsou potřeba, protože View
vlastnost je vlastnost ViewCell
obsahu .) Tento kód zobrazí vlastnost každého NamedColor
objektuFriendlyName
:
Mnohem lepší. Vše, co je teď potřeba, je namáčknout šablonu položky s dalšími informacemi a skutečnou barvou. Pro podporu této šablony byly některé hodnoty a objekty definovány ve slovníku prostředků stránky:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:XamlSamples"
x:Class="XamlSamples.ListViewDemoPage"
Title="ListView Demo Page">
<ContentPage.Resources>
<ResourceDictionary>
<OnPlatform x:Key="boxSize"
x:TypeArguments="x:Double">
<On Platform="iOS, Android, UWP" Value="50" />
</OnPlatform>
<OnPlatform x:Key="rowHeight"
x:TypeArguments="x:Int32">
<On Platform="iOS, Android, UWP" Value="60" />
</OnPlatform>
<local:DoubleToIntConverter x:Key="intConverter" />
</ResourceDictionary>
</ContentPage.Resources>
<ListView ItemsSource="{x:Static local:NamedColor.All}"
RowHeight="{StaticResource rowHeight}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Padding="5, 5, 0, 5"
Orientation="Horizontal"
Spacing="15">
<BoxView WidthRequest="{StaticResource boxSize}"
HeightRequest="{StaticResource boxSize}"
Color="{Binding Color}" />
<StackLayout Padding="5, 0, 0, 0"
VerticalOptions="Center">
<Label Text="{Binding FriendlyName}"
FontAttributes="Bold"
FontSize="Medium" />
<StackLayout Orientation="Horizontal"
Spacing="0">
<Label Text="{Binding Color.R,
Converter={StaticResource intConverter},
ConverterParameter=255,
StringFormat='R={0:X2}'}" />
<Label Text="{Binding Color.G,
Converter={StaticResource intConverter},
ConverterParameter=255,
StringFormat=', G={0:X2}'}" />
<Label Text="{Binding Color.B,
Converter={StaticResource intConverter},
ConverterParameter=255,
StringFormat=', B={0:X2}'}" />
</StackLayout>
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>
Všimněte si použití OnPlatform
k definování velikosti BoxView
a výšky ListView
řádků. I když jsou hodnoty pro všechny platformy stejné, značky by se daly snadno přizpůsobit pro další hodnoty, aby bylo možné vyladit zobrazení.
Převaděče hodnot vazeb
Předchozí ListView Demo XAML soubor zobrazí jednotlivé R
, G
a B
vlastnosti Xamarin.FormsColor
struktury. Tyto vlastnosti jsou typu a jsou v rozsahu double
od 0 do 1. Pokud chcete zobrazit šestnáctkové hodnoty, nemůžete jednoduše použít StringFormat
se specifikací formátování "X2". To funguje pouze pro celá čísla a kromě toho, double
hodnoty musí být vynásobeny 255.
Tento malý problém byl vyřešen pomocí převaděče hodnot, označovaný také jako převaděč vazby. Toto je třída, která implementuje IValueConverter
rozhraní, což znamená, že má dvě metody pojmenované Convert
a ConvertBack
. Metoda Convert
se volá, když je hodnota přenesena ze zdroje do cíle; ConvertBack
metoda se volá pro přenosy z cíle do zdroje nebo OneWayToSource
TwoWay
vazby:
using System;
using System.Globalization;
using Xamarin.Forms;
namespace XamlSamples
{
class DoubleToIntConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
double multiplier;
if (!Double.TryParse(parameter as string, out multiplier))
multiplier = 1;
return (int)Math.Round(multiplier * (double)value);
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
double divider;
if (!Double.TryParse(parameter as string, out divider))
divider = 1;
return ((double)(int)value) / divider;
}
}
}
Metoda ConvertBack
v tomto programu nehraje roli, protože vazby jsou pouze jedním ze zdroje do cíle.
Vazba odkazuje na převaděč vazby Converter
s vlastností. Převaděč vazeb může také přijmout parametr zadaný s ConverterParameter
vlastností. Pro určitou všestrannost je to způsob určení násobitele. Převaděč vazeb zkontroluje, zda parametr převaděče obsahuje platnou double
hodnotu.
Převaděč se vytvoří instance ve slovníku prostředků, takže ho lze sdílet mezi několika vazbami:
<local:DoubleToIntConverter x:Key="intConverter" />
Na tuto jednu instanci odkazují tři datové vazby. Všimněte si, že Binding
rozšíření značek obsahuje vložené StaticResource
rozšíření značek:
<Label Text="{Binding Color.R,
Converter={StaticResource intConverter},
ConverterParameter=255,
StringFormat='R={0:X2}'}" />
Tady je výsledek:
Jedná se ListView
o poměrně sofistikované zpracování změn, které se můžou dynamicky vyskytovat v podkladových datech, ale pouze v případě, že provedete určité kroky. Pokud kolekce položek přiřazených k ItemsSource
vlastnosti ListView
změn během běhu – to znamená, že pokud lze položky přidat nebo odebrat z kolekce , použijte ObservableCollection
pro tyto položky třídu. ObservableCollection
implementuje INotifyCollectionChanged
rozhraní a ListView
nainstaluje obslužnou rutinu CollectionChanged
události.
Pokud se vlastnosti samotných položek mění během běhu, měly by položky v kolekci implementovat rozhraní a signalizovat INotifyPropertyChanged
změny hodnot vlastností pomocí PropertyChanged
události. To je znázorněno v další části této série, část 5. Z datové vazby na MVVM.
Shrnutí
Datové vazby poskytují výkonný mechanismus pro propojení vlastností mezi dvěma objekty na stránce nebo mezi vizuálními objekty a podkladovými daty. Když ale aplikace začne pracovat se zdroji dat, začíná se jako užitečný paradigma objevit oblíbený vzor architektury aplikace. To je popsáno v části 5. Z datových vazeb na MVVM.