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


Руководство. Создание привязок данных

Предположим, вы разработали и внедрили красивый пользовательский интерфейс, наполненный временными изображениями, текстом-заглушкой "lorem ipsum" и элементами управления, которые пока ничего не делают. Затем необходимо подключить его к реальным данным и преобразовать его из прототипа разработки в живое приложение.

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

Вы начнете с упрощенной версии примера PhotoLab. Эта начальная версия включает полный уровень данных, а также базовые макеты страниц XAML и оставляет много функций, чтобы упростить обзор кода. В этом руководстве не создается полное приложение, поэтому обязательно ознакомьтесь с окончательной версией, чтобы увидеть такие функции, как пользовательские анимации и адаптивные макеты. Окончательную версию можно найти в корневой папке репозитория Windows-appsample-photo-lab.

Пример приложения PhotoLab содержит две страницы. На главной странице отображается представление коллекции фотографий, а также некоторые сведения о каждом файле изображения.

снимок экрана главной страницы лаборатории фотографии.

Страница деталей отображает одну фотографию после её выбора. Всплывающее меню редактирования позволяет изменять, переименовать и сохранить фотографию.

снимок экрана страницы сведений о лаборатории фотографий.

Предпосылки

Часть 0. Получение начального кода с GitHub

В этом руководстве вы начните с упрощенной версии примера PhotoLab.

  1. Перейдите на страницу GitHub для примера: https://github.com/Microsoft/Windows-appsample-photo-lab.

  2. Затем необходимо клонировать или скачать пример. Нажмите кнопку Клонировать или скачать. Появляется вложенное меню. меню «Клонировать или скачать» на странице примера PhotoLab на GitHub

    Если вы не знакомы с GitHub:

    a. Выберите Загрузить ZIP и сохраните файл локально. При этом загружается файл .zip, содержащий все необходимые файлы проекта.

    б. Извлеките файл. Используйте Проводник файлов, чтобы перейти к файлу .zip, который вы только что скачали, щелкните правой кнопкой мыши и выберите Извлечь все....

    с. Перейдите к локальной копии примера и перейдите в каталог Windows-appsample-photo-lab-master\xaml-basics-starting-points\data-binding.

    Если вы знакомы с GitHub:

    a. Клонируйте основную ветвь репозитория локально.

    б. Перейдите к каталогу Windows-appsample-photo-lab\xaml-basics-starting-points\data-binding.

  3. Дважды щелкните Photolab.sln, чтобы открыть решение в Visual Studio.

Часть 1. Замена переменных

Здесь вы создаете одноразовые привязки в XAML-шаблоне данных для отображения изображения и его метаданных вместо контента-заполнителя.

Однократные привязки предназначены только для чтения, неизменяемых данных, что означает, что они обладают высокой производительностью и их легко создавать, позволяя отображать большие наборы данных в GridView и ListView контролах.

