Поделиться через


Обзор привязки данных 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>

Вот результат.

Binding a textblock

Привязка к коллекции элементов

Распространенный сценарий — привязка к коллекции бизнес-объектов. В 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 — возвращать имя типа.

Binding a list view 1

Чтобы устранить эту проблему, можно переопределить 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>

Binding a list view 2

<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>

Binding a list view 3

Дополнительные сведения о синтаксисе XAML см. в статье "Создание пользовательского интерфейса с помощью XAML". Дополнительные сведения о макете элемента управления см. в разделе "Определение макетов с помощью XAML".

Добавление представления сведений

Вы можете отобразить все сведения о объектах в элементах Recording ListView. Но это занимает много места. Вместо этого можно отобразить достаточно данных в элементе, чтобы определить его, а затем, когда пользователь выбирает элемент, можно отобразить все сведения выбранного элемента в отдельном элементе пользовательского интерфейса, известном как представление сведений. Это расположение также называется представлением master/details или представлением списка и сведений.

Это можно сделать двумя способами. Представление сведений можно привязать к свойству SelectedItem объекта ListView. Или вы можете использовать CollectionViewSource, в этом случае вы привязываете как представление сведений, так ListView и представление сведений к CollectionViewSource этому элементу (это делает для вас выбранный в данный момент элемент). Оба способа описаны ниже. Они дают аналогичные результаты, показанные на картинке.

Во-первых , вот метод 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 ресурс в виде окна.

<Window.Resources>
    <CollectionViewSource x:Name="RecordingsCollection" Source="{x:Bind ViewModel.Recordings}"/>
</Window.Resources>

А затем настройте привязки в ListView (которые больше не должны быть названы) и в представлении сведений для использования CollectionViewSource. Обратите внимание, что путем привязки представления сведений непосредственно к CollectionViewSourceколлекции подразумевается, что вы хотите привязать к текущему элементу в привязках, где путь не найден в самой коллекции. Нет необходимости указывать CurrentItem свойство в качестве пути для привязки, хотя это можно сделать, если есть какая-либо неоднозначность).

...
<ListView ItemsSource="{Binding Source={StaticResource RecordingsCollection}}">
...
<StackPanel DataContext="{Binding Source={StaticResource RecordingsCollection}}" ...>
...

И вот одинаковый результат в каждом случае.

Binding a list view 4

Форматирование или преобразование значений данных для отображения

С описанной выше визуализацией связана одна проблема. Это 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 свойства.

<Window.Resources>
    <local:StringFormatter x:Key="StringFormatterValueConverter"/>
</Window.Resources>
...
<TextBlock Text="{Binding ReleaseDateTime,
    Converter={StaticResource StringFormatterValueConverter},
    ConverterParameter=Released: \{0:d\}}"/>
...

Как видно выше, чтобы повысить гибкость форматирования, мы используем разметку для передачи строки формата в преобразователь с помощью параметра преобразователя. В примере кода, приведенном в этом разделе, преобразователь значений C# использует этот параметр.

Вот результат.

displaying a date with custom formatting

См. также