Udostępnij przez


Szczegółowe powiązanie danych systemu Windows

W tym artykule opisano funkcje powiązania danych WinUI przy użyciu interfejsów API w przestrzeni nazw Microsoft.UI.Xaml.Data.

Uwaga / Notatka

W tym temacie opisano szczegółowo funkcje powiązania danych. Krótkie, praktyczne wprowadzenie można znaleźć w temacie Omówienie powiązania danych.

Ważne interfejsy API

Wprowadzenie

Powiązanie danych to technika, która umożliwia interfejsowi użytkownika aplikacji efektywne wyświetlanie i synchronizowanie danych. Oddzielając obawy dotyczące danych od kwestii interfejsu użytkownika, upraszcza projektowanie aplikacji, zwiększa czytelność i zwiększa łatwość konserwacji.

Możesz użyć powiązania danych, aby po prostu wyświetlić wartości ze źródła danych po pierwszym wyświetleniu interfejsu użytkownika, ale nie reagować na zmiany w tych wartościach. Ten tryb powiązania nazywany jest jednorazowym i dobrze sprawdza się w przypadku wartości, która nie zmienia się w czasie działania. Alternatywnie możesz wybrać opcję "obserwowania" wartości i aktualizowania interfejsu użytkownika podczas ich zmiany. Ten tryb jest nazywany jednokierunkowym i dobrze sprawdza się w przypadku danych tylko do odczytu. Ostatecznie możesz wybrać zarówno obserwowanie, jak i aktualizowanie, aby zmiany wprowadzone przez użytkownika do wartości w interfejsie użytkownika zostały automatycznie wypchnięte z powrotem do źródła danych. Ten tryb jest nazywany dwukierunkowym i dobrze sprawdza się w przypadku danych odczytu i zapisu. Oto kilka przykładów.

  • Możesz użyć trybu jednorazowego, aby powiązać obraz ze zdjęciem bieżącego użytkownika.
  • Możesz użyć trybu jednokierunkowego, aby powiązać element ListView z kolekcją artykułów z wiadomościami w czasie rzeczywistym pogrupowanych według sekcji gazety.
  • Możesz użyć trybu dwukierunkowego, aby powiązać pole tekstowe z nazwą klienta w formularzu.

Niezależnie od trybu istnieją dwa rodzaje powiązań i zazwyczaj deklarujesz oba w adiustacji interfejsu użytkownika. Możesz użyć rozszerzenia znaczników {x:Bind} lub rozszerzenia znaczników {Binding}. Można nawet użyć kombinacji tych dwóch w tej samej aplikacji — nawet w tym samym elemecie interfejsu użytkownika. {x:Bind} był nowy w systemie UWP dla systemu Windows 10 i ma lepszą wydajność. Wszystkie szczegóły opisane w tym temacie dotyczą obu rodzajów powiązania, chyba że jawnie stwierdzimy inaczej.

Przykładowe aplikacje platformy UWP, które demonstrują element {x:Bind}

Przykładowe aplikacje platformy UWP, które demonstrują {Binding}

Każde powiązanie obejmuje te elementy

  • Źródło powiązania. To źródło udostępnia dane dla powiązania. Może to być wystąpienie dowolnej klasy, która ma elementy członkowskie, których wartości mają być wyświetlane w interfejsie użytkownika.
  • Element docelowy powiązania. Ten element docelowy jest właściwością DependencyPropertyelementu FrameworkElement w interfejsie użytkownika, który wyświetla dane.
  • Obiekt powiązania. Ten obiekt przesyła wartości danych ze źródła do obiektu docelowego i opcjonalnie z obiektu docelowego z powrotem do źródła. Obiekt powiązania jest tworzony w czasie ładowania XAML z rozszerzenia znaczników {x:Bind} lub {Binding} .

W poniższych sekcjach przyjrzysz się bliżej źródle powiązania, celowi powiązania i obiektowi powiązania. Sekcje łączą się poprzez przykład powiązania zawartości przycisku z właściwością ciągu o nazwie NextButtonText, która należy do klasy o nazwie HostViewModel.

Źródło powiązania

Oto podstawowa implementacja klasy, której można użyć jako źródła powiązania.

public class HostViewModel
{
    public HostViewModel()
    {
        NextButtonText = "Next";
    }

    public string NextButtonText { get; set; }
}

Ta implementacja HostViewModel i jej właściwość NextButtonText działają tylko w przypadku jednorazowego powiązania. Jednak powiązania jednokierunkowe i dwukierunkowe są bardzo powszechne. W tego rodzaju powiązania interfejs użytkownika automatycznie aktualizuje się w odpowiedzi na zmiany wartości danych źródła powiązania. Aby tego rodzaju powiązanie działało poprawnie, należy ustawić źródło powiązania jako widoczne dla obiektu powiązania. W naszym przykładzie, jeśli chcesz powiązać jednokierunkowo lub dwukierunkowo właściwość NextButtonText, wszelkie zmiany, które występują w czasie wykonywania wartości tej właściwości, muszą być widoczne dla obiektu powiązania.

Jednym ze sposobów jest utworzenie klasy reprezentującej źródło powiązania z klasy DependencyObject i uwidocznienie wartości danych za pomocą właściwości DependencyProperty*. W ten sposób element FrameworkElement staje się zauważalny. A FrameworkElement jest dobrym źródłem powiązania bezpośrednio z pudełka.

Bardziej uproszczony sposób obserwowania klasy — i niezbędny dla klas, które mają już klasę bazową — to zaimplementowanie elementu System.ComponentModel.INotifyPropertyChanged. Takie podejście obejmuje zaimplementowanie pojedynczego zdarzenia o nazwie PropertyChanged. Przykład użycia HostViewModel pokazano w poniższym kodzie.

...
using System.ComponentModel;
using System.Runtime.CompilerServices;
...
public class HostViewModel : INotifyPropertyChanged
{
    private string nextButtonText;

    public event PropertyChangedEventHandler PropertyChanged = delegate { };

    public HostViewModel()
    {
        NextButtonText = "Next";
    }

    public string NextButtonText
    {
        get { return nextButtonText; }
        set
        {
            nextButtonText = value;
            OnPropertyChanged();
        }
    }

    public void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        // Raise the PropertyChanged event, passing the name of the property whose value has changed.
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

NextButtonText Teraz właściwość jest zauważalna. Podczas tworzenia jednokierunkowego lub dwukierunkowego powiązania z tej właściwości (pokażemy, jak później), wynikowy obiekt powiązania subskrybuje PropertyChanged zdarzenie. Po wystąpieniu tego zdarzenia program obsługi obiektu powiązania odbiera argument zawierający nazwę zmienionej właściwości. W ten sposób obiekt powiązania wie, która wartość właściwości ma być odczytywana ponownie.

Aby nie trzeba było implementować wzorca pokazanego wcześniej wiele razy, jeśli używasz języka C#, możesz użyć klasy bazowej BindableBase , którą znajdziesz w przykładzie QuizGame (w folderze "Common"). Oto przykład tego, jak to wygląda.

public class HostViewModel : BindableBase
{
    private string nextButtonText;

    public HostViewModel()
    {
        NextButtonText = "Next";
    }

    public string NextButtonText
    {
        get { return nextButtonText; }
        set { SetProperty(ref nextButtonText, value); }
    }
}

PropertyChanged Wywołanie zdarzenia z argumentem String.Empty lub null wskazuje, że wszystkie właściwości niebędące indeksatorami w obiekcie powinny być ponownie odczytane. Zdarzenie można zgłosić, aby wskazać, że właściwości indeksatora obiektu zostały zmienione przy użyciu argumentu "Item[indexer]" dla określonych indeksatorów (gdzie indeksator jest wartością indeksatora) lub wartością "Item[]" dla wszystkich indeksatorów.

Źródło powiązania można traktować jako pojedynczy obiekt, którego właściwości zawierają dane, lub jako kolekcję obiektów. W kodzie języka C# można jednorazowo powiązać z obiektem, który implementuje listę<T> , aby wyświetlić kolekcję, która nie zmienia się w czasie wykonywania. W przypadku obserwowalnej kolekcji (śledzenie dodawania i usuwania elementów z kolekcji), zastosuj zamiast tego jednokierunkowe powiązanie z ObservableCollection<T>. Aby powiązać z własnymi klasami kolekcji, skorzystaj ze wskazówek w poniższej tabeli.

Scenario C# (CLR) C++/WinRT
Wiązanie z obiektem. Może być dowolnym obiektem. Może być dowolnym obiektem.
Pobieranie powiadomień o zmianie właściwości z powiązanego obiektu. Obiekt musi implementować element INotifyPropertyChanged. Obiekt musi implementować element INotifyPropertyChanged.
Wiązanie z kolekcją. Lista<T> IVectorIInspectable lub IBindableObservableVector. Zobacz kontrolki elementów XAML; wiązanie z kolekcją I kolekcjami C++/WinRTza pomocą języka C++/WinRT.
Pobieranie powiadomień o zmianie kolekcji z powiązanej kolekcji. ObservableCollection<T> IObservableVector z IInspectable. Na przykład winrt::single_threaded_observable_vector<T>.
Zaimplementuj kolekcję, która obsługuje powiązanie. Rozszerz interfejs List<T> lub zaimplementuj interfejs IList, IList<Object>, IEnumerable lub IEnumerable<Object>. Wiązanie z typem generycznym IList<T> i IEnumerable<T> nie jest obsługiwane. Zaimplementuj IVector dla IInspectable. Zobacz kontrolki elementów XAML; wiązanie z kolekcją I kolekcjami C++/WinRTza pomocą języka C++/WinRT.
Zaimplementuj kolekcję, która obsługuje powiadomienia o zmianie kolekcji. Rozszerz ObservableCollection<T> lub zaimplementuj (niegeneryczne) IList i INotifyCollectionChanged. Zaimplementuj IObservableVectorIInspectable lub IBindableObservableVector.
Zaimplementuj kolekcję, która obsługuje ładowanie przyrostowe. Rozszerz ObservableCollection<T> lub zaimplementuj (niegeneryczne) IList i INotifyCollectionChanged. Ponadto zaimplementuj ISupportIncrementalLoading. Zaimplementuj IObservableVector dla IInspectable lub IBindableObservableVector. Ponadto zaimplementuj ISupportIncrementalLoading

Kontrolki listy można powiązać z arbitralnie dużymi źródłami danych i nadal osiągać wysoką wydajność przy użyciu ładowania przyrostowego. Można na przykład powiązać kontrolki listy z wynikami zapytania obrazów Bing bez konieczności ładowania wszystkich wyników jednocześnie. Zamiast tego załadujesz tylko niektóre wyniki natychmiast i załaduj dodatkowe wyniki zgodnie z potrzebami. Aby obsługiwać ładowanie przyrostowe, należy zaimplementować funkcję ISupportIncrementalLoading w źródle danych obsługującym powiadomienia o zmianie kolekcji. Gdy aparat powiązania danych żąda większej ilości danych, źródło danych musi wysłać odpowiednie żądania, zintegrować wyniki, a następnie wysłać odpowiednie powiadomienia w celu zaktualizowania interfejsu użytkownika.

Obiekt docelowy powiązania

W poniższych dwóch przykładach Button.Content właściwość jest obiektem docelowym powiązania. Jego wartość jest ustawiona na rozszerzenie składni, które określa obiekt powiązania. W pierwszym przykładzie pokazano {x:Bind}, a w drugim przykładzie pokazano {Binding}. Deklarowanie powiązań w znacznikach jest typowym przypadkiem, ponieważ jest wygodne, czytelne i wspierane przez narzędzia. Jeśli jednak musisz, możesz uniknąć narzutu i imperatywnie (programowo) utworzyć wystąpienie klasy Binding .

<Button Content="{x:Bind ...}" ... />
<Button Content="{Binding ...}" ... />

Jeśli używasz języka C++/WinRT, musisz dodać atrybut BindableAttribute do dowolnej klasy środowiska uruchomieniowego, z którym chcesz użyć rozszerzenia znaczników {Binding} .

Ważne

Jeśli używasz języka C++/WinRT, atrybut BindableAttribute jest dostępny w zestawie SDK aplikacji systemu Windows. Bez tego atrybutu należy zaimplementować interfejsy ICustomPropertyProvider i ICustomProperty , aby móc używać rozszerzenia znaczników {Binding} .

Zadeklarowany obiekt powiązania przy użyciu elementu {x:Bind}

Przed utworzeniem znaczników {x:Bind} należy udostępnić klasę źródłową powiązania w klasie reprezentującej stronę znaczników. Dodaj właściwość (typ HostViewModel w tym przypadku) do MainWindow klasy okna.

namespace DataBindingInDepth
{
    public sealed partial class MainWindow : Window
    {
        public MainWindow()
        {
            this.InitializeComponent();
            ViewModel = new HostViewModel();
        }
    
        public HostViewModel ViewModel { get; set; }
    }
}

Po dodaniu atrybutu, można bliżej przyjrzeć się znacznikowi deklarującemu obiekt powiązania. W poniższym przykładzie użyto tego samego Button.Content celu powiązania, który wcześniej widziałeś w sekcji "Cel powiązania". Pokazuje, że element docelowy powiązania jest powiązany z właściwością HostViewModel.NextButtonText .

<!-- MainWindow.xaml -->
<Window x:Class="DataBindingInDepth.MainWindow" ... >
    <Button Content="{x:Bind Path=ViewModel.NextButtonText, Mode=OneWay}" ... />
</Window>

Zwróć uwagę na wartość, którą określisz dla Pathelementu . Okno interpretuje tę wartość we własnym kontekście. W takim przypadku ścieżka zaczyna się od odwoływania ViewModel się do właściwości, którą właśnie dodano do MainWindow strony. Ta właściwość zwraca HostViewModel wystąpienie, więc możesz umieścić kropkęHostViewModel.NextButtonText w tym obiekcie, aby uzyskać dostęp do właściwości. Należy określić Mode , aby zastąpić wartość domyślną {x:Bind} jednorazową.

Właściwość Path obsługuje różne opcje składniowe powiązania z zagnieżdżonymi właściwościami, właściwościami załączonymi oraz indeksatorami dla liczb całkowitych i ciągów znaków. Aby uzyskać więcej informacji, zobacz Składnia ścieżki właściwości. Powiązanie z indeksatorami ciągów daje efekt powiązania z właściwościami dynamicznymi bez konieczności implementowania elementu ICustomPropertyProvider. Inne ustawienia można znaleźć w temacie {x:Bind} markup extension (Rozszerzenie znaczników {x:Bind}).

Aby zilustrować, że HostViewModel.NextButtonText właściwość jest zauważalna, dodaj procedurę Click obsługi zdarzeń do przycisku i zaktualizuj wartość HostViewModel.NextButtonText. Skompiluj, uruchom i kliknij przycisk , aby wyświetlić wartość aktualizacji przycisku Content .

// MainWindow.xaml.cs
private void Button_Click(object sender, RoutedEventArgs e)
{
    ViewModel.NextButtonText = "Updated Next button text";
}

Uwaga / Notatka

Zmiany w TextBox.Text są wysyłane do źródła powiązanego w trybie dwukierunkowym, gdy TextBox traci focus, a nie po każdym naciśnięciu klawisza przez użytkownika.

DataTemplate i x:DataType

Wewnątrz elementu DataTemplate (bez względu na to, czy używasz go jako szablonu elementu, szablonu zawartości lub szablonu nagłówka), wartość Path elementu nie jest interpretowana w kontekście okna. Zamiast tego działa w kontekście obiektu danych, który jest używany do tworzenia szablonów. W przypadku użycia {x:Bind} w szablonie danych można zweryfikować jego powiązania w czasie kompilacji i wygenerować dla nich wydajny kod. W tym DataTemplate celu należy zadeklarować typ obiektu danych przy użyciu x:DataType. Poniższy przykład może służyć jako ItemTemplate kontrolka elementów powiązana z kolekcją SampleDataGroup obiektów.

<DataTemplate x:Key="SimpleItemTemplate" x:DataType="data:SampleDataGroup">
    <StackPanel Orientation="Vertical" Height="50">
      <TextBlock Text="{x:Bind Title}"/>
      <TextBlock Text="{x:Bind Description}"/>
    </StackPanel>
  </DataTemplate>

Słabo wpisane obiekty w ścieżce

Załóżmy, że masz typ o nazwie SampleDataGroup , który implementuje właściwość ciągu o nazwie Title. Masz również właściwość MainWindow.SampleDataGroupAsObject, która jest typu object, ale w rzeczywistości zwraca wystąpienie SampleDataGroup. Powiązanie <TextBlock Text="{x:Bind SampleDataGroupAsObject.Title}"/> powoduje błąd kompilacji, ponieważ właściwość Title nie jest dostępna w typie object. Aby naprawić ten błąd, dodaj rzutowanie do składni Path w następujący sposób: <TextBlock Text="{x:Bind ((data:SampleDataGroup)SampleDataGroupAsObject).Title}"/>. Oto inny przykład, w którym Element jest zadeklarowany jako , object ale jest w rzeczywistości : TextBlock<TextBlock Text="{x:Bind Element.Text}"/>. Rzutowanie rozwiązuje problem: <TextBlock Text="{x:Bind ((TextBlock)Element).Text}"/>.

Jeśli dane są ładowane asynchronicznie

Klasy częściowe dla twoich windows generują kod do obsługi {x:Bind} w czasie kompilacji. Te pliki można znaleźć w obj folderze z nazwami takimi jak (dla języka C#). <view name>.g.cs Wygenerowany kod zawiera procedurę obsługi dla zdarzenia ładowania okna. Ta procedura obsługi wywołuje metodę Initialize w wygenerowanej klasie, która reprezentuje powiązania okna. Initialize wywołuje metodę Update , aby rozpocząć przenoszenie danych między źródłem powiązania a elementem docelowym. Loading jest wywoływany tuż przed pierwszym przekazaniem miary okna lub kontrolki użytkownika. Jeśli dane są ładowane asynchronicznie, mogą nie być gotowe w momencie wywołania Initialize. Po załadowaniu danych można wymusić jednorazowe powiązania w celu zainicjowania przez wywołanie metody this.Bindings.Update();. Jeśli potrzebujesz powiązań na jeden raz dla danych ładowanych asynchronicznie, znacznie tańsze jest ich zainicjowanie w ten sposób niż ustanawianie powiązań jednokierunkowych i nasłuchiwanie zmian. Jeśli dane nie zostaną poddane szczegółowym zmianom i prawdopodobnie zostaną zaktualizowane w ramach określonej akcji, możesz jednorazowo wprowadzić powiązania i wymusić ręczną aktualizację w dowolnym momencie za pomocą wywołania metody Update.

Uwaga / Notatka

{x:Bind} nie nadaje się do scenariuszy związanych z późnym opóźnieniem, takich jak nawigowanie po strukturze słownika obiektu JSON ani wpisywanie duck. "Duck typing" jest słabą formą pisania na podstawie dopasowań leksykalnych na nazwach właściwości (jak w, "jeśli chodzi, pływa i quacks jak kaczka, to jest kaczka"). W przypadku wpisywania kaczki powiązanie Age z właściwością byłoby równie zadowalające z Person obiektu lub Wine (zakładając, że te typy mają Age właściwość). W tych scenariuszach użyj {Binding} rozszerzenia znaczników.

Obiekt powiązania zadeklarowany przy użyciu {Binding}

Jeśli używasz języka C++/WinRT, dodaj atrybut BindableAttribute do dowolnej klasy środowiska uruchomieniowego, z którą chcesz powiązać podczas korzystania z rozszerzenia znaczników {Binding} . Aby użyć elementu {x:Bind}, nie potrzebujesz tego atrybutu.

// HostViewModel.idl
// Add this attribute:
[Microsoft.UI.Xaml.Data.Bindable]
runtimeclass HostViewModel : Microsoft.UI.Xaml.Data.INotifyPropertyChanged
{
    HostViewModel();
    String NextButtonText;
}

Ważne

Jeśli używasz języka C++/WinRT, atrybut BindableAttribute jest dostępny w zestawie SDK aplikacji systemu Windows. Bez tego atrybutu należy zaimplementować interfejsy ICustomPropertyProvider i ICustomProperty , aby móc używać rozszerzenia znaczników {Binding} .

Domyślnie {Binding} przyjmuje, że wiążesz z DataContext twojego okna oznaczeń. Dlatego ustaw DataContext okna, aby był wystąpieniem swojej klasy źródłowej powiązania (w tym przypadku typu HostViewModel). W poniższym przykładzie przedstawiono znaczniki, które deklarują obiekt powiązania. Używa on tego samego Button.Content obiektu docelowego powiązania wykorzystywanego wcześniej w sekcji "Obiekt docelowy powiązania" i wiąże się z właściwością HostViewModel.NextButtonText.

<Window xmlns:viewmodel="using:DataBindingInDepth" ... >
    <Window.DataContext>
        <viewmodel:HostViewModel x:Name="viewModelInDataContext"/>
    </Window.DataContext>
    ...
    <Button Content="{Binding Path=NextButtonText}" ... />
</Window>
// MainWindow.xaml.cs
private void Button_Click(object sender, RoutedEventArgs e)
{
    viewModelInDataContext.NextButtonText = "Updated Next button text";
}

Zwróć uwagę na wartość określoną dla Path. Obiekt DataContext okna interpretuje tę wartość, która w tym przykładzie jest ustawiona na wystąpienie klasy HostViewModel. Ścieżka odwołuje się do HostViewModel.NextButtonText właściwości . Można pominąć Mode element, ponieważ domyślne ustawienie {Binding} w trybie jednokierunkowym działa w tym przypadku.

Wartość domyślna elementu DataContext dla elementu interfejsu użytkownika to dziedziczona wartość elementu nadrzędnego. Możesz zastąpić to ustawieniem domyślnym, ustawiając DataContext jawnie, co z kolei jest dziedziczone przez elementy podrzędne domyślnie. Jawne ustawienie DataContext elementu jest przydatne, gdy chcesz mieć wiele powiązań, które używają tego samego źródła.

Obiekt powiązania ma Source właściwość , która domyślnie jest ustawiona na Wartość DataContext elementu interfejsu użytkownika, na którym zadeklarowano powiązanie. Tę wartość domyślną można zastąpić, ustawiając Sourcewartość , RelativeSourcelub ElementName jawnie w powiązaniu (zobacz {Binding} ), aby uzyskać szczegółowe informacje.

Wewnątrz elementu DataTemplate obiekt DataContext jest automatycznie ustawiany na obiekt danych, który jest szablonowany. Poniższy przykład może służyć jako ItemTemplate kontrolka elementów powiązana z kolekcją dowolnego typu, która ma właściwości tekstowe o nazwie Title i Description.

<DataTemplate x:Key="SimpleItemTemplate">
    <StackPanel Orientation="Vertical" Height="50">
      <TextBlock Text="{Binding Title}"/>
      <TextBlock Text="{Binding Description"/>
    </StackPanel>
  </DataTemplate>

Uwaga / Notatka

Domyślnie zmiany właściwości TextBox.Text są wysyłane do dwukierunkowego źródła powiązanego, gdy pole TextBox traci fokus. Aby spowodować wysłanie zmian po każdym naciśnięciu użytkownika, ustaw UpdateSourceTrigger wartość PropertyChanged na powiązanie w znaczniku. Możesz również całkowicie przejąć kontrolę nad tym, kiedy zmiany są wysyłane do źródła, ustawiając wartość UpdateSourceTrigger .Explicit Następnie obsługujesz zdarzenia w polu tekstowym (zazwyczaj TextBox.TextChanged), wywołaj metodę GetBindingExpression w obiekcie docelowym, aby uzyskać obiekt BindingExpression , a na koniec wywołaj metodę BindingExpression.UpdateSource , aby programowo zaktualizować źródło danych.

Właściwość Path obsługuje różne opcje składni powiązania z zagnieżdżonymi właściwościami, dołączonymi właściwościami oraz indeksatorami liczb całkowitych i ciągów. Aby uzyskać więcej informacji, zobacz Składnia ścieżki właściwości. Powiązanie z indeksatorami ciągów daje efekt powiązania z właściwościami dynamicznymi bez konieczności implementowania elementu ICustomPropertyProvider. Właściwość ElementName jest przydatna w przypadku powiązania elementu-element. Właściwość RelativeSource ma kilka zastosowań, z których jedna jest bardziej zaawansowaną alternatywą dla powiązania szablonu wewnątrz elementu ControlTemplate. Aby uzyskać inne ustawienia, zobacz rozszerzenie znaczników {Binding} i klasę Binding .

Co zrobić, jeśli źródło i obiekt docelowy nie są tego samego typu?

Jeśli chcesz kontrolować widoczność elementu interfejsu użytkownika na podstawie wartości właściwości logicznej, lub jeśli chcesz renderować element interfejsu użytkownika z kolorem, który jest funkcją zakresu lub trendu wartości liczbowej, lub jeśli chcesz wyświetlić wartość daty i/lub godziny we właściwości elementu interfejsu użytkownika, która oczekuje ciągu, następnie należy przekonwertować wartości z jednego typu na inny. Istnieją przypadki, w których właściwym rozwiązaniem jest udostępnienie innej właściwości odpowiedniego typu z klasy źródłowej powiązania, zachowując tam hermetyzowaną i testowalną logikę konwersji. Jednak to rozwiązanie nie jest elastyczne ani skalowalne, jeśli masz dużą liczbę lub duże kombinacje właściwości źródłowych i docelowych. W takim przypadku masz kilka opcji:

  • Jeśli używasz {x:Bind} polecenia , możesz powiązać bezpośrednio z funkcją, aby wykonać tę konwersję
  • Możesz też określić konwerter wartości, który jest obiektem przeznaczonym do przeprowadzenia konwersji

Konwertery wartości

Oto konwerter wartości, odpowiedni dla powiązania jednorazowego lub jednokierunkowego, który konwertuje wartość DateTime na wartość zawierającą string miesiąc. Klasa implementuje element IValueConverter.

public class DateToStringConverter : IValueConverter
{
    // Define the Convert method to convert a DateTime value to 
    // a month string.
    public object Convert(object value, Type targetType, 
        object parameter, string language)
    {
        // value is the data from the source object.
        DateTime thisDate = (DateTime)value;
        int monthNum = thisDate.Month;
        string month;
        switch (monthNum)
        {
            case 1:
                month = "January";
                break;
            case 2:
                month = "February";
                break;
            default:
                month = "Month not found";
                break;
        }
        // Return the value to pass to the target.
        return month;
    }

    // ConvertBack is not implemented for a OneWay binding.
    public object ConvertBack(object value, Type targetType, 
        object parameter, string language)
    {
        throw new NotImplementedException();
    }
}

Oto jak używasz tego konwertera wartości w znaczniku obiektu powiązania.

<UserControl.Resources>
  <local:DateToStringConverter x:Key="Converter1"/>
</UserControl.Resources>
...
<TextBlock Grid.Column="0" 
  Text="{x:Bind ViewModel.Month, Converter={StaticResource Converter1}}"/>
<TextBlock Grid.Column="0" 
  Text="{Binding Month, Converter={StaticResource Converter1}}"/>

Aparat powiązania wywołuje metody Convert i ConvertBack , jeśli parametr Konwerter jest zdefiniowany dla powiązania. Gdy dane są przekazywane ze źródła, aparat powiązania wywołuje Convert i przekazuje zwrócone dane do obiektu docelowego. Gdy dane są przekazywane z obiektu docelowego (dla powiązania dwukierunkowego), aparat powiązania wywołuje ConvertBack i przekazuje zwrócone dane do źródła.

Konwerter ma również opcjonalne parametry: ConverterLanguage, który umożliwia określenie języka, który ma być używany w konwersji, i ConverterParameter, który umożliwia przekazanie parametru dla logiki konwersji. Przykład, który używa parametru konwertera, zobacz IValueConverter.

Uwaga / Notatka

Jeśli w konwersji wystąpi błąd, nie zgłaszaj wyjątku. Zamiast tego zwróć wartość DependencyProperty.UnsetValue, co spowoduje zatrzymanie transferu danych.

Aby wyświetlić wartość domyślną, która ma być używana za każdym razem, gdy nie można rozpoznać źródła powiązania, ustaw FallbackValue właściwość obiektu powiązania w adiustacji. Jest to przydatne do obsługi błędów konwersji i formatowania. Przydatne jest również powiązanie z właściwościami źródłowymi, które mogą nie istnieć we wszystkich obiektach w powiązanej kolekcji typów heterogenicznych.

Jeśli powiążesz kontrolkę tekstową z wartością, która nie jest ciągiem, aparat powiązania danych przekonwertuje wartość na ciąg. Jeśli wartość jest typem odwołania, aparat powiązania danych pobierze wartość ciągu, wywołując metodę ICustomPropertyProvider.GetStringRepresentation lub IStringable.ToString , jeśli jest dostępna, i w przeciwnym razie wywoła metodę Object.ToString. Należy jednak pamiętać, że aparat powiązania zignoruje każdą ToString implementację, która ukrywa implementację klasy bazowej. Implementacje podklasy powinny zastąpić metodę klasy ToString bazowej. Podobnie w językach natywnych wszystkie obiekty zarządzane wydają się implementować ICustomPropertyProvider i IStringable. Jednak wszystkie wywołania metody GetStringRepresentation i IStringable.ToString są kierowane do Object.ToString lub zastąpienia tej metody i nigdy nie do nowej ToString implementacji, która ukrywa implementację klasy bazowej.

Uwaga / Notatka

Zestaw narzędzi Społeczności systemu Windows udostępnia element BoolToVisibilityConverter. Konwerter mapuje true wartość Visible wyliczenia i false na Collapsed , aby można było powiązać Visibility właściwość z wartością logiczną bez tworzenia konwertera. Aby użyć konwertera, projekt musi dodać pakiet NuGet CommunityToolkit.WinUI.Converters .

Powiązanie funkcji w elemencie {x:Bind}

{x:Bind} umożliwia wykonanie ostatniego kroku w ścieżce powiązania jako funkcji. Ta funkcja służy do przeprowadzania konwersji lub tworzenia powiązań, które zależą od więcej niż jednej właściwości. Aby uzyskać więcej informacji, zobacz Funkcje w pliku x:Bind.

Powiązanie elementu-element-element

Właściwość jednego elementu XAML można powiązać z właściwością innego elementu XAML. Oto przykład, jak to powiązanie jest widoczne w znacznikach.

<TextBox x:Name="myTextBox" />
<TextBlock Text="{x:Bind myTextBox.Text, Mode=OneWay}" />

Słowniki zasobów z {x:Bind}

Rozszerzenie znaczników {x:Bind} zależy od generowania kodu, dlatego potrzebuje pliku za pomocą kodu zawierającego konstruktor InitializeComponent wywołujący (w celu zainicjowania wygenerowanego kodu). Aby ponownie użyć słownika zasobów, utwórz wystąpienie typu tego słownika (co spowoduje wywołanie InitializeComponent) zamiast odwoływać się do jego nazwy pliku. Oto przykład tego, co zrobić, jeśli masz istniejący słownik zasobów i chcesz go użyć {x:Bind} .

<!-- TemplatesResourceDictionary.xaml -->
<ResourceDictionary
    x:Class="ExampleNamespace.TemplatesResourceDictionary"
    .....
    xmlns:examplenamespace="using:ExampleNamespace">
    
    <DataTemplate x:Key="EmployeeTemplate" x:DataType="examplenamespace:IEmployee">
        <Grid>
            <TextBlock Text="{x:Bind Name}"/>
        </Grid>
    </DataTemplate>
</ResourceDictionary>
// TemplatesResourceDictionary.xaml.cs
using Microsoft.UI.Xaml.Data;
 
namespace ExampleNamespace
{
    public partial class TemplatesResourceDictionary
    {
        public TemplatesResourceDictionary()
        {
            InitializeComponent();
        }
    }
}
<!-- MainWindow.xaml -->
<Window x:Class="ExampleNamespace.MainWindow"
    ....
    xmlns:examplenamespace="using:ExampleNamespace">

    <Window.Resources>
        <ResourceDictionary>
            .... 
            <ResourceDictionary.MergedDictionaries>
                <examplenamespace:TemplatesResourceDictionary/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>
</Window>

Mieszanie {x:Bind} i {Binding} w stylu wielokrotnego użytku

W poprzednim przykładzie pokazano, jak używać {x:Bind} w narzędziu DataTemplates. Można również utworzyć style wielokrotnego użytku, które łączą zarówno rozszerzenia znaczników, jak {x:Bind} i {Binding} . Ta kombinacja jest przydatna, gdy chcesz powiązać niektóre właściwości ze znanymi wartościami w czasie kompilacji przy użyciu {x:Bind} oraz inne właściwości z wartościami DataContext dostępnymi w czasie wykonywania przy użyciu {Binding}.

W poniższym przykładzie pokazano, jak utworzyć styl przycisku wielokrotnego użytku, który używa obu metod powiązania:

TemplatesResourceDictionary.xaml

<!-- TemplatesResourceDictionary.xaml -->
<ResourceDictionary
    x:Class="ExampleNamespace.TemplatesResourceDictionary"
    .....
    xmlns:examplenamespace="using:ExampleNamespace">
    
    <!-- DataTemplate using x:Bind -->
    <DataTemplate x:Key="EmployeeTemplate" x:DataType="examplenamespace:IEmployee">
        <Grid>
            <TextBlock Text="{x:Bind Name}"/>
        </Grid>
    </DataTemplate>
    
    <!-- Style that mixes x:Bind and Binding -->
    <Style x:Key="CustomButtonStyle" TargetType="Button">
        <Setter Property="Background" Value="{Binding ButtonBackgroundBrush}"/>
        <Setter Property="Foreground" Value="{Binding ButtonForegroundBrush}"/>
        <Setter Property="FontSize" Value="16"/>
        <Setter Property="Margin" Value="4"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Button">
                    <Border x:Name="RootBorder"
                            Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}"
                            CornerRadius="4">
                        <StackPanel Orientation="Horizontal" 
                                    HorizontalAlignment="Center"
                                    VerticalAlignment="Center">
                            <!-- x:Bind to a static property or page-level property -->
                            <Ellipse Width="8" Height="8" 
                                     Fill="{x:Bind DefaultIndicatorBrush}" 
                                     Margin="0,0,8,0"/>
                            <!-- Binding to DataContext -->
                            <ContentPresenter x:Name="ContentPresenter"
                                              Content="{TemplateBinding Content}"
                                              Foreground="{TemplateBinding Foreground}"
                                              FontSize="{TemplateBinding FontSize}"/>
                        </StackPanel>
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="CommonStates">
                                <VisualState x:Name="Normal"/>
                                <VisualState x:Name="PointerOver">
                                    <VisualState.Setters>
                                        <!-- Binding to DataContext for hover color -->
                                        <Setter Target="RootBorder.Background" 
                                                Value="{Binding ButtonHoverBrush}"/>
                                    </VisualState.Setters>
                                </VisualState>
                                <VisualState x:Name="Pressed">
                                    <VisualState.Setters>
                                        <!-- x:Bind to a compile-time known resource -->
                                        <Setter Target="RootBorder.Background" 
                                                Value="{x:Bind DefaultPressedBrush}"/>
                                    </VisualState.Setters>
                                </VisualState>
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

TemplatesResourceDictionary.xaml.cs

// TemplatesResourceDictionary.xaml.cs
using Microsoft.UI;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Media;
 
namespace ExampleNamespace
{
    public partial class TemplatesResourceDictionary
    {
        public TemplatesResourceDictionary()
        {
            InitializeComponent();
        }
        