Замените заполнители единовременными привязками

  1. Откройте папку xaml-basics-starting-points\data-binding и запустите файл PhotoLab.sln в Visual Studio.

  2. Убедитесь, что платформы решений задано значение x86 или x64, а не Arm, а затем запустите приложение. Это показывает состояние приложения с заполнителями пользовательского интерфейса перед добавлением привязок.

    Запуск приложения с изображениями-заполнителями и текстом

  3. Откройте MainPage.xaml и найдите DataTemplate с именем ImageGridView_DefaultItemTemplate. Вы обновите этот шаблон, чтобы использовать привязки данных.

    до:

    <DataTemplate x:Key="ImageGridView_DefaultItemTemplate">
    

    Значение x:Key используется ImageGridView для выбора данного шаблона для отображения объектов данных.

  4. Добавьте значение x:DataType в шаблон.

    после:

    <DataTemplate x:Key="ImageGridView_DefaultItemTemplate"
                  x:DataType="local:ImageFileInfo">
    

    x:DataType указывает, для какого типа это шаблон. В этом случае это шаблон для класса ImageFileInfo (где local: указывает локальное пространство имен, как определено в объявлении xmlns в верхней части файла).

    x:DataType требуется при использовании выражений x:Bind в шаблоне данных, как описано далее.

  5. В DataTemplateнайдите элемент Image с именем ItemImage и замените его значение Source, как показано ниже.

    до:

    <Image x:Name="ItemImage"
           Source="/Assets/StoreLogo.png"
           Stretch="Uniform" />
    

    после:

    <Image x:Name="ItemImage"
           Source="{x:Bind ImageSource}"
           Stretch="Uniform" />
    

    x:Name идентифицирует элемент XAML, чтобы на него можно было ссылаться в другом месте в XAML и в коде.

    x:Bind выражения предоставляют значение свойству пользовательского интерфейса, получая значение из свойства объекта данных. В шаблонах указанное свойство является свойством того, к чему было установлено x:DataType. Таким образом, в этом случае источник данных является свойством ImageFileInfo.ImageSource.

    Замечание

    Значение x:Bind также позволяет редактору знать о типе данных, поэтому вместо ввода имени свойства в выражении x:Bind можно использовать IntelliSense. Попробуйте использовать код, который вы только что вставили: поместите курсор сразу после x:Bind и нажмите пробел, чтобы просмотреть список свойств, к которые можно привязать.

  6. Замените значения других элементов управления пользовательским интерфейсом таким же образом. (Попробуйте сделать это с IntelliSense вместо копирования и вставки!)

    до:

    <TextBlock Text="Placeholder" ... />
    <StackPanel ... >
        <TextBlock Text="PNG file" ... />
        <TextBlock Text="50 x 50" ... />
    </StackPanel>
    <muxc:RatingControl Value="3" ... />
    

    после:

    <TextBlock Text="{x:Bind ImageTitle}" ... />
    <StackPanel ... >
        <TextBlock Text="{x:Bind ImageFileType}" ... />
        <TextBlock Text="{x:Bind ImageDimensions}" ... />
    </StackPanel>
    <muxc:RatingControl Value="{x:Bind ImageRating}" ... />
    

Запустите приложение, чтобы узнать, как оно выглядит на данный момент. Больше никаких заполнителей! Мы хорошо начали.

Запуск приложения с реальными изображениями и текстом вместо заполнителей

Замечание

Если вы хотите дополнительно поэкспериментировать, попробуйте добавить новый TextBlock в шаблон данных и используйте трюк x:Bind IntelliSense для поиска свойства для отображения.

Здесь вы создадите однократные привязки на странице XAML для подключения представления коллекции к коллекции изображений, заменив существующий процедурный код, который выполняет это в коде программной части. Вы также создадите кнопку "Удалить", чтобы посмотреть, как изменится галерея представления при удалении изображений из коллекции. В то же время вы узнаете, как привязывать события к обработчикам событий, чтобы получить большую гибкость, чем та, которую обеспечивают традиционные обработчики событий.

Все привязки, описанные до сих пор, находятся внутри шаблонов данных и ссылаются на свойства класса, указанного значением x:DataType. А что с остальным XAML на вашей странице?

x:Bind выражения за пределами шаблонов данных всегда привязаны к самой странице. Это означает, что вы можете ссылаться на все, что вы помещаете в код или объявляете в XAML, включая пользовательские свойства и свойства других элементов управления пользовательского интерфейса на странице (если они имеют значение x:Name).

В примере PhotoLab одна из таких привязок заключается в подключении основного управляющего элемента GridView непосредственно к коллекции изображений, вместо использования кода на заднем плане. Позже вы увидите другие примеры.

Привязка основного элемента управления GridView к коллекции Изображений

  1. В MainPage.xaml.cs найдите метод GetItemsAsync и удалите код, который задает ItemsSource.

    до:

    ImageGridView.ItemsSource = Images;
    

    после:

    // Replaced with XAML binding:
    // ImageGridView.ItemsSource = Images;
    
  2. В MainPage.xaml найдите GridView с именем ImageGridView и добавьте атрибут ItemsSource. Для этого значения используйте выражение x:Bind, ссылающееся на свойство Images, реализованное в программном коде.

    до:

    <GridView x:Name="ImageGridView"
    

    после:

    <GridView x:Name="ImageGridView"
              ItemsSource="{x:Bind Images}"
    

    Свойство Images имеет тип ObservableCollection<ImageFileInfo>, поэтому отдельные элементы, отображаемые в GridView, имеют тип ImageFileInfo. Это соответствует значению x:DataType, описанному в части 1.

