Руководство. Создание привязок данных
Предположим, что вы спроектировали и реализовали красивый пользовательский интерфейс с изображениями-заполнителями, шаблоном с текстом-заполнителем и элементами управления без каких-либо функций. Далее необходимо подключить его к реальным данным и превратить из прототипа в действующее приложение.
В этом руководстве вы узнаете, как заменить шаблон привязками данных и создать другие прямые ссылки между пользовательским интерфейсом и данными. Вы также узнаете, как отформатировать или преобразовать данные для отображения и сохранить пользовательский интерфейс и данные в синхронизации. По завершении работы с этим руководством вы сможете улучшить простоту и организацию кода XAML и C#, что упрощает обслуживание и расширение.
Начнем с упрощенной версии примера PhotoLab. Эта начальная версия включает в себя полный уровень данных и базовые макеты страниц на языке XAML, но для удобства просмотра кода в ней отсутствуют многие возможности. В этом руководстве не будет создано полноценное приложение, поэтому ознакомьтесь с окончательной версией, в которой присутствуют такие возможности, как пользовательские анимации и адаптивные макеты. Окончательную версию можно найти в корневой папке репозитория Windows-appsample-photo-lab.
Пример приложения PhotoLab содержит две страницы. Главная страница, на которой отображается представление фотоальбома, а также сведения о каждом файле изображения.
Страница сведений, на которой отображается одна выбранная фотография. Всплывающее меню редактирования позволяет изменить, переименовать и сохранить фотографию.
Необходимые компоненты
- Visual Studio 2019 или более поздней версии: скачайте Visual Studio (выпуск Community free.)
- Пакет SDK для Windows (10.0.17763.0 или более поздней версии): скачайте последний пакет SDK для Windows (бесплатно)
- Windows 10, версия 1809 или выше.
Часть 0. Получение начального кода с GitHub
В этом учебнике мы начнем с упрощенной версии примера PhotoLab.
Перейдите на страницу GitHub с примером кода: https://github.com/Microsoft/Windows-appsample-photo-lab.
Далее необходимо клонировать или скачать пример. Нажмите кнопку Clone or download (Клонировать или скачать). Откроется подменю.
Если вы не знакомы с GitHub:
a. Нажмите кнопку Download ZIP (Скачать ZIP-файл) и сохраните файл в локальном расположении. Будет выполнено скачивание ZIP-файла с необходимыми файлами проекта.
b. Извлеките файл. Используйте проводник, чтобы перейти к скачанному ZIP-файлу, щелкните его правой кнопкой мыши и выберите Извлечь все.
c. Перейдите к локальной копии примера кода, а затем в каталог
Windows-appsample-photo-lab-master\xaml-basics-starting-points\data-binding
.Если вы знакомы с GitHub:
a. Клонируйте главную ветвь репозитория локально.
b. Перейдите в каталог
Windows-appsample-photo-lab\xaml-basics-starting-points\data-binding
.Дважды щелкните
Photolab.sln
, чтобы открыть решение в Visual Studio.
Часть 1. Замена заполнителей
В этой части вы создадите однократные привязки в шаблоне данных XAML для отображения реальных изображений и их метаданных вместо заполнителей.
Однократные привязки предназначены только для чтения, изменяющихся данных, что означает, что они являются высокопроизводительной и ListView
легкой для создания, позволяя отображать большие наборы данных и GridView
элементы управления.
Замена заполнителей однократными привязками
Откройте папку
xaml-basics-starting-points\data-binding
и запустите файлPhotoLab.sln
в Visual Studio.Убедитесь, что в качестве платформы решения выбрана архитектура x86 или x64, а не ARM, а затем запустите приложение. Ниже показано состояние приложения с заполнителями пользовательского интерфейса до добавления привязок.
Откройте файл MainPage.xaml и найдите шаблон
DataTemplate
с именем ImageGridView_DefaultItemTemplate. Этот шаблон будет обновлен для использования привязок данных.До:
<DataTemplate x:Key="ImageGridView_DefaultItemTemplate">
Значение
x:Key
используетсяImageGridView
для выбора этого шаблона для отображения объектов данных.x:DataType
Добавьте значение в шаблон.После:
<DataTemplate x:Key="ImageGridView_DefaultItemTemplate" x:DataType="local:ImageFileInfo">
x:DataType
указывает тип, для которого используется шаблон. В этом случае шаблон предназначен для классаImageFileInfo
(гдеlocal:
указывает на локальное пространство имен, как определено в объявлении xmlns в верхней части файла).x:DataType
требуется при использованииx:Bind
выражений в шаблоне данных, как описано далее.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
получают значение от свойства data-object и предоставляют это значение свойству пользовательского интерфейса. В шаблонах указанное свойство является свойством любогоx:DataType
заданного значения. Таким образом, в этом случае источник данных является свойствомImageFileInfo.ImageSource
.Примечание.
Значение
x:Bind
также позволяет редактору знать о типе данных, поэтому вместо ввода имени свойства вx:Bind
выражении можно использовать IntelliSense. Попробуйте использовать код, который вы только что вставили: поместите курсор сразу послеx:Bind
и нажмите пробел, чтобы просмотреть список свойств, к которые можно привязать.Замените значения других элементов управления пользовательского интерфейса таким же образом. (Попробуйте сделать это с помощью функции 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 в шаблон данных и используйте функцию IntelliSense и x:Bind, чтобы найти свойство для отображения.
Часть 2. Использование привязки для подключения пользовательского интерфейса коллекции к изображениям
В этой части мы создадим однократные привязки на странице XAML, чтобы связать представление коллекции с набором изображений. Для этого необходимо заменить существующий процедурный код, выполняющий эту функцию, в коде программной части. Также мы создадим кнопку Delete (Удалить), чтобы проверить, как изменится представление коллекции при удалении изображений из набора. Также вы узнаете, как привязывать события к обработчикам событий, чтобы обеспечить большую гибкость по сравнению с традиционными обработчиками событий.
Все привязки, описанные до сих пор, находятся внутри шаблонов данных и ссылаются на свойства класса, указанного значением x:DataType
. Как насчет остального кода XAML на вашей странице?
x:Bind
выражения вне шаблонов данных всегда привязаны к самой странице. Это означает, что вы можете ссылаться на все, что вы помещаете в код или объявляете в XAML, включая настраиваемые свойства и свойства других элементов управления пользовательского интерфейса на странице (если они имеют x:Name
значение).
В примере PhotoLab одна из таких привязок заключается в подключении основного GridView
элемента управления непосредственно к коллекции изображений, а не в коде. В дальнейшем будут представлены другие примеры.
Привязка основного элемента управления GridView к коллекции изображений
В MainPage.xaml.cs найдите
GetItemsAsync
метод и удалите код, который задаетItemsSource
.До:
ImageGridView.ItemsSource = Images;
После:
// Replaced with XAML binding: // ImageGridView.ItemsSource = Images;
В 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
.
Добавление кнопки удаления
В файле MainPage.xaml найдите
CommandBar
с именем MainCommandBa и добавьте новую кнопку перед кнопкой масштабирования. (Элементы управления масштабом пока не работают. Вы подключите их в следующей части руководства.)<AppBarButton Icon="Delete" Label="Delete selected image" Click="{x:Bind DeleteSelectedImage}" />
Если вы уже знакомы с XAML, это
Click
значение может выглядеть необычно. В предыдущих версиях XAML в качестве этого значения использовался метод с определенной подписью обработчика событий, включавшей в себя, как правило, параметры отправителя событий и объект аргументов, характерных для разных событий. Этот способ также можно использовать, если вам нужны аргументы событий, но с помощьюx:Bind
можно обеспечивать связь и с другими методами. Например, если вам не нужны данные событий, можно выполнить привязку к методам без параметров, как в нашем случае.В 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
, которые работают практически так же. Эти значения сообщают модулю привязки, что Binding Value — это привязка к свойству 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 для обновления пользовательского интерфейса
В MainPage.xaml.cs измените сигнатуру
MainPage
класса, чтобы он реализовалINotifyPropertyChanged
интерфейс.До:
public sealed partial class MainPage : Page
После:
public sealed partial class MainPage : Page, INotifyPropertyChanged
Таким образом система привязки узнает, что у
MainPage
есть событиеPropertyChanged
(добавляется ниже), которое могут ожидать привязки для обновления пользовательского интерфейса.PropertyChanged
Добавьте событие вMainPage
класс.public event PropertyChangedEventHandler PropertyChanged;
Это событие обеспечивает полную реализацию, необходимую интерфейсом
INotifyPropertyChanged
. Однако чтобы это событие дало результат, необходимо явным образом породить его в пользовательских свойствах.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
. Вопросительный знак проверяет, имеет ли событие значение NULL, т. е. добавляется лиPropertyChanged
какие-либо обработчики событий. Каждая односторонняя или двухсторонняя привязка незаметно добавляет обработчик событий, но если прослушиватели отсутствуют, никаких дальнейших действий выполнено не будет. Но, если значение событияPropertyChanged
не нулевое, вызывается методInvoke
со ссылкой на источник события (саму страницу, представленную ключевым словомthis
) и объект аргументы события, указывающий имя свойства. С помощью этой информации любые одностороннюю или двухстороннюю привязки кItemSize
свойству будут проинформированы о любых изменениях, чтобы они могли обновить привязанный пользовательский интерфейс.В MainPage.xaml найдите именованный
DataTemplate
ImageGridView_DefaultItemTemplate
и заменитеWidth
Height
значения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
Добавьте метод в
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; } }
В MainPage.xaml перейдите в начало файла и добавьте
SizeChanged
привязку события к элементуPage
.До:
<Page x:Name="page"
После:
<Page x:Name="page" SizeChanged="{x:Bind DetermineItemSize}"
Найдите
Slider
с именемZoomSlider
(в разделеPage.Resources
) и добавьте привязку событийValueChanged
.До:
<Slider x:Name="ZoomSlider"
После:
<Slider x:Name="ZoomSlider" ValueChanged="{x:Bind DetermineItemSize}"
Найдите именованную
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
В файле MainPage.xaml найдите
GridView
с именемImageGridView
. Чтобы сделать элементы доступными для щелчка, задайте для параметраIsItemClickEnabled
значениеTrue
и добавьте обработчик событийItemClick
.Совет
Если изменение ввести ниже, а не скопировать и вставить, отобразится всплывающее окно IntelliSense с текстом <New Event Handler> (Новый обработчик событий). При нажатии клавиши TAB в качестве значения будет использовано имя обработчика метода по умолчанию, а метод, показанный в следующем шаге, будет автоматически снабжен заглушкой. Затем можно перейти к методу в коде программной части с помощью клавиши F12.
До:
<GridView x:Name="ImageGridView">
После:
<GridView x:Name="ImageGridView" IsItemClickEnabled="True" ItemClick="ImageGridView_ItemClick">
Примечание.
В этом случае мы используем стандартный обработчик событий вместо выражения x:Bind. Причина этого в том, что нам необходимо видеть данные событий, как показано далее.
В файле MainPage.xaml.cs добавьте обработчик событий (или заполните его, если вы воспользовались подсказкой на предыдущем шаге).
private void ImageGridView_ItemClick(object sender, ItemClickEventArgs e) { this.Frame.Navigate(typeof(DetailPage), e.ClickedItem); }
Этот метод переходит на страницу сведений, передает выбранный пользователем элемент, который является объектом
ImageFileInfo
, используемым методом DetailPage.OnNavigatedTo для инициализации страницы. В этом руководстве вам не придется реализовывать данный метод, но вы можете ознакомиться с его функциями.(Необязательно.) Удалите или закомментируйте все элементы управления, добавленные в предыдущих точках воспроизведения, которые работают с выбранным на данный момент изображением. Если их оставить, ничего плохого не произойдет, но при этом будет гораздо сложнее выбрать изображение без перехода на страницу сведений.
Подключив две страницы, запустите приложение и изучите его. Работает все, кроме элементов управления на панели редактирования: они не реагируют при попытке изменения значений.
Как видно, в текстовом поле названия отображается название, а также его можно изменить. Для подтверждения изменений необходимо перевести фокус на другой элемент управления, но название в верхнем левом углу экрана еще не обновляется.
Все элементы управления уже привязаны с помощью простых x:Bind
выражений, которые мы рассмотрели в части 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}" ... >
Сделайте то же самое для всех ползунков эффектов, расположенных после элемента управления оценкой.
<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. Форматирование значений с помощью привязки функции
Остается последняя проблема. При перемещении ползунков эффектов метки рядом с ними не меняются.
Последняя часть этого руководства посвящена добавлению привязок для форматирования отображаемых значений ползунков.
Привязка меток ползунков эффектов и форматирование отображаемых значений
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
типа, если вы находитесь в шаблоне данных. В этом случае метод — это знакомый метод .NETToString
, к которому осуществляется доступ через свойство элемента страницы, а затем черезExposure
свойство элемента. (В этом примере показано, как выполнить привязку к методам и свойствам, расположенным глубоко в цепи связей.)Привязка функции — отличный способ форматирования отображаемых значений, так как при этом можно передать другие источники привязки, например аргументы методов, а выражение привязки будет ожидать передачи этих значений, как и должно происходить в одностороннем режиме. В этом примере аргумент culture является ссылкой на неизменное поле, реализованное в коде программной части, но он также мог бы быть свойством, вызывающим события
PropertyChanged
. В этом случае любые изменения значения свойства могут вызватьx:Bind
выражениеToString
с новым значением, а затем обновить пользовательский интерфейс с результатом.Сделайте то же самое для
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
.)
Дальнейшее продвижение в этом направлении
Завершив это практическое занятие, вы получили достаточный объем знаний о привязке, чтобы в дальнейшем решать задачи самостоятельно.
Как вы могли заметить, при изменении уровня масштабирования на странице сведений он автоматически сбрасывается при возврате и повторном выборе того же изображения. Сможете ли вы разобраться, как сохранить и восстановить уровень масштабирования отдельно для каждого изображения? Удачи!
В этом руководстве должны быть все необходимые сведения, но если вам нужны дополнительные рекомендации, воспользуйтесь доступной документацией по привязке данных. Начните здесь: