Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować się zalogować lub zmienić katalog.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Powiązanie danych w aplikacjach WinUI umożliwia efektywne łączenie kontrolek ze źródłami danych. Dowiedz się, jak powiązać kontrolkę z pojedynczym elementem lub kolekcją elementów, renderowanie elementów, implementacja widoków szczegółowych i formatowanie danych do wyświetlania. Aby uzyskać więcej informacji, zobacz Szczegółowe powiązanie danych.
Warunki wstępne
W tym temacie założono, że wiesz, jak utworzyć podstawową aplikację WinUI przy użyciu zestawu SDK aplikacji systemu Windows. Aby uzyskać instrukcje dotyczące tworzenia pierwszej aplikacji WinUI, zobacz Tworzenie aplikacji WinUI.
Tworzenie projektu
Utwórz nową pustą aplikację WinUI, spakowany projekt C#. Nadaj mu nazwę "Szybki start".
Wiązanie z pojedynczym elementem
Każde powiązanie składa się z elementu docelowego powiązania i źródła powiązania. Zazwyczaj elementem docelowym jest właściwość kontrolki lub innego elementu interfejsu użytkownika, a źródłem jest właściwość wystąpienia klasy (model danych lub model widoku). W tym przykładzie pokazano, jak powiązać kontrolkę z jednym elementem. Obiektem docelowym jest właściwość Text należąca do TextBlock. Źródłem jest wystąpienie prostej klasy o nazwie Recording, która reprezentuje nagranie audio. Przyjrzyjmy się najpierw klasie.
Dodaj nową klasę do projektu i nadaj jej nazwę Recording.
namespace Quickstart
{
public class Recording
{
public string ArtistName { get; set; }
public string CompositionName { get; set; }
public DateTime ReleaseDateTime { get; set; }
public Recording()
{
ArtistName = "Wolfgang Amadeus Mozart";
CompositionName = "Andante in C for Piano";
ReleaseDateTime = new DateTime(1761, 1, 1);
}
public string OneLineSummary
{
get
{
return $"{CompositionName} by {ArtistName}, released: "
+ ReleaseDateTime.ToString("d");
}
}
}
public class RecordingViewModel
{
private Recording defaultRecording = new();
public Recording DefaultRecording { get { return defaultRecording; } }
}
}
Następnie udostępnij klasę źródła powiązania z klasy, która reprezentuje twoje okno znaczników. Dodaj właściwość typu RecordingViewModel do MainWindow.xaml.cs.
namespace Quickstart
{
public sealed partial class MainWindow : Window
{
public MainWindow()
{
this.InitializeComponent();
}
public RecordingViewModel ViewModel{ get; } = new RecordingViewModel();
}
}
Ostatnim elementem jest powiązanie elementu TextBlock z właściwością ViewModel.DefaultRecording.OneLineSummary.
<Window x:Class="Quickstart.MainWindow" ... >
<Grid>
<TextBlock Text="{x:Bind ViewModel.DefaultRecording.OneLineSummary}"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Grid>
</Window>
Oto wynik.
Wiązanie z kolekcją elementów
Typowym scenariuszem jest powiązanie z kolekcją obiektów biznesowych. W języku C# użyj ogólnej klasy ObservableCollection<T> do powiązania danych. Implementuje interfejs INotifyCollectionChanged , który zapewnia powiadomienie o zmianie powiązań po dodaniu lub usunięciu elementów. Jednak ze względu na znaną usterkę trybu Release WinUI z .NET 8 lub nowszymi może być konieczne użycie List<T> w niektórych scenariuszach, zwłaszcza jeśli kolekcja jest statyczna i nie zmienia się po inicjalizacji. Jeśli interfejs użytkownika musi zaktualizować czas zmiany kolekcji w czasie wykonywania, użyj polecenia ObservableCollection<T>. Jeśli potrzebujesz tylko stałego zestawu elementów, List<T> wystarczy. Ponadto jeśli chcesz, aby powiązane kontrolki były aktualizowane ze zmianami właściwości obiektów w kolekcji, te obiekty powinny implementować element INotifyPropertyChanged. Aby uzyskać więcej informacji, zobacz Szczegółowe powiązanie danych.
Notatka
Za pomocą polecenia List<T>możesz nie otrzymywać powiadomień o zmianie w przypadku zmian kolekcji. Jeśli musisz odpowiedzieć na zmiany, rozważ użycie polecenia ObservableCollection<T>. W tym przykładzie nie trzeba odpowiadać na zmiany kolekcji, więc List<T> wystarczy.
W poniższym przykładzie element ListView jest powiązany z kolekcją Recording obiektów. Najpierw dodaj kolekcję do modelu wyświetlania. Dodaj te nowe elementy członkowskie do klasy RecordingViewModel.
public class RecordingViewModel
{
...
private List<Recording> recordings = new();
public List<Recording> Recordings{ get{ return recordings; } }
public RecordingViewModel()
{
recordings.Add(new Recording(){ ArtistName = "Johann Sebastian Bach",
CompositionName = "Mass in B minor", ReleaseDateTime = new DateTime(1748, 7, 8) });
recordings.Add(new Recording(){ ArtistName = "Ludwig van Beethoven",
CompositionName = "Third Symphony", ReleaseDateTime = new DateTime(1805, 2, 11) });
recordings.Add(new Recording(){ ArtistName = "George Frideric Handel",
CompositionName = "Serse", ReleaseDateTime = new DateTime(1737, 12, 3) });
}
}
Następnie powiąż element ListView z właściwością ViewModel.Recordings .
<Window x:Class="Quickstart.MainWindow" ... >
<Grid>
<ListView ItemsSource="{x:Bind ViewModel.Recordings}"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Grid>
</Window>
Nie podano jeszcze szablonu danych dla Recording klasy, więc najlepszą strukturą interfejsu użytkownika może być wywołanie metody ToString dla każdego elementu w klasie ListView. Domyślna implementacja ToString zwraca nazwę typu.
Aby rozwiązać ten problem, możesz zastąpić ciąg ToString , aby zwrócić wartość OneLineSummary, lub podać szablon danych. Opcja szablonu danych jest bardziej typowym i elastycznym rozwiązaniem. Szablon danych określa się przy użyciu właściwości ContentTemplate kontrolki zawartości lub właściwości ItemTemplate kontrolki elementów. Oto dwa sposoby projektowania szablonu danych dla Recording oraz ilustrację wyniku.
<ListView ItemsSource="{x:Bind ViewModel.Recordings}"
HorizontalAlignment="Center" VerticalAlignment="Center">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:Recording">
<TextBlock Text="{x:Bind OneLineSummary}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<ListView ItemsSource="{x:Bind ViewModel.Recordings}"
HorizontalAlignment="Center" VerticalAlignment="Center">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:Recording">
<StackPanel Orientation="Horizontal" Margin="6">
<SymbolIcon Symbol="Audio" Margin="0,0,12,0"/>
<StackPanel>
<TextBlock Text="{x:Bind ArtistName}" FontWeight="Bold"/>
<TextBlock Text="{x:Bind CompositionName}"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Aby uzyskać więcej informacji na temat składni XAML, zobacz
Dodawanie widoku szczegółów
Możesz wyświetlić wszystkie szczegóły obiektów Recording w elementach ListView. Ale takie podejście zajmuje dużo miejsca. Zamiast tego możesz wyświetlić wystarczająco dużo danych w elemencie, aby je zidentyfikować. Gdy użytkownik dokona wyboru, możesz wyświetlić wszystkie szczegóły wybranego elementu w osobnym interfejsie użytkownika znanym jako widok szczegółów. Ten układ jest również znany jako widok wzorca/szczegółów lub widoku listy/szczegółów.
Ten układ można zaimplementować na dwa sposoby. Można powiązać widok szczegółów z właściwością SelectedItem obiektu ListView. Możesz też użyć elementu CollectionViewSource. W takim przypadku powiążesz zarówno ListView, jak i widok szczegółów z elementem CollectionViewSource. To podejście zajmuje się aktualnie wybranym elementem. Obie techniki są pokazane w poniższych sekcjach i dają te same wyniki (pokazane na ilustracji).
Notatka
Do tej pory w tym temacie użyto tylko rozszerzenia znaczników {x:Bind}. Obie techniki przedstawione w poniższych sekcjach wymagają bardziej elastycznego (ale mniej wydajnego) rozszerzenia znaczników {Binding}.
Najpierw poniżej przedstawiono technikę SelectedItem. W przypadku aplikacji C# jedyną konieczną zmianą jest zmiana w mark-upie.
<Window x:Class="Quickstart.MainWindow" ... >
<Grid>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<ListView x:Name="recordingsListView" ItemsSource="{x:Bind ViewModel.Recordings}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:Recording">
<StackPanel Orientation="Horizontal" Margin="6">
<SymbolIcon Symbol="Audio" Margin="0,0,12,0"/>
<StackPanel>
<TextBlock Text="{x:Bind CompositionName}"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<StackPanel DataContext="{Binding SelectedItem, ElementName=recordingsListView}"
Margin="0,24,0,0">
<TextBlock Text="{Binding ArtistName}"/>
<TextBlock Text="{Binding CompositionName}"/>
<TextBlock Text="{Binding ReleaseDateTime}"/>
</StackPanel>
</StackPanel>
</Grid>
</Window>
W przypadku techniki CollectionViewSource najpierw dodaj CollectionViewSource jako zasób najwyższego poziomu Grid.
<Grid.Resources>
<CollectionViewSource x:Name="RecordingsCollection" Source="{x:Bind ViewModel.Recordings}"/>
</Grid.Resources>
Notatka
Klasa Window w interfejsie WinUI nie ma właściwości Resources. Zamiast tego możesz dodać CollectionViewSource do najwyższego poziomu elementu Grid (lub innego nadrzędnego elementu interfejsu użytkownika, takiego jak StackPanel). Jeśli pracujesz w Page, możesz dodać CollectionViewSource do Page.Resources.
Następnie dostosuj powiązania w widoku ListView (który nie musi już być nazwany) i w widoku szczegółów, aby użyć elementu CollectionViewSource. Powiązanie widoku szczegółów bezpośrednio z elementem CollectionViewSourceoznacza, że chcesz powiązać z bieżącym elementem w powiązaniach, w których nie można odnaleźć ścieżki w samej kolekcji. Nie ma potrzeby określania właściwości CurrentItem jako ścieżki dla powiązania, chociaż można to zrobić, jeśli istnieje jakakolwiek niejednoznaczność.
...
<ListView ItemsSource="{Binding Source={StaticResource RecordingsCollection}}">
...
<StackPanel DataContext="{Binding Source={StaticResource RecordingsCollection}}" ...>
...
I oto identyczny wynik w każdym przypadku.
Formatowanie lub konwertowanie wartości danych na potrzeby wyświetlania
Powyższe renderowanie ma problem. Właściwość ReleaseDateTime nie jest tylko datą; to jest DateTime. Dlatego jest wyświetlana z większą dokładnością niż potrzebujesz. Jednym z rozwiązań jest dodanie właściwości string do klasy Recording, która zwraca odpowiednik ReleaseDateTime.ToString("d"). Nazewnictwo tej właściwości ReleaseDate wskazuje, że zwraca datę, a nie datę i godzinę. Nazewnictwo ReleaseDateAsString dalej wskazuje, że zwraca ciąg.
Bardziej elastycznym rozwiązaniem jest użycie konwertera wartości. Oto przykład tworzenia własnego konwertera wartości. Dodaj następujący kod do pliku kodu źródłowego Recording.cs .
public class StringFormatter : Microsoft.UI.Xaml.Data.IValueConverter
{
// This converts the value object to the string to display.
// This will work with most simple types.
public object Convert(object value, Type targetType,
object parameter, string language)
{
// Retrieve the format string and use it to format the value.
string formatString = parameter as string;
if (!string.IsNullOrEmpty(formatString))
{
return string.Format(formatString, value);
}
// If the format string is null or empty, simply
// call ToString() on the value.
return value.ToString();
}
// No need to implement converting back on a one-way binding
public object ConvertBack(object value, Type targetType,
object parameter, string language)
{
throw new NotImplementedException();
}
}
Teraz możesz dodać wystąpienie StringFormatter jako zasób i użyć go w powiązaniu TextBlock, które wyświetla właściwość ReleaseDateTime.
<Grid.Resources>
...
<local:StringFormatter x:Key="StringFormatterValueConverter"/>
</Grid.Resources>
...
<TextBlock Text="{Binding ReleaseDateTime,
Converter={StaticResource StringFormatterValueConverter},
ConverterParameter=Released: \{0:d\}}"/>
...
Jak widać, w celu zapewnienia elastyczności formatowania znacznik przekazuje ciąg formatu do konwertera za pomocą parametru konwertera. W przykładzie kodu pokazanym w tym temacie konwerter wartości języka C# korzysta z tego parametru.
Oto wynik.
Różnice między wiązaniem danych a x:Bind
Podczas pracy z powiązaniem danych w aplikacjach WinUI mogą wystąpić dwa podstawowe mechanizmy powiązań: Binding i x:Bind. Oba te elementy służą do łączenia elementów interfejsu użytkownika ze źródłami danych, ale mają różne różnice:
-
x:Bind: oferuje sprawdzanie w czasie kompilacji, lepszą wydajność i jest silnie typowany. Jest to idealne rozwiązanie w scenariuszach, w których znasz strukturę danych w czasie kompilacji. -
Binding: zapewnia ocenę środowiska uruchomieniowego i jest bardziej elastyczny w scenariuszach dynamicznych, takich jak wtedy, gdy struktura danych nie jest znana w czasie kompilacji.
Scenariusze nieobsługiwane przez x:Bind
Chociaż x:Bind jest zaawansowany, nie można go używać w niektórych scenariuszach:
-
Dynamiczne struktury danych: jeśli struktura danych nie jest znana w czasie kompilacji, nie można użyć elementu
x:Bind. -
Powiązanie między elementami:
x:Bindnie obsługuje powiązania bezpośrednio między dwoma elementami interfejsu użytkownika. -
Powiązanie z
DataContext:x:Bindnie automatycznie dziedziczyDataContextelementu nadrzędnego. -
Powiązania dwukierunkowe z elementem
Mode=TwoWay: Chociaż są obsługiwane,x:Bindwymaga jawnego zaimplementowaniaINotifyPropertyChangeddla każdej właściwości, którą interfejs użytkownika ma aktualizować, gdy źródło ulega zmianie, niezależnie od tego, czy używane jest powiązanie jednokierunkowe, czy dwukierunkowe. Kluczową różnicą w powiązaniach dwukierunkowych jest to, że zmiany również przepływają z interfejsu użytkownika z powrotem do źródła.
Aby zapoznać się z praktycznymi przykładami i dokładniej zrozumieć, kiedy z nich korzystać, zapoznaj się z następującymi tematami:
Powiązana zawartość
Windows developer