        // Properties for x:Bind - these are compile-time bound
        public SolidColorBrush DefaultIndicatorBrush { get; } = 
            new SolidColorBrush(Colors.Green);
            
        public SolidColorBrush DefaultPressedBrush { get; } = 
            new SolidColorBrush(Colors.DarkGray);
    }
}

Użycie w pliku MainWindow.xaml z modelem ViewModel udostępniającym wartości środowiska uruchomieniowego:

<!-- MainWindow.xaml -->
<Window x:Class="ExampleNamespace.MainWindow"
    ....
    xmlns:examplenamespace="using:ExampleNamespace">

    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <examplenamespace:TemplatesResourceDictionary/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>

    <Grid>
        <Grid.DataContext>
            <examplenamespace:ButtonThemeViewModel/>
        </Grid.DataContext>
        
        <StackPanel Margin="20">
            <!-- These buttons use the mixed binding style -->
            <Button Content="Save" Style="{StaticResource CustomButtonStyle}"/>
            <Button Content="Cancel" Style="{StaticResource CustomButtonStyle}"/>
        </StackPanel>
    </Grid>
</Window>

ButtonThemeViewModel.cs (element DataContext, który udostępnia wartości powiązań środowiska uruchomieniowego):

using System.ComponentModel;
using Microsoft.UI;
using Microsoft.UI.Xaml.Media;

namespace ExampleNamespace
{
    public class ButtonThemeViewModel : INotifyPropertyChanged
    {
        private SolidColorBrush _buttonBackgroundBrush = new SolidColorBrush(Colors.LightBlue);
        private SolidColorBrush _buttonForegroundBrush = new SolidColorBrush(Colors.DarkBlue);
        private SolidColorBrush _buttonHoverBrush = new SolidColorBrush(Colors.LightCyan);

        public SolidColorBrush ButtonBackgroundBrush
        {
            get => _buttonBackgroundBrush;
            set
            {
                _buttonBackgroundBrush = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ButtonBackgroundBrush)));
            }
        }

        public SolidColorBrush ButtonForegroundBrush
        {
            get => _buttonForegroundBrush;
            set
            {
                _buttonForegroundBrush = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ButtonForegroundBrush)));
            }
        }

        public SolidColorBrush ButtonHoverBrush
        {
            get => _buttonHoverBrush;
            set
            {
                _buttonHoverBrush = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ButtonHoverBrush)));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }
}

W tym przykładzie:

  • {Binding} jest używany dla właściwości, które zależą od DataContext (ButtonBackgroundBrush, ButtonForegroundBrush, ButtonHoverBrush)
  • {x:Bind} jest używany do właściwości, które są znane w czasie kompilacji i należą do samego zasobuDictionary (DefaultIndicatorBrush, DefaultPressedBrush)
  • Styl jest wielokrotnego użytku i można go zastosować do dowolnego przycisku
  • Motywy środowiska uruchomieniowego są możliwe za pośrednictwem elementu DataContext, a jednocześnie korzystają z wydajności elementów statycznych {x:Bind}

Powiązanie zdarzeń i ICommand

Aplikacja {x:Bind} obsługuje funkcję o nazwie powiązanie zdarzeń. Za pomocą tej funkcji można określić obsługę zdarzenia przy użyciu powiązania. Ta funkcja jest dodatkową opcją obsługi zdarzeń, oprócz obsługi zdarzeń za pomocą metod w pliku code-behind. Załóżmy, że masz program ListViewDoubleTapped obsługujący zdarzenia w klasie MainWindow.

