Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Привязка данных в приложениях WinUI позволяет эффективно подключать элементы управления к источникам данных. Узнайте, как привязать элемент управления к одному элементу или коллекции элементов, управлять отрисовкой элементов, реализовать детализированные представления и форматировать данные для отображения. Дополнительные сведения см. в подробной статье о привязке данных.
Необходимые условия
В этом разделе предполагается, что вы знаете, как создать базовое приложение WinUI с помощью пакета SDK для приложений Windows. Инструкции по созданию первого приложения WinUI см. в статье "Создание приложения WinUI".
Создание проекта
Создайте новый пустой проект C#, упакованное приложение WinUI. Назовите его "Быстрый старт".
Привязка к одному элементу
Каждая привязка состоит из целевого объекта привязки и источника привязки. Как правило, целевой объект — это свойство элемента управления или другого элемента пользовательского интерфейса, а источник — это свойство экземпляра класса (модель данных или модель представления). В этом примере показано, как привязать элемент управления к одному элементу. Цель - это свойство 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();
public Recording DefaultRecording { get { return defaultRecording; } }
}
}
Затем предоставьте исходный класс привязки из класса, представляющего окно разметки. Добавьте свойство типа RecordingViewModel в MainWindow.xaml.cs.
namespace Quickstart
{
public sealed partial class MainWindow : Window
{
public MainWindow()
{
this.InitializeComponent();
}
public RecordingViewModel ViewModel{ get; } = new RecordingViewModel();
}
}
Последний элемент заключается в привязке 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> для привязки данных. Он реализует интерфейс INotifyCollectionChanged , который предоставляет уведомление об изменении привязок при добавлении или удалении элементов. Однако из-за известной ошибки режима Release WinUI с .NET 8 и более поздних версий может потребоваться использовать List<T> в некоторых сценариях, особенно если коллекция статична и не изменяется после инициализации. Если пользовательский интерфейс должен обновиться при изменении коллекции во время выполнения, используйте ObservableCollection<T>. Если необходимо отобразить только фиксированный набор элементов, List<T> достаточно. Кроме того, если вы хотите, чтобы связанные элементы управления обновлялись с изменениями свойств объектов в коллекции, эти объекты должны реализовать INotifyPropertyChanged. Дополнительные сведения см. в разделе Подробное изучение привязки данных.
Заметка
При использовании List<T>вы можете не получать уведомления об изменениях коллекции. Если вам нужно ответить на изменения, рассмотрите возможность использования ObservableCollection<T>. В этом примере не нужно реагировать на изменения коллекции, поэтому List<T> достаточно.
Следующий пример привязывает ListView к коллекции Recording объектов. Сначала добавьте коллекцию в модель представления. Добавьте эти новые члены в 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) });
}
}
Затем привязать 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# использует этот параметр.
Вот результат.
Различия между привязкой и x:Bind
При работе с привязкой данных в приложениях WinUI может возникнуть два основных механизма привязки: Binding и x:Bind. Хотя оба служат для подключения элементов пользовательского интерфейса к источникам данных, они имеют различные отличия:
-
x:Bind: предлагает проверку во время компиляции, более высокую производительность и строго типизированную. Это идеально подходит для сценариев, когда вы знаете структуру данных во время компиляции. -
Binding: обеспечивает оценку среды выполнения и более гибкую для динамических сценариев, например если структура данных не известна во время компиляции.
Сценарии, не поддерживаемые x:Bind
Хотя x:Bind это мощный, его нельзя использовать в определенных сценариях:
-
Динамические структуры данных: если структура данных не известна во время компиляции, нельзя использовать
x:Bind. -
Привязка между элементами:
x:Bindне поддерживает привязку непосредственно между двумя элементами пользовательского интерфейса. -
Привязка к :
DataContextx:Bindне наследует автоматически свойство родительского элемента. -
Двусторонняя привязка с
Mode=TwoWay: хотя поддерживается, для любого свойства, которое вам нужно обновить при изменении источника, независимо от того, используется односторонняя или двусторонняя привязка, необходима явная реализацияx:BindINotifyPropertyChanged. Ключевое отличие двухсторонних привязок заключается в том, что изменения также происходят из пользовательского интерфейса обратно к источнику.
Практические примеры и более глубокое понимание того, когда следует использовать каждый из них, см. в следующих разделах:
Связанное содержимое
Windows developer