Обзор привязки данных Windows
В этом разделе показано, как привязать элемент управления (или другой элемент пользовательского интерфейса) к одному элементу или привязать элемент управления элементами к коллекции элементов в приложении пакета SDK для приложений windows. Кроме того, мы покажем, как управлять отрисовкой элементов, реализовывать представление сведений на основе выделения и преобразовывать данные для отображения. Дополнительные сведения см. в подробной привязке данных.
Необходимые условия
В этом разделе предполагается, что вы знаете, как создать базовое приложение пакета SDK для приложений Windows. Инструкции по созданию первого приложения пакета SDK для приложений Windows см. в статье Создание первого проекта WinUI 3 (пакет SDK для приложений Windows).
Создание проекта
Создайте новое пустое приложение, упакованное (WinUI 3 в настольном приложении) проект C#. Назовите его "Быстрый старт".
Привязка к одному элементу
Каждая привязка состоит из целевого объекта привязки и источника привязки. Как правило, целевой объект — это свойство элемента управления или другого элемента пользовательского интерфейса, а источник — это свойство экземпляра класса (модель данных или модель представления). В этом примере показано, как привязать элемент управления к одному элементу. Цель - это свойство Text
объекта TextBlock
. Источник — это экземпляр простого класса с именем Recording
, представляющий запись звука. Давайте сначала рассмотрим класс.
Добавьте новый класс в проект и присвойте классу имя 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 Recording();
public Recording DefaultRecording { get { return defaultRecording; } }
}
}
Затем предоставьте исходный класс привязки из класса, представляющего окно разметки. Мы делаем это путем добавления свойства типа RecordingViewModel
в MainWindow.xaml.cs.
namespace Quickstart
{
public sealed partial class MainWindow : Window
{
public MainWindow()
{
this.InitializeComponent();
ViewModel = new RecordingViewModel();
}
public RecordingViewModel ViewModel{ get; set; }
}
}
Последний элемент заключается в привязке TextBlock
к свойству ViewModel.DefaultRecording.OneLineSummary
.
<Window x:Class="Quickstart.MainWindow" ... >
<Grid>
<TextBlock Text="{x:Bind ViewModel.DefaultRecording.OneLineSummary}"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Grid>
</Window>
Вот результат.
Привязка к коллекции элементов
Распространенный сценарий — привязка к коллекции бизнес-объектов. В C# универсальный класс ObservableCollection<T> является хорошим выбором для привязки данных, так как он реализует интерфейсы INotifyPropertyChanged и INotifyCollectionChanged. Эти интерфейсы предоставляют уведомление об изменении привязок при добавлении или удалении элементов или свойстве самого списка. Если вы хотите, чтобы связанные элементы управления обновлялись с изменениями свойств объектов в коллекции, бизнес-объект также должен реализовать INotifyPropertyChanged
. Дополнительные сведения см. в разделе Подробное изучение привязки данных.
Следующий пример привязывает ListView к коллекции объектов Recording
. Начнем с добавления коллекции в модель представления. Просто добавьте эти новые члены в класс RecordingViewModel
.
public class RecordingViewModel
{
...
private ObservableCollection<Recording> recordings = new ObservableCollection<Recording>();
public ObservableCollection<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) });
}
}
А затем привязать ListView к свойству ViewModel.Recordings
.
<Window x:Class="Quickstart.MainWindow" ... >
<Grid>
<ListView ItemsSource="{x:Bind ViewModel.Recordings}"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Window>
Мы еще не предоставили шаблон данных для класса Recording
, поэтому лучше всего использовать платформу пользовательского интерфейса для вызова ToString для каждого элемента в ListView. Реализация ToString
по умолчанию заключается в том, чтобы возвращать имя типа.
Чтобы устранить эту проблему, можно переопределить ToString, чтобы вернуть значение OneLineSummary
или указать шаблон данных. Параметр шаблона данных является более привычным решением, а также более гибким. Вы указываете шаблон данных с помощью свойства ContentTemplate элемента управления содержимым или свойства ItemTemplate элемента управления элементами. Ниже приведены два способа разработки шаблона данных для Recording
вместе с иллюстрацией результата.
<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>
Дополнительные сведения о синтаксисе XAML см. в статье Создание пользовательского интерфейса с помощью XAML. Дополнительные сведения о макете элемента управления см. в статье Определение макетов с помощью XAML.
Добавление представления сведений
Вы можете выбрать, чтобы отобразить все сведения об объектах Recording
в элементах ListView . Но это занимает много места. Вместо этого можно отобразить достаточно данных в элементе, чтобы определить его, а затем, когда пользователь выбирает элемент, можно отобразить все сведения выбранного элемента в отдельном элементе пользовательского интерфейса, известном как представление сведений. Такое расположение также называется представлением "основное и детали" или представлением "список и сведения".
Это можно сделать двумя способами. Представление сведений можно привязать к свойству ListView
и детальное представление к CollectionViewSource
, что позаботится о выбранном в данный момент элементе. Оба метода показаны ниже, и оба они дают одинаковые результаты (показанные на рисунке).
Заметка
До сих пор в этом разделе мы использовали только расширение разметки {x:Bind} , но оба метода, которые мы рассмотрим ниже, требуют более гибкого (но менее производительного) расширения разметки {Binding} .
Во-первых, вот метод SelectedItem. Для приложения C# необходимо внести только изменения в разметку.
<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>
Для метода CollectionViewSource сначала добавьте CollectionViewSource
в качестве ресурса Grid
верхнего уровня.
<Grid.Resources>
<CollectionViewSource x:Name="RecordingsCollection" Source="{x:Bind ViewModel.Recordings}"/>
</Grid.Resources>
Заметка
Класс Window в WinUI не имеет свойства Resources
. Вместо этого можно добавить CollectionViewSource
в элемент верхнего уровня Grid
(или другой родительский элемент пользовательского интерфейса, например StackPanel
). Если вы работаете в рамках Page
, вы можете добавить CollectionViewSource
в Page.Resources
.
А затем настройте привязки в ListView (который теперь не требуется называть) и представлении сведений для использования CollectionViewSource. Обратите внимание, что если вы привязываете представление сведений непосредственно к CollectionViewSource
, это означает, что вы хотите установить привязку к текущему элементу в тех случаях, когда путь не удается найти в самой коллекции. Нет необходимости указывать свойство CurrentItem
в качестве пути для привязки, хотя это можно сделать, если есть какая-либо неоднозначность.
...
<ListView ItemsSource="{Binding Source={StaticResource RecordingsCollection}}">
...
<StackPanel DataContext="{Binding Source={StaticResource RecordingsCollection}}" ...>
...
И вот одинаковый результат в каждом случае.
Форматирование или преобразование значений данных для отображения
Возникла проблема с отрисовкой выше. Свойство ReleaseDateTime
— это не просто дата, это DateTime. Таким образом, он отображается с большей точностью, чем нам нужно. Одним из решений является добавление строкового свойства в класс Recording
, возвращающий эквивалент ReleaseDateTime.ToString("d")
. Именование этого свойства ReleaseDate
указывает, что он возвращает дату, а не дату и время. Именование ReleaseDateAsString
будет указывать на то, что он возвращает строку.
Более гибкое решение — использовать что-то известное как преобразователь значений. Ниже приведен пример создания собственного преобразователя значений. Добавьте приведенный ниже код в файл исходного кода 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();
}
}
Теперь можно добавить экземпляр StringFormatter
в качестве ресурса и использовать его в привязке TextBlock
, отображающей свойство ReleaseDateTime
.
<Grid.Resources>
...
<local:StringFormatter x:Key="StringFormatterValueConverter"/>
</Grid.Resources>
...
<TextBlock Text="{Binding ReleaseDateTime,
Converter={StaticResource StringFormatterValueConverter},
ConverterParameter=Released: \{0:d\}}"/>
...
Как видно выше, для гибкости форматирования мы используем разметку для передачи строки формата в преобразователь с помощью параметра преобразователя. В примере кода, приведенном в этом разделе, преобразователь значений C# использует этот параметр.
Вот результат.
Связанное содержимое
Windows developer