public sealed partial class MainWindow : Window
{
    ...
    public void ListViewDoubleTapped()
    {
        // Handle double-tapped logic
    }
}

Zdarzenie DoubleTapped obiektu ListView można powiązać z metodą w systemie MainWindow w następujący sposób.

<ListView DoubleTapped="{x:Bind ListViewDoubleTapped}" />

Nie można użyć metod przeciążonych do obsługi zdarzenia za pomocą tej techniki. Ponadto jeśli metoda, która obsługuje zdarzenie, ma parametry, wszystkie z nich muszą być przypisywane z typów wszystkich parametrów zdarzenia, odpowiednio. W tym przypadku ListViewDoubleTapped nie jest przeciążony i nie ma parametrów (ale nadal będzie prawidłowy, nawet jeśli wziął dwa object parametry).

Technika powiązania zdarzeń jest podobna do implementowania i używania poleceń. Polecenie to właściwość zwracająca obiekt implementujący interfejs ICommand . Zarówno element {x:Bind} jak i {Binding} współpracują z poleceniami. Aby nie trzeba było wielokrotnie implementować wzorca poleceń, możesz użyć DelegateCommand klasy pomocniczej, którą można znaleźć w przykładzie QuizGame UWP (w folderze "Common").

Wiązanie z kolekcją folderów lub plików

Możesz użyć interfejsów API w przestrzeni nazw Windows.Storage , aby pobrać dane folderów i plików w spakowanych aplikacjach zestawu SDK aplikacji systemu Windows. Jednak różne GetFilesAsyncmetody , GetFoldersAsynci GetItemsAsync nie zwracają wartości, które są odpowiednie do powiązania z kontrolkami listy. Zamiast tego należy powiązać z zwracanymi wartościami metod GetVirtualizedFilesVector, GetVirtualizedFoldersVector i GetVirtualizedItemsVector klasy FileInformationFactory. Poniższy przykład kodu z przykładu StorageDataSource i GetVirtualizedFilesVector UWP przedstawia typowy wzorzec użycia. Pamiętaj, aby zadeklarować funkcję picturesLibrary w manifeście pakietu aplikacji i potwierdzić, że w folderze biblioteki obrazów znajdują się obrazy.

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    var library = Windows.Storage.KnownFolders.PicturesLibrary;
    var queryOptions = new Windows.Storage.Search.QueryOptions();
    queryOptions.FolderDepth = Windows.Storage.Search.FolderDepth.Deep;
    queryOptions.IndexerOption = Windows.Storage.Search.IndexerOption.UseIndexerWhenAvailable;

    var fileQuery = library.CreateFileQueryWithOptions(queryOptions);

    var fif = new Windows.Storage.BulkAccess.FileInformationFactory(
        fileQuery,
        Windows.Storage.FileProperties.ThumbnailMode.PicturesView,
        190,
        Windows.Storage.FileProperties.ThumbnailOptions.UseCurrentScale,
        false
        );

    var dataSource = fif.GetVirtualizedFilesVector();
    this.PicturesListView.ItemsSource = dataSource;
}

Zazwyczaj ta metoda służy do tworzenia widoku informacji o plikach i folderach tylko do odczytu. Powiązania dwukierunkowe można tworzyć we właściwościach pliku i folderu, na przykład aby umożliwić użytkownikom ocenianie utworu w widoku muzyki. Jednak wszelkie zmiany nie są utrwalane, dopóki nie wywołasz odpowiedniej SavePropertiesAsync metody (na przykład MusicProperties.SavePropertiesAsync). Zmiany należy zatwierdzić, gdy element utraci fokus, ponieważ ta akcja wyzwala resetowanie zaznaczenia.

Należy pamiętać, że powiązanie dwukierunkowe przy użyciu tej techniki działa tylko z indeksowanymi lokalizacjami, takimi jak Muzyka. Możesz określić, czy lokalizacja jest indeksowana, wywołując metodę FolderInformation.GetIndexedStateAsync .

Należy również pamiętać, że zwirtualizowany wektor może zwracać null niektóre elementy przed wypełnieniem ich wartości. Na przykład należy sprawdzić null , czy przed użyciem wartości SelectedItem kontrolki listy powiązanej z wektorem zwirtualizowanym lub zamiast tego użyj polecenia SelectedIndex .

Wiązanie z danymi pogrupowane według klucza

Jeśli weźmiesz płaską kolekcję elementów (na przykład książek reprezentowanych przez klasę BookSku) i pogrupujesz elementy, używając wspólnej właściwości jako klucz (na przykład właściwości BookSku.AuthorName), to wynik nazywa się danymi pogrupowanymi. Pogrupowanie danych nie jest już kolekcją płaską. Pogrupowane dane to kolekcja obiektów grupowych, w których każdy obiekt grupy ma:

  • klucz i
  • kolekcja elementów, których właściwość pasuje do tego klucza.

Aby wrócić do przykładu książek, wynik grupowania książek według nazwy autora powoduje kolekcję grup nazw autorów, w których każda grupa ma:

  • klucz, który jest nazwą autora i
  • kolekcja BookSku obiektów, których AuthorName właściwość pasuje do klucza grupy.

Ogólnie rzecz biorąc, aby wyświetlić kolekcję, należy powiązać element ItemsSource kontrolki elementów (np. ListView lub GridView) bezpośrednio z właściwością zwracającą kolekcję. Jeśli jest to płaska kolekcja elementów, nie musisz wykonywać żadnych specjalnych czynności. Jeśli jednak jest to kolekcja obiektów grupowych (tak jak w przypadku powiązania z pogrupowanych danych), potrzebne są usługi obiektu pośredniego o nazwie CollectionViewSource , który znajduje się między kontrolką elementów a źródłem powiązania. Powiąż element z CollectionViewSource właściwością, która zwraca zgrupowane dane, i powiążesz kontrolkę elementy z kontrolką CollectionViewSource. Dodatkową wartością elementu CollectionViewSource jest to, że śledzi bieżący element, dzięki czemu można zachować więcej niż jedną kontrolkę elementów w synchronizacji przez powiązanie ich wszystkich z tymi samymi CollectionViewSourceelementami . Dostęp do bieżącego elementu można również uzyskać programowo za pomocą właściwości ICollectionView.CurrentItem obiektu zwróconego przez właściwość CollectionViewSource.View .

