Ескертпе
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Жүйеге кіруді немесе каталогтарды өзгертуді байқап көруге болады.
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Каталогтарды өзгертуді байқап көруге болады.
Привязка данных в приложениях WinUI 3 позволяет эффективно подключать элементы управления к источникам данных. Узнайте, как привязать элемент управления к одному элементу или коллекции элементов, управлять отрисовкой элементов, реализовать детализированные представления и форматировать данные для отображения. Дополнительные сведения см. в подробной статье о привязке данных.
Необходимые условия
В этом разделе предполагается, что вы знаете, как создать базовое приложение WinUI с Windows App SDK. Инструкции по созданию первого приложения 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 , который предоставляет уведомление об изменении привязок при добавлении или удалении элементов. Однако из-за известной ошибки режима выпуска 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