Udostępnij za pośrednictwem


Podstawy powiązań danych

Przeglądaj przykład. Przeglądanie przykładu

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:

  1. BindingContext Właściwość obiektu docelowego musi być ustawiona na obiekt źródłowy,
  2. SetBinding Metoda (często używana w połączeniu z Binding 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 BindingContextobiekt , 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 docelowego
  • OneWayToSource — wartości są przesyłane z miejsca docelowego do źródła
  • TwoWay — wartości są transferowane na oba sposoby między źródłem a obiektem docelowym
  • OneTime — dane przechodzą ze źródła do miejsca docelowego, ale tylko wtedy, gdy BindingContext 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ć Scalewłaściwości Label, Rotate, RotateXi 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ą Scalewłaściwościami Label, Rotate, RotateXi 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 Rotatewłaściwości , RotateXi RotateY elementu Label:

Powiązania odwrotne.

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 IEnumerablei 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 , Greeni RedBlue . 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:

Wiązanie z kolekcją.

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:

Wiązanie z kolekcją za pomocą elementu DataTemplate.

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 Redwłaściwości , Greeni Blue każdego NamedColorelementu . 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:

Wiązanie z kolekcją za pomocą elementu DataTemplate i konwertera.

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.