Все привязки, которые мы рассмотрели до сих пор, являются однократными привязками только для чтения, что является поведением по умолчанию для простых выражений x:Bind. Данные загружаются только при инициализации, что обеспечивает высокую производительность привязок — идеально подходит для поддержки нескольких сложных представлений больших наборов данных.

Даже добавленная ItemsSource привязка — это однократная привязка только для чтения к неизменному значению свойства, но здесь есть важное различие. Неизменное значение свойства Images — это одиночный конкретный экземпляр коллекции, инициализированный один раз, как показано здесь.

private ObservableCollection<ImageFileInfo> Images { get; }
    = new ObservableCollection<ImageFileInfo>();

Значение свойства Images никогда не изменяется, но поскольку свойство имеет тип ObservableCollection<T>, содержимое коллекции может измениться, и привязка автоматически заметит изменения и обновит пользовательский интерфейс.

Чтобы проверить это, мы временно добавим кнопку, которая удаляет выбранный в данный момент образ. Эта кнопка отсутствует в окончательной версии, так как выбор изображения приведет вас к странице сведений. Однако поведение ObservableCollection<T> по-прежнему важно в последнем примере PhotoLab, так как XAML инициализирован в конструкторе страниц (с помощью вызова метода InitializeComponent), но коллекция Images заполняется позже в методе GetItemsAsync.

Кнопка "Удалить"

  1. В Файле MainPage.xaml найдите CommandBar с именем MainCommandBar и добавьте новую кнопку перед кнопкой масштабирования. (Элементы управления масштабированием еще не работают. Вы подключитесь к ним в следующей части руководства.)

    <AppBarButton Icon="Delete"
                  Label="Delete selected image"
                  Click="{x:Bind DeleteSelectedImage}" />
    

    Если вы уже знакомы с XAML, это Click значение может выглядеть необычно. В предыдущих версиях XAML необходимо задать этот метод с определенной сигнатурой обработчика событий, обычно включая параметры отправителя события и объекта аргументов для конкретного события. Этот метод по-прежнему можно использовать при необходимости аргументов событий, но с помощью x:Bind, вы также можете подключиться к другим методам. Например, если вам не нужны данные события, можно подключиться к методам, не имеющим параметров, как и здесь.

  2. В MainPage.xaml.cs добавьте метод DeleteSelectedImage.

    private void DeleteSelectedImage() =>
        Images.Remove(ImageGridView.SelectedItem as ImageFileInfo);
    

    Этот метод просто удаляет выбранный образ из коллекции Images.

Теперь запустите приложение и нажмите кнопку для удаления нескольких изображений. Как видно, пользовательский интерфейс обновляется автоматически, благодаря привязке данных и типу ObservableCollection<T>.

Замечание

Этот код удаляет только экземпляр ImageFileInfo из коллекции Images в работающем приложении. Он не удаляет файл изображения с компьютера.

Часть 3. Настройка ползунка масштабирования

В этой части вы создадите односторонние привязки из элемента управления в шаблоне данных к ползунку масштабирования за пределами шаблона. Вы также узнаете, что можно использовать привязку данных со многими свойствами элемента управления, а не только с наиболее очевидными, такими как TextBlock.Text и Image.Source.

Привязка шаблона данных изображения к ползунку масштабирования

  • Найдите DataTemplate с именем ImageGridView_DefaultItemTemplate и замените значения **Height** и Width элемента управления Grid в верхней части шаблона.

    До

    <DataTemplate x:Key="ImageGridView_DefaultItemTemplate"
                  x:DataType="local:ImageFileInfo">
        <Grid Height="200"
              Width="200"
              Margin="{StaticResource LargeItemMargin}">
    

    После

    <DataTemplate x:Key="ImageGridView_DefaultItemTemplate"
                  x:DataType="local:ImageFileInfo">
        <Grid Height="{Binding Value, ElementName=ZoomSlider}"
              Width="{Binding Value, ElementName=ZoomSlider}"
              Margin="{StaticResource LargeItemMargin}">
    