Aby aktywować obiekt grupowania kolekcji CollectionViewSource, ustaw dla parametru IsSourceGrouped wartość true. Niezależnie od tego, czy należy również ustawić właściwość ItemsPath , zależy od tego, jak tworzysz obiekty grupy. Istnieją dwa sposoby tworzenia obiektu grupy: wzorzec "is-a-group" i wzorzec "has-a-group". We wzorcu "is-a-group" obiekt grupy pochodzi z typu kolekcji (na przykład List<T>), więc obiekt grupy jest faktycznie grupą elementów. Za pomocą tego wzorca nie trzeba ustawiać wartości ItemsPath. We wzorcu "has-a-group" obiekt grupy ma co najmniej jedną właściwość typu kolekcji (na przykład List<T>), więc grupa "ma" grupę elementów w postaci właściwości (lub kilka grup elementów w postaci kilku właściwości). Za pomocą tego wzorca należy ustawić ItemsPath nazwę właściwości zawierającej grupę elementów.

Poniższy przykład ilustruje wzorzec "posiada grupę". Klasa okien ma właściwość o nazwie DataContext, która zwraca wystąpienie naszego modelu widoku. Właściwość CollectionViewSource wiąże się z Authors właściwością modelu widoku (Authorsjest kolekcją obiektów grupy), a także określa, że jest Author.BookSkus to właściwość zawierająca zgrupowane elementy. Na koniec element GridView jest powiązany z elementem CollectionViewSourcei ma zdefiniowany styl grupy, aby umożliwić renderowanie elementów w grupach.

<Window.Resources>
    <CollectionViewSource
    x:Name="AuthorHasACollectionOfBookSku"
    Source="{x:Bind ViewModel.Authors}"
    IsSourceGrouped="true"
    ItemsPath="BookSkus"/>
</Window.Resources>
...
<GridView
ItemsSource="{x:Bind AuthorHasACollectionOfBookSku}" ...>
    <GridView.GroupStyle>
        <GroupStyle
            HeaderTemplate="{StaticResource AuthorGroupHeaderTemplateWide}" ... />
    </GridView.GroupStyle>
</GridView>

Wzorzec "is-a-group" można zaimplementować na jeden z dwóch sposobów. Jednym ze sposobów jest utworzenie własnej klasy grupy. Utwórz klasę na podstawie List<T> (gdzie T jest typem elementów). Na przykład public class Author : List<BookSku>. Drugim sposobem jest użycie wyrażenia LINQ do dynamicznego tworzenia obiektów grupy (i klasy grupowej) z takich wartości właściwości elementów BookSku . Takie podejście — utrzymywanie tylko płaskiej listy elementów i grupowanie ich razem na bieżąco — jest typowe dla aplikacji, która uzyskuje dostęp do danych z usługi w chmurze. Elastyczność grupowania książek według autora lub gatunku (na przykład) jest możliwa bez konieczności używania specjalnych klas grupowych, takich jak Autor i Gatunek.

Poniższy przykład ilustruje wzorzec "is-a-group" przy użyciu LINQ. Tym razem grupujemy książki według gatunku, wyświetlane z nazwą gatunku w nagłówkach grupy. To grupowanie jest wskazywane przez ścieżkę właściwości "Key" w odniesieniu do wartości klucza grupy Key.

using System.Linq;
...
private IOrderedEnumerable<IGrouping<string, BookSku>> genres;

public IOrderedEnumerable<IGrouping<string, BookSku>> Genres
{
    get
    {
        if (genres == null)
        {
            genres = from book in bookSkus
                     group book by book.genre into grp
                     orderby grp.Key
                     select grp;
        }
        return genres;
    }
}

Pamiętaj, że korzystając z szablonów danych {x:Bind}, należy wskazać typ, do którego się wiąże, poprzez ustawienie wartości x:DataType. Jeśli typ jest ogólny, nie można wyrazić tego w znacznikach, więc musisz użyć { Binding} zamiast tego w szablonie nagłówka stylu grupy.

    <Grid.Resources>
        <CollectionViewSource x:Name="GenreIsACollectionOfBookSku"
        Source="{x:Bind Genres}"
        IsSourceGrouped="true"/>
    </Grid.Resources>
    <GridView ItemsSource="{x:Bind GenreIsACollectionOfBookSku}">
        <GridView.ItemTemplate x:DataType="local:BookTemplate">
            <DataTemplate>
                <TextBlock Text="{x:Bind Title}"/>
            </DataTemplate>
        </GridView.ItemTemplate>
        <GridView.GroupStyle>
            <GroupStyle>
                <GroupStyle.HeaderTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Key}"/>
                    </DataTemplate>
                </GroupStyle.HeaderTemplate>
            </GroupStyle>
        </GridView.GroupStyle>
    </GridView>

Kontrolka SemanticZoom to doskonały sposób na wyświetlanie i nawigowanie po zgrupowanych danych przez użytkowników. Przykładowa aplikacja bookstore2 platformy UNIWERSALNEJ systemu Windows ilustruje sposób używania pliku SemanticZoom. W tej aplikacji możesz wyświetlić listę książek pogrupowanych według autora (widok powiększenia) lub pomniejsić, aby wyświetlić listę autorów (widok powiększony). Lista skoków zapewnia znacznie szybszą nawigację niż przewijanie listy książek. Widoki powiększenia i powiększenia są rzeczywiście ListView lub GridView kontrolki powiązane z tym samym CollectionViewSourceelementem .

Ilustracja semanticZoom

Po powiązaniu z danymi hierarchicznymi , takimi jak podkategorie w kategoriach, można wybrać wyświetlanie poziomów hierarchicznych w interfejsie użytkownika z serią kontrolek elementów. Zaznaczenie w jednej kontrolce elementów określa zawartość kolejnych kontrolek elementów. Listy można synchronizować przez powiązanie każdej listy z własną kolekcją CollectionViewSource i powiązanie CollectionViewSource wystąpień w łańcuchu. Ta konfiguracja jest nazywana widokiem wzorca/szczegółów (lub listy/szczegółów). Aby uzyskać więcej informacji, zobacz Jak powiązać dane hierarchiczne i utworzyć widok wzorca/szczegółów.

Diagnozowanie i debugowanie problemów z powiązaniem danych

Znacznik powiązania zawiera nazwy właściwości (i dla języka C#, czasami pola i metody). Dlatego podczas zmieniania nazwy właściwości należy również zmienić dowolne powiązanie, które się do niej odwołuje. Jeśli zapomnisz to zrobić, utworzysz usterkę powiązania danych, a aplikacja nie zostanie skompilowana lub nie zostanie uruchomiona poprawnie.

Obiekty powiązania tworzone przez element {x:Bind} i {Binding} są w dużej mierze równoważne funkcjonalnie. Jednak {x:Bind} ma informacje o typie źródła powiązania i generuje kod źródłowy w czasie kompilacji. W przypadku {x:Bind} uzyskasz taki sam rodzaj wykrywania problemów, jaki występuje w pozostałej części kodu. Wykrywanie obejmuje walidację w czasie kompilacji wyrażeń powiązań i debugowanie przez ustawienie punktów przerwania w kodzie źródłowym wygenerowanym jako klasa częściowa dla strony. Te klasy można znaleźć w plikach w obj folderze z nazwami takimi jak (dla języka C#). <view name>.g.cs Jeśli masz problem z powiązaniem, włącz opcję Przełamanie przy nieobsługiwanych wyjątkach w debugerze programu Microsoft Visual Studio. Debuger przerywa wykonywanie w tym momencie, a następnie możesz debugować, co poszło nie tak. Kod wygenerowany przez {x:Bind} program jest zgodny z tym samym wzorcem dla każdej części grafu węzłów źródłowych powiązania i można użyć informacji w oknie stosu wywołań , aby ułatwić określenie sekwencji wywołań, które doprowadziły do problemu.

Element {Binding} nie zawiera informacji o typie dla źródła powiązania. Jednak po uruchomieniu aplikacji z dołączonym debugerem wszelkie błędy powiązań są wyświetlane w oknach Błędy powiązań danych wyjściowych i XAML w programie Visual Studio. Aby uzyskać więcej informacji na temat debugowania błędów powiązań w programie Visual Studio, zobacz Diagnostyka powiązań danych XAML.

Tworzenie powiązań w kodzie

Uwaga / Notatka

Ta sekcja dotyczy tylko elementu {Binding}, ponieważ nie można utworzyć powiązań {x:Bind} w kodzie. Jednak niektóre z tych samych korzyści można osiągnąć w {x:Bind} przypadku obiektu DependencyObject.RegisterPropertyChangedCallback, co umożliwia zarejestrowanie się w celu otrzymywania powiadomień o zmianie dla dowolnej właściwości zależności.

Elementy interfejsu użytkownika można również połączyć z danymi przy użyciu kodu proceduralnego zamiast kodu XAML. W tym celu utwórz nowy obiekt Binding , ustaw odpowiednie właściwości, a następnie wywołaj metodę FrameworkElement.SetBinding lub BindingOperations.SetBinding. Tworzenie powiązań programowo jest przydatne, gdy chcesz wybrać wartości właściwości powiązania w czasie wykonywania lub udostępnić jedno powiązanie między wieloma kontrolkami. Nie można jednak zmienić wartości właściwości powiązania po wywołaniu metody SetBinding.

W poniższym przykładzie pokazano, jak zaimplementować powiązanie w kodzie.

<TextBox x:Name="MyTextBox" Text="Text"/>
// Create an instance of the MyColors class 
// that implements INotifyPropertyChanged.
var textcolor = new MyColors();

// Brush1 is set to be a SolidColorBrush with the value Red.
textcolor.Brush1 = new SolidColorBrush(Colors.Red);

// Set the DataContext of the TextBox MyTextBox.
MyTextBox.DataContext = textcolor;

// Create the binding and associate it with the text box.
var binding = new Binding { Path = new PropertyPath("Brush1") };
MyTextBox.SetBinding(TextBox.ForegroundProperty, binding);

Porównanie funkcji {x:Bind} i {Binding}

Funkcja {x:Bind} a {Binding} Notatki
Ścieżka jest właściwością domyślną {x:Bind a.b.c}
-
{Binding a.b.c}
Właściwość Path {x:Bind Path=a.b.c}
-
{Binding Path=a.b.c}
W x:Bindpliku Path domyślnie w oknie znajduje się rooted, a nie DataContext.
Indexer {x:Bind Groups[2].Title}
-
{Binding Groups[2].Title}
Wiąże się z określonym elementem w kolekcji. Obsługiwane są tylko indeksy oparte na liczbach całkowitych.
Dołączone właściwości {x:Bind Button22.(Grid.Row)}
-
{Binding Button22.(Grid.Row)}
Dołączone właściwości są określane przy użyciu nawiasów. Jeśli właściwość nie jest zadeklarowana w przestrzeni nazw XAML, należy ją poprzedzić nazwą przestrzeni XML, która powinna być powiązana z przestrzenią nazw kodu na początku dokumentu.
Casting {x:Bind groups[0].(data:SampleDataGroup.Title)}
-
Nie jest wymagane w przypadku elementu {Binding}.
Rzuty są określane przy użyciu nawiasów. Jeśli właściwość nie jest zadeklarowana w przestrzeni nazw XAML, poprzedź ją przestrzenią nazw XML, która powinna być zmapowana na przestrzeń nazw kodu na początku dokumentu.
Konwerter {x:Bind IsShown, Converter={StaticResource BoolToVisibility}}
-
{Binding IsShown, Converter={StaticResource BoolToVisibility}}
Zadeklaruj konwertery w katalogu głównym okna, kontrolki, elementu ResourceDictionary lub w pliku App.xaml.
ParameterKonwertera, JęzykKonwertera {x:Bind IsShown, Converter={StaticResource BoolToVisibility}, ConverterParameter=One, ConverterLanguage=fr-fr}
-
{Binding IsShown, Converter={StaticResource BoolToVisibility}, ConverterParameter=One, ConverterLanguage=fr-fr}
Zadeklaruj konwertery w korzeniu okna, kontrolki, ResourceDictionary lub w pliku App.xaml.
TargetNullValue {x:Bind Name, TargetNullValue=0}
-
{Binding Name, TargetNullValue=0}
Używany, gdy liść wyrażenia powiązania ma wartość null. Użyj pojedynczych cudzysłowów dla wartości ciągu.
FallbackValue {x:Bind Name, FallbackValue='empty'}
-
{Binding Name, FallbackValue='empty'}
Używany, gdy dowolna część ścieżki powiązania (z wyjątkiem liścia) ma wartość null.
Nazwa elementu {x:Bind slider1.Value}
-
{Binding Value, ElementName=slider1}
Za pomocą {x:Bind} wiążesz się z polem; Path jest domyślnie osadzony w oknie, co pozwala uzyskać dostęp do dowolnego nazwanego elementu przez jego pole.
Źródło względne: Samozwań <Rectangle x:Name="rect1" Width="200" Height="{x:Bind rect1.Width}" ... />
-
<Rectangle Width="200" Height="{Binding Width, RelativeSource={RelativeSource Self}}" ... />
Za pomocą {x:Bind}polecenia nadaj elementowi nazwę i użyj jej nazwy w pliku Path.
Źródło względne: SzablondParent Nie jest wymagane dla {x:Bind}
-
{Binding <path>, RelativeSource={RelativeSource TemplatedParent}}
Za pomocą polecenia {x:Bind} w elemecie TargetTypeControlTemplate wskazuje powiązanie z elementem nadrzędnym szablonu. W przypadku {Binding}programu regularne powiązanie szablonu może być używane w szablonach kontrolek w większości zastosowań. TemplatedParent Należy jednak użyć konwertera lub powiązania dwukierunkowego.
Źródło Nie jest wymagane dla {x:Bind}
-
<ListView ItemsSource="{Binding Orders, Source={StaticResource MyData}}"/>
Możesz {x:Bind} bezpośrednio użyć nazwanego elementu, użyć właściwości lub ścieżki statycznej.
Mode {x:Bind Name, Mode=OneWay}
-
{Binding Name, Mode=TwoWay}
Mode może to być OneTime, OneWaylub TwoWay. {x:Bind} wartość domyślna to OneTime; {Binding} wartość domyślna to OneWay.
UpdateSourceTrigger {x:Bind Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}
-
{Binding UpdateSourceTrigger=PropertyChanged}
UpdateSourceTrigger może to być Default, LostFocuslub PropertyChanged. {x:Bind} nie obsługuje UpdateSourceTrigger=Explicit. {x:Bind} używa PropertyChanged zachowania dla wszystkich przypadków z wyjątkiem TextBox.Text, gdzie używa LostFocus zachowania.

Zobacz także