Podstawy powiązań danych
Powiązania danych interfejsu użytkownika aplikacji wieloplatformowej platformy .NET (.NET MAUI) umożliwiają łączenie właściwości dwóch obiektów, dzięki czemu zmiana w jednej powoduje zmianę w drugiej. Jest to bardzo cenne narzędzie, a powiązania danych można zdefiniować całkowicie w kodzie, język XAML udostępnia skróty i wygodę.
Powiązania danych
Powiązania danych łączą właściwości dwóch obiektów, nazywanych źródłem i obiektem docelowym. W kodzie wymagane są dwa kroki:
BindingContext
Właściwość obiektu docelowego musi być ustawiona na obiekt źródłowy,SetBinding
Metoda (często używana w połączeniu zBinding
klasą) musi być wywoływana na obiekcie docelowym, aby powiązać właściwość tego obiektu z właściwością obiektu źródłowego.
Właściwość docelowa musi być właściwością powiązaną, co oznacza, że obiekt docelowy musi pochodzić z BindableObjectklasy . Właściwość Label, taka jak Text
, jest skojarzona z właściwością powiązaną TextProperty
.
W języku XAML należy również wykonać te same dwa kroki, które są wymagane w kodzie, z tą różnicą, że Binding
rozszerzenie znaczników ma miejsce SetBinding
wywołania i Binding
klasy. Jednak podczas definiowania powiązań danych w języku XAML istnieje wiele sposobów ustawiania BindingContext
obiektu docelowego. Czasami jest on ustawiany z pliku za kodem, czasami przy użyciu StaticResource
rozszerzenia lub x:Static
znaczników, a czasami jako zawartość tagów BindingContext
właściwości-elementu.
Powiązania widok-widok
Powiązania danych można zdefiniować, aby połączyć właściwości dwóch widoków na tej samej stronie. W takim przypadku należy ustawić BindingContext
obiekt docelowy przy użyciu x:Reference
rozszerzenia znaczników.
Poniższy przykład zawiera dwa Slider Label widoki, z których jeden jest obracany przez Slider wartość, a drugi, który wyświetla Slider wartość:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.SliderBindingsPage"
Title="Slider Bindings Page">
<StackLayout>
<Label Text="ROTATION"
BindingContext="{x:Reference slider}"
Rotation="{Binding Path=Value}"
FontAttributes="Bold"
FontSize="18"
HorizontalOptions="Center"
VerticalOptions="Center" />
<Slider x:Name="slider"
Maximum="360"
VerticalOptions="Center" />
<Label BindingContext="{x:Reference slider}"
Text="{Binding Value, StringFormat='The angle is {0:F0} degrees'}"
FontAttributes="Bold"
FontSize="18"
HorizontalOptions="Center"
VerticalOptions="Center" />
</StackLayout>
</ContentPage>
x:Name
Zawiera Slider atrybut, do którego odwołuje się dwa Label widoki przy użyciu x:Reference
rozszerzenia znaczników. x:Reference
Rozszerzenie powiązania definiuje właściwość o nazwie Name
, aby ustawić nazwę elementu, do którego odwołuje się odwołanie, w tym przypadku slider
. ReferenceExtension
Jednak klasa, która definiuje x:Reference
rozszerzenie znaczników, definiuje ContentProperty
również atrybut dla Name
, co oznacza, że nie jest jawnie wymagane.
Rozszerzenie Binding
znaczników może mieć kilka właściwości, podobnie jak klasa BindingBase
i Binding
. Element for ContentProperty
Binding
to Path
, ale część "Path=" rozszerzenia znaczników może zostać pominięta, jeśli ścieżka jest pierwszym elementem Binding
w rozszerzeniu znaczników.
Drugie Binding
rozszerzenie znaczników ustawia StringFormat
właściwość . W programie .NET MAUI powiązania nie wykonują żadnych niejawnych konwersji typów, a jeśli musisz wyświetlić obiekt inny niż ciąg jako ciąg, musisz podać konwerter typów lub użyć polecenia StringFormat
.
Ważne
Ciągi formatowania muszą być umieszczane w pojedynczych cudzysłowach.
Tryb wiązania
Pojedynczy widok może mieć powiązania danych z kilkoma jego właściwościami. Jednak każdy widok może mieć tylko jeden BindingContext
obiekt , więc wiele powiązań danych w tym widoku musi zawierać wszystkie właściwości odwołania tego samego obiektu.
Rozwiązaniem tego i innych problemów jest Mode
właściwość , która jest ustawiona na element członkowski BindingMode
wyliczenia:
Default
OneWay
— wartości są przesyłane ze źródła do miejsca docelowegoOneWayToSource
— wartości są przesyłane z miejsca docelowego do źródłaTwoWay
— wartości są transferowane na oba sposoby między źródłem a obiektem docelowymOneTime
— dane przechodzą ze źródła do miejsca docelowego, ale tylko wtedy, gdyBindingContext
zmiany
W poniższym przykładzie pokazano jedno typowe użycie trybów OneWayToSource
powiązań i :TwoWay
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
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>
W tym przykładzie cztery Slider widoki mają kontrolować Scale
właściwości Label, Rotate
, RotateX
i RotateY
. Na początku wydaje się, że te cztery właściwości Label powinny być obiektami docelowymi powiązania danych, ponieważ każdy z nich jest ustawiany przez Sliderelement . Może BindingContext
Label to być jednak tylko jeden obiekt i istnieją cztery różne suwaki. Z tego powodu każdy BindingContext
z czterech suwaków jest ustawiony na Label, a powiązania są ustawiane na Value
właściwości suwaków. Za pomocą OneWayToSource
trybów i TwoWay
te Value
właściwości mogą ustawiać właściwości źródłowe, które są Scale
właściwościami Label, Rotate
, RotateX
i RotateY
.
Powiązania trzech widoków Slider to OneWayToSource
, co oznacza, że Slider wartość powoduje zmianę właściwości jej BindingContext
, która jest Label nazwą label
. Te trzy Slider widoki powodują zmiany Rotate
właściwości , RotateX
i RotateY
elementu Label:
Jednak powiązanie właściwości Scale
to TwoWay
. Jest to spowodowane tym, że Scale
właściwość ma wartość domyślną 1, a użycie TwoWay
powiązania powoduje Slider ustawienie wartości początkowej na 1, a nie 0. Gdyby to powiązanie było OneWayToSource
, Scale
właściwość początkowo zostałaby ustawiona na wartość 0 z wartości domyślnej Slider . Nie Label będzie widoczny.
Uwaga
Klasa VisualElement ma ScaleX
również właściwości i ScaleY
, które są skalowane VisualElement odpowiednio na osi x i y.
Powiązania i kolekcje
ListViewItemsSource
definiuje właściwość typu IEnumerable
i wyświetla elementy w tej kolekcji. Te elementy mogą być obiektami dowolnego typu. Domyślnie ListView do wyświetlania tego elementu jest używana ToString
metoda każdego elementu. Czasami jest to tylko to, ToString
czego potrzebujesz, ale w wielu przypadkach zwraca tylko w pełni kwalifikowaną nazwę klasy obiektu.
Jednak elementy w ListView kolekcji mogą być wyświetlane w dowolny sposób za pomocą szablonu, który obejmuje klasę pochodzącą z Cellklasy . Szablon jest klonowany dla każdego elementu w elemencie ListView, a powiązania danych ustawione na szablon są przenoszone do poszczególnych klonów. Komórki niestandardowe można tworzyć dla elementów przy użyciu ViewCell klasy .
ListView program może wyświetlić listę każdego nazwanego koloru dostępnego NamedColor
w programie .NET MAUI z pomocą klasy :
using System.Reflection;
using System.Text;
namespace XamlSamples
{
public class NamedColor
{
public string Name { get; private set; }
public string FriendlyName { get; private set; }
public Color Color { get; private set; }
// Expose the Color fields as properties
public float Red => Color.Red;
public float Green => Color.Green;
public float Blue => Color.Blue;
public static IEnumerable<NamedColor> All { get; private set; }
static NamedColor()
{
List<NamedColor> all = new List<NamedColor>();
StringBuilder stringBuilder = new StringBuilder();
// Loop through the public static fields of the Color structure.
foreach (FieldInfo fieldInfo in typeof(Colors).GetRuntimeFields())
{
if (fieldInfo.IsPublic &&
fieldInfo.IsStatic &&
fieldInfo.FieldType == typeof(Color))
{
// Convert the name to a friendly name.
string name = fieldInfo.Name;
stringBuilder.Clear();
int index = 0;
foreach (char ch in name)
{
if (index != 0 && Char.IsUpper(ch))
{
stringBuilder.Append(' ');
}
stringBuilder.Append(ch);
index++;
}
// Instantiate a NamedColor object.
NamedColor namedColor = new NamedColor
{
Name = name,
FriendlyName = stringBuilder.ToString(),
Color = (Color)fieldInfo.GetValue(null)
};
// Add it to the collection.
all.Add(namedColor);
}
}
all.TrimExcess();
All = all;
}
}
}
Każdy NamedColor
obiekt ma Name
FriendlyName
właściwości typu string
, Color
właściwość typu Color, i właściwości , Green
i Red
Blue
. Ponadto NamedColor
konstruktor statyczny tworzy kolekcję zawierającą IEnumerable<NamedColor>
NamedColor
obiekty odpowiadające polaom typu Color w Colors klasie i przypisuje ją do jej publicznej właściwości statycznej All
.
Ustawienie właściwości ItemsSource
statycznej NamedColor.All
ListView na element można osiągnąć przy użyciu x:Static
rozszerzenia znaczników:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
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>
Wynik określa, że elementy są typu XamlSamples.NamedColor
:
Aby zdefiniować szablon dla elementów, ItemTemplate
należy ustawić element na wartość DataTemplate , która odwołuje się do elementu ViewCell. Element ViewCell powinien definiować układ jednego lub większej liczby widoków, aby wyświetlić każdy element:
<ListView ItemsSource="{x:Static local:NamedColor.All}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label Text="{Binding FriendlyName}" />
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Uwaga
Źródłem powiązania dla komórek i elementów podrzędnych komórek jest ListView.ItemsSource
kolekcja.
W tym przykładzie Label element jest ustawiony na View właściwość ViewCell. Tagi ViewCell.View
nie są potrzebne, ponieważ View właściwość jest właściwością zawartości .ViewCell Ten kod XAML wyświetla FriendlyName
właściwość każdego NamedColor
obiektu:
Szablon elementu można rozwinąć, aby wyświetlić więcej informacji i rzeczywisty kolor:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:XamlSamples"
x:Class="XamlSamples.ListViewDemoPage"
Title="ListView Demo Page">
<ContentPage.Resources>
<x:Double x:Key="boxSize">50</x:Double>
<x:Int32 x:Key="rowHeight">60</x:Int32>
<local:FloatToIntConverter x:Key="intConverter" />
</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="14" />
<StackLayout Orientation="Horizontal"
Spacing="0">
<Label Text="{Binding Red,
Converter={StaticResource intConverter},
ConverterParameter=255,
StringFormat='R={0:X2}'}" />
<Label Text="{Binding Green,
Converter={StaticResource intConverter},
ConverterParameter=255,
StringFormat=', G={0:X2}'}" />
<Label Text="{Binding Blue,
Converter={StaticResource intConverter},
ConverterParameter=255,
StringFormat=', B={0:X2}'}" />
</StackLayout>
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>
Konwertery wartości powiązania
W poprzednim przykładzie XAML są wyświetlane poszczególne Red
właściwości , Green
i Blue
każdego NamedColor
elementu . Te właściwości są typu float
i wahają się od 0 do 1. Jeśli chcesz wyświetlić wartości szesnastkowe, nie można po prostu używać StringFormat
ze specyfikacją formatowania "X2". To działa tylko dla liczb całkowitych, float
a poza tym wartości muszą być mnożone przez 255.
Ten problem można rozwiązać za pomocą konwertera wartości, nazywanego również konwerterem powiązań. Jest to klasa, która implementuje IValueConverter interfejs, co oznacza, że ma dwie metody o nazwie Convert
i ConvertBack
. Metoda jest wywoływana Convert
, gdy wartość jest transferowana ze źródła do elementu docelowego. Metoda jest wywoływana ConvertBack
dla transferów z lokalizacji docelowej do źródła w OneWayToSource
pliku lub TwoWay
powiązań:
using System.Globalization;
namespace XamlSamples
{
public class FloatToIntConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
float multiplier;
if (!float.TryParse(parameter as string, out multiplier))
multiplier = 1;
return (int)Math.Round(multiplier * (float)value);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
float divider;
if (!float.TryParse(parameter as string, out divider))
divider = 1;
return ((float)(int)value) / divider;
}
}
}
Uwaga
Metoda ConvertBack
nie odgrywa roli w tym przykładzie, ponieważ powiązania są tylko jednym ze sposobów od źródła do celu.
Powiązanie odwołuje się do konwertera powiązań z właściwością Converter
. Konwerter powiązań może również zaakceptować parametr określony za pomocą ConverterParameter
właściwości . W przypadku pewnej wszechstronności jest to sposób określenia mnożnika. Konwerter powiązań sprawdza parametr konwertera dla prawidłowej float
wartości.
Konwerter jest tworzone w słowniku zasobów strony, dzięki czemu może być współużytkowany między wieloma powiązaniami:
<local:FloatToIntConverter x:Key="intConverter" />
Trzy powiązania danych odwołują się do tego pojedynczego wystąpienia:
<Label Text="{Binding Red,
Converter={StaticResource intConverter},
ConverterParameter=255,
StringFormat='R={0:X2}'}" />
Szablon elementu odtwarza kolor, przyjazną nazwę i wartości RGB:
Może ListView obsługiwać zmiany, które dynamicznie występują w danych bazowych, ale tylko wtedy, gdy podejmujesz określone kroki. Jeśli kolekcja elementów przypisanych ListView do ItemsSource
właściwości zmian w czasie wykonywania, użyj ObservableCollection<T> klasy dla tych elementów. ObservableCollection<T>implementuje interfejs i ListView zainstaluje INotifyCollectionChanged
program obsługi dla CollectionChanged
zdarzenia.
Jeśli właściwości elementów zmieniają się podczas wykonywania, elementy w kolekcji powinny implementować INotifyPropertyChanged
interfejs i sygnalizować zmiany wartości właściwości przy użyciu PropertyChanged
zdarzenia.
Następne kroki
Powiązania danych zapewniają zaawansowany mechanizm łączenia właściwości między dwoma obiektami na stronie lub między obiektami wizualnymi i podstawowymi danymi. Jednak gdy aplikacja zaczyna pracować ze źródłami danych, popularny wzorzec architektury aplikacji zaczyna pojawiać się jako przydatny paradygmat.