Вы заметили, что это Binding выражения, а не x:Bind выражения? Это старый способ выполнения привязок данных, и он в основном устарел. x:Bind делает почти все, что Binding делает, и многое другое. Однако при использовании x:Bind в шаблоне данных он привязывается к типу, объявленному в значении x:DataType. Таким образом, как привязать что-то в шаблоне к чему-то на странице XAML или в коде программной части? Необходимо использовать устаревшее выражение Binding.

Binding выражения не распознают значение x:DataType, но эти Binding выражения имеют ElementName значения, которые работают почти так же. Они сообщают механизму привязки, что значение привязки является привязкой к свойству Value указанного элемента на странице (то есть элемента с этим значением x:Name). Если вы хотите привязаться к свойству в фоновом коде, это будет выглядеть примерно как {Binding MyCodeBehindProperty, ElementName=page}, где page обращается к значению x:Name, заданному в элементе Page в XAML.

Замечание

По умолчанию Binding выражения — это односторонние, то есть они автоматически обновляют данные в интерфейсе при изменении значения привязанного свойства.

В отличие от этого, по умолчанию для x:Bind используется однократноевремя, что означает, что любые изменения в связанном свойстве игнорируются. Это по умолчанию, так как это самый высокопроизводительный вариант, и большинство привязок относятся к статическим, доступным только для чтения данным.

В этом уроке показано, что если вы используете x:Bind со свойствами, которые могут изменить их значения, обязательно добавьте Mode=OneWay или Mode=TwoWay. Примеры этого вы увидите в следующем разделе.

Запустите приложение и используйте ползунок для изменения измерений шаблона изображения. Как видите, эффект довольно мощный, не нуждаясь в большом коде.

Запуск приложения с ползунком масштабирования

Замечание

Чтобы испытать себя, попробуйте привязать другие свойства пользовательского интерфейса к свойству ползунка масштабирования Value или к другим ползункам, добавленным после ползунка масштабирования. Например, можно привязать свойство FontSize элемента TitleTextBlock к новому ползунку со значением по умолчанию 24. Не забудьте задать разумные минимальные и максимальные значения.

Часть 4. Улучшение возможностей масштабирования

В этой части вы добавите пользовательское свойство ItemSize для заднего кода и создадите односторонние привязки из шаблона изображения к новому свойству. Значение ItemSize будет обновлено ползунком масштабирования и другими факторами, такими как переключателем Обрезка по экрану и размером окна, что создаёт более изысканный опыт.

В отличие от встроенных свойств элемента управления, ваши настраиваемые свойства не обновляют пользовательский интерфейс автоматически, даже с односторонней и двусторонней привязкой. Они работают хорошо с однимвремя привязки, но если вы хотите, чтобы изменения свойств на самом деле отображались в пользовательском интерфейсе, вам нужно выполнить некоторую работу.

Создайте свойство ItemSize, чтобы обновить пользовательский интерфейс

  1. В MainPage.xaml.cs измените сигнатуру класса MainPage таким образом, чтобы он реализовал интерфейс INotifyPropertyChanged.

    до:

    public sealed partial class MainPage : Page
    

    после:

    public sealed partial class MainPage : Page, INotifyPropertyChanged
    

    Это сообщает системе привязки, что MainPage имеет событие PropertyChanged, которое будет добавлено далее и на которое могут подписываться привязки для обновления пользовательского интерфейса.

  2. Добавьте событие PropertyChanged в класс MainPage.

    public event PropertyChangedEventHandler PropertyChanged;
    

    Это событие обеспечивает полную реализацию, необходимую интерфейсом INotifyPropertyChanged. Тем не менее, чтобы оно могло иметь какой-либо эффект, необходимо явно вызвать событие в пользовательских свойствах.

  3. Добавьте свойство ItemSize и вызовите событие PropertyChanged в его методе установки.

    public double ItemSize
    {
        get => _itemSize;
        set
        {
            if (_itemSize != value)
            {
                _itemSize = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ItemSize)));
            }
        }
    }
    private double _itemSize;
    

    Свойство ItemSize предоставляет значение частного поля _itemSize. При использовании резервного поля, подобного этому, свойство позволяет проверить, совпадает ли новое значение со старым значением, прежде чем оно вызывает потенциально ненужные события PropertyChanged.

    Само событие вызывается методом Invoke. Вопросительный знак проверяет, является ли событие PropertyChanged null, то есть были ли добавлены какие-нибудь обработчики событий. Каждая односторонняя или двусторонняя привязка добавляет обработчик событий в скрытом режиме, но если здесь никто не слушает, ничего больше не произойдет. Если PropertyChanged не имеет значения NULL, то Invoke вызывается со ссылкой на источник событий (сама страница, представленная ключевым словом this) и объект event-args, указывающий имя свойства. Эта информация позволит односторонним или двусторонним привязкам к свойству ItemSize быть в курсе всех изменений, чтобы они могли обновить привязанный пользовательский интерфейс.

  4. В MainPage.xaml найдите DataTemplate с именем ImageGridView_DefaultItemTemplate и замените значения Height и Width элемента управления Grid в верхней части шаблона. (Если вы выполняли привязку элементы управления к элементу управления в предыдущей части этого руководства, то единственными изменениями будут замена Value на ItemSize и ZoomSlider на page. Не забудьте сделать это как для Height, так и для Width!)

    До

    <DataTemplate x:Key="ImageGridView_DefaultItemTemplate"
                  x:DataType="local:ImageFileInfo">
        <Grid Height="{Binding Value, ElementName=ZoomSlider}"
            Width="{Binding Value, ElementName=ZoomSlider}"
            Margin="{StaticResource LargeItemMargin}">
    

    После

    <DataTemplate x:Key="ImageGridView_DefaultItemTemplate"
                  x:DataType="local:ImageFileInfo">
        <Grid Height="{Binding ItemSize, ElementName=page}"
              Width="{Binding ItemSize, ElementName=page}"
              Margin="{StaticResource LargeItemMargin}">
    

Теперь, когда пользовательский интерфейс может реагировать на изменения ItemSize, необходимо действительно их внести. Как упоминалось ранее, значение ItemSize вычисляется из текущего состояния различных элементов управления пользовательским интерфейсом, но вычисление должно выполняться всякий раз, когда эти элементы управления изменяют состояние. Для этого вы будете использовать привязку событий, чтобы определенные изменения пользовательского интерфейса вызвали вспомогательный метод, который обновляет ItemSize.

Обновление значения свойства ItemSize

  1. Добавьте метод DetermineItemSize для MainPage.xaml.cs.

    private void DetermineItemSize()
    {
        if (FitScreenToggle != null
            && FitScreenToggle.IsOn == true
            && ImageGridView != null
            && ZoomSlider != null)
        {
            // The 'margins' value represents the total of the margins around the
            // image in the grid item. 8 from the ItemTemplate root grid + 8 from
            // the ItemContainerStyle * (Right + Left). If those values change,
            // this value needs to be updated to match.
            int margins = (int)this.Resources["LargeItemMarginValue"] * 4;
            double gridWidth = ImageGridView.ActualWidth -
                (int)this.Resources["DefaultWindowSidePaddingValue"];
            double ItemWidth = ZoomSlider.Value + margins;
            // We need at least 1 column.
            int columns = (int)Math.Max(gridWidth / ItemWidth, 1);
    
            // Adjust the available grid width to account for margins around each item.
            double adjustedGridWidth = gridWidth - (columns * margins);
    
            ItemSize = (adjustedGridWidth / columns);
        }
        else
        {
            ItemSize = ZoomSlider.Value;
        }
    }
    
  2. В MainPage.xaml перейдите в начало файла и добавьте привязку события SizeChanged к элементу Page.

    до:

    <Page x:Name="page"
    

    после:

    <Page x:Name="page"
          SizeChanged="{x:Bind DetermineItemSize}"
    
  3. Найдите Slider с именем ZoomSlider (в разделе Page.Resources) и добавьте привязку события ValueChanged.

    до:

    <Slider x:Name="ZoomSlider"
    

    после:

    <Slider x:Name="ZoomSlider"
            ValueChanged="{x:Bind DetermineItemSize}"
    
  4. Найдите ToggleSwitch с именем FitScreenToggle и добавьте привязку событий Toggled.

    до:

    <ToggleSwitch x:Name="FitScreenToggle"
    

    после:

    <ToggleSwitch x:Name="FitScreenToggle"
                  Toggled="{x:Bind DetermineItemSize}"
    

Запустите приложение и используйте ползунок масштабирования и Fit to screen переключатель, чтобы изменить размеры шаблона изображения. Как видно, последние изменения позволяют более подробно масштабировать или изменять размер, сохраняя код хорошо упорядоченным.

Запуск приложения с включенной функцией подгонки к экрану

Замечание

Для испытания попробуйте добавить TextBlock после ZoomSlider и привязать свойство Text к свойству ItemSize. Так как он не находится в шаблоне данных, вы можете использовать x:Bind вместо Binding, как в предыдущих привязках ItemSize.

Часть 5. Включение правок пользователей

Здесь вы создадите двусторонние привязки, чтобы пользователи могли обновлять значения, включая название изображения, рейтинг и различные визуальные эффекты.

Для этого вы обновите существующую DetailPage, которая предоставляет средство просмотра одного изображения, элемент управления масштабированием и пользовательский интерфейс редактирования.

Сначала, однако, необходимо подключить DetailPage, чтобы приложение могло перейти к нему, когда пользователь кликает по изображению в представлении галереи.

Прикрепить страницу DetailPage

  1. В Файле MainPage.xaml найдите GridView с именем ImageGridView. Чтобы сделать элементы доступными для щелчка, задайте для IsItemClickEnabled значение True и добавьте обработчик событий ItemClick.

    Подсказка

    Если ввести изменения ниже вместо копирования и вставки, появится всплывающее окно IntelliSense с надписью "<Новый обработчик событий>". Если нажать клавишу TAB, он будет заполнять значение именем обработчика методов по умолчанию и автоматически заглушить метод, показанный на следующем шаге. Затем можно нажать клавишу F12, чтобы перейти к методу в коде позади.

    до:

    <GridView x:Name="ImageGridView">
    

    после:

    <GridView x:Name="ImageGridView"
              IsItemClickEnabled="True"
              ItemClick="ImageGridView_ItemClick">
    

    Замечание

    Мы используем обычный обработчик событий вместо выражения x:Bind. Это связано с тем, что нам нужно просмотреть данные события, как показано далее.

  2. В MainPage.xaml.cs добавьте обработчик событий (или заполните его, если вы использовали подсказку на последнем шаге).

    private void ImageGridView_ItemClick(object sender, ItemClickEventArgs e)
    {
        this.Frame.Navigate(typeof(DetailPage), e.ClickedItem);
    }
    

    Этот метод просто переходит на детализированную страницу, передавая нажатый элемент, который является объектом ImageFileInfo, используемым DetailPage.OnNavigatedTo для инициализации страницы. Вам не придется реализовывать этот метод в этом руководстве, но вы можете изучить, как он работает.

  3. (Необязательно) Удалите или закомментируйте все элементы управления, добавленные в предыдущие точки воспроизведения, которые работают с выбранным изображением. Сохранение их не повредит, но сейчас гораздо сложнее выбрать изображение, не перейдя на страницу подробностей.

Теперь, когда вы подключили две страницы, запустите приложение и посмотрите вокруг. Все работает, кроме элементов управления на панели редактирования, которые не отвечают при попытке изменить значения.

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

Все элементы управления уже привязаны с помощью простых x:Bind выражений, которые мы рассмотрели в части 1. Если вы помните, это означает, что они являются однократными привязками, что объясняет, почему изменения значений не регистрируются. Чтобы устранить эту проблему, все, что нам нужно сделать, заключается в том, чтобы превратить их в двусторонние привязки.

Создание интерактивных элементов управления редактированием

  1. В DetailPage.xaml найдите TextBlock с именем TitleTextBlock и контроль RatingControl после него, и обновите их выражения x:Bind, чтобы включить Mode=TwoWay.

    до:

    <TextBlock x:Name="TitleTextBlock"
               Text="{x:Bind item.ImageTitle}"
               ... >
    <muxc:RatingControl Value="{x:Bind item.ImageRating}"
                            ... >
    

    после:

    <TextBlock x:Name="TitleTextBlock"
               Text="{x:Bind item.ImageTitle, Mode=TwoWay}"
               ... >
    <muxc:RatingControl Value="{x:Bind item.ImageRating, Mode=TwoWay}"
                            ... >
    
  2. Сделайте то же самое для всех ползунков эффекта, которые приходят после элемента управления рейтингом.

    <Slider Header="Exposure"    ... Value="{x:Bind item.Exposure, Mode=TwoWay}" ...
    <Slider Header="Temperature" ... Value="{x:Bind item.Temperature, Mode=TwoWay}" ...
    <Slider Header="Tint"        ... Value="{x:Bind item.Tint, Mode=TwoWay}" ...
    <Slider Header="Contrast"    ... Value="{x:Bind item.Contrast, Mode=TwoWay}" ...
    <Slider Header="Saturation"  ... Value="{x:Bind item.Saturation, Mode=TwoWay}" ...
    <Slider Header="Blur"        ... Value="{x:Bind item.Blur, Mode=TwoWay}" ...
    

Двусторонний режим, как и ожидалось, означает, что данные перемещаются в обоих направлениях всякий раз, когда есть изменения на обеих сторонах.

Как и односторонние привязки, описанные ранее, эти двусторонние привязки теперь обновляют пользовательский интерфейс при изменении связанных свойств благодаря реализации INotifyPropertyChanged в классе ImageFileInfo. Однако при двусторонней привязке значения также будут перемещаться из пользовательского интерфейса в привязанные свойства, когда пользователь взаимодействует с элементом управления. Ничего больше не требуется на стороне XAML.

Запустите приложение и попробуйте элементы управления редактированием. Как видно, при внесении изменения он влияет на значения изображения, и эти изменения сохраняются при переходе на главную страницу.

Часть 6. Форматирование значений с помощью привязки функции

Одна последняя проблема остается. При перемещении ползунка эффектов метки рядом с ними по-прежнему не изменяются.

ползунки Эффект со значениями меток по умолчанию

Последняя часть этого руководства заключается в добавлении привязок, которые форматируют значения ползунка для отображения.

Привязка меток слайдера эффектов и форматирование значений для отображения

  1. Найдите TextBlock после ползунка Exposure и замените значение Text выражением привязки, показанным здесь.

    до:

    <Slider Header="Exposure" ... />
    <TextBlock ... Text="0.00" />
    

    после:

    <Slider Header="Exposure" ... />
    <TextBlock ... Text="{x:Bind item.Exposure.ToString('N', culture), Mode=OneWay}" />
    

    Это называется привязкой функции, так как вы привязываются к возвращаемого значения метода. Метод должен быть доступен через внутренний код страницы или тип x:DataType, если вы используете шаблон данных. В этом случае метод — это знакомый метод ToString .NET, к которому осуществляется доступ через свойство элемента страницы, а затем через свойство Exposure элемента. (Это показывает, как можно привязаться к методам и свойствам, которые глубоко вложены в цепочку соединений.)

    Привязка функции — это идеальный способ форматирования значений для отображения, так как вы можете передавать другие источники привязки в качестве аргументов метода, и выражение привязки будет отслеживать изменения этих значений, как и ожидалось, при одностороннем режиме. В этом примере аргумент языка и региональных параметров является ссылкой на неизменное поле, реализованное в коде, но это может быть так же легко, как свойство, которое вызывает события. В этом случае любые изменения значения свойства приведут к тому, что выражение x:Bind вызовет ToString с новым значением, а затем обновит пользовательский интерфейс с результатом.

  2. Сделайте то же самое для меток TextBlock, которые отмечают другие ползунки эффектов.

    <Slider Header="Temperature" ... />
    <TextBlock ... Text="{x:Bind item.Temperature.ToString('N', culture), Mode=OneWay}" />
    
    <Slider Header="Tint" ... />
    <TextBlock ... Text="{x:Bind item.Tint.ToString('N', culture), Mode=OneWay}" />
    
    <Slider Header="Contrast" ... />
    <TextBlock ... Text="{x:Bind item.Contrast.ToString('N', culture), Mode=OneWay}" />
    
    <Slider Header="Saturation" ... />
    <TextBlock ... Text="{x:Bind item.Saturation.ToString('N', culture), Mode=OneWay}" />
    
    <Slider Header="Blur" ... />
    <TextBlock ... Text="{x:Bind item.Blur.ToString('N', culture), Mode=OneWay}" />
    

Теперь, когда вы запускаете приложение, все работает, включая метки ползунка.

ползунки эффекта с активными метками

Заключение

Это руководство дало вам вкус привязки данных и показал вам некоторые доступные функциональные возможности. Одно слово осторожности перед тем, как мы заключим: не все привязывается, а иногда значения, к которым вы пытаетесь подключиться, несовместимы со свойствами, которые вы пытаетесь привязать. Существует много гибкости в привязке, но она не будет работать в каждой ситуации.

Один из примеров проблемы, не устраненной привязкой, заключается в том, что элемент управления не имеет подходящих свойств для привязки, как и функция масштабирования страницы сведений. Этот ползунок масштабирования должен взаимодействовать с ScrollViewer, отображающей изображение, но ScrollViewer можно обновить только с помощью метода ChangeView. В этом случае мы используем стандартные обработчики событий, чтобы синхронизировать ScrollViewer и ползунок масштабирования; подробности см. в методах ZoomSlider_ValueChanged и MainImageScroll_ViewChanged в DetailPage.

Тем не менее привязка — это эффективный и гибкий способ упростить код и сохранить логику пользовательского интерфейса отдельно от логики данных. Это значительно упрощает настройку любой стороны этого деления при снижении риска внедрения ошибок на другой стороне.

Одним из примеров разделения пользовательского интерфейса и данных является свойство ImageFileInfo.ImageTitle. Это свойство (и свойство ImageRating) немного отличается от свойства ItemSize, созданного в части 4, так как значение хранится в метаданных файла (предоставляемых через тип ImageProperties) вместо поля. Кроме того, ImageTitle возвращает значение ImageName (установлено как имя файла), если в метаданных файла нет заголовка.

public string ImageTitle
{
    get => String.IsNullOrEmpty(ImageProperties.Title) ? ImageName : ImageProperties.Title;
    set
    {
        if (ImageProperties.Title != value)
        {
            ImageProperties.Title = value;
            var ignoreResult = ImageProperties.SavePropertiesAsync();
            OnPropertyChanged();
        }
    }
}

Как видно, средство задания обновляет свойство ImageProperties.Title, а затем вызывает SavePropertiesAsync для записи нового значения в файл. (Это асинхронный метод, но мы не можем использовать ключевое слово await в свойстве — и вы бы не захотели, потому что методы получения и задания свойств должны завершаться немедленно. Зато вы вызовете метод и проигнорируете возвращаемый объект Task.)

Продвигаться дальше

Теперь, когда вы завершили эту лабораторную работу, у вас достаточно компетентных знаний, чтобы самостоятельно решить проблему.

Как вы могли заметить, если изменить уровень масштабирования на странице сведений, он сбрасывается автоматически при переходе назад, а затем снова выберите то же изображение. Можно ли выяснить, как сохранить и восстановить уровень масштабирования для каждого изображения по отдельности? Удачи!

У вас должны быть все необходимые сведения в этом руководстве, но если вам нужны дополнительные рекомендации, то документацию по привязке данных можно получить всего в один клик. Начните с этого: