Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Предположим, вы разработали и внедрили красивый пользовательский интерфейс, наполненный временными изображениями, текстом-заглушкой "lorem ipsum" и элементами управления, которые пока ничего не делают. Затем необходимо подключить его к реальным данным и преобразовать его из прототипа разработки в живое приложение.
В этом руководстве вы узнаете, как заменить шаблон привязками данных и создать другие прямые связи между пользовательским интерфейсом и данными. Вы также узнаете, как отформатировать или преобразовать данные для отображения и сохранить пользовательский интерфейс и данные в синхронизации. По завершении работы с этим руководством вы сможете улучшить простоту и организацию кода XAML и C#, что упрощает обслуживание и расширение.
Вы начнете с упрощенной версии примера PhotoLab. Эта начальная версия включает полный уровень данных, а также базовые макеты страниц XAML и оставляет много функций, чтобы упростить обзор кода. В этом руководстве не создается полное приложение, поэтому обязательно ознакомьтесь с окончательной версией, чтобы увидеть такие функции, как пользовательские анимации и адаптивные макеты. Окончательную версию можно найти в корневой папке репозитория Windows-appsample-photo-lab.
Пример приложения PhotoLab содержит две страницы. На главной странице отображается представление коллекции фотографий, а также некоторые сведения о каждом файле изображения.
Страница деталей отображает одну фотографию после её выбора. Всплывающее меню редактирования позволяет изменять, переименовать и сохранить фотографию.
Предпосылки
- Visual Studio 2019 или более новая версия: Скачать Visual Studio (Выпуск Community является бесплатным.)
- Пакет SDK для Windows (10.0.17763.0 или более поздней версии): скачать последнюю версию пакета SDK для Windows (бесплатно)
- Windows 10 версии 1809 или более поздней
Часть 0. Получение начального кода с GitHub
В этом руководстве вы начните с упрощенной версии примера PhotoLab.
Перейдите на страницу GitHub для примера: https://github.com/Microsoft/Windows-appsample-photo-lab.
Затем необходимо клонировать или скачать пример. Нажмите кнопку Клонировать или скачать. Появляется вложенное меню.
Если вы не знакомы с 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
.Дважды щелкните
Photolab.sln
, чтобы открыть решение в Visual Studio.
Часть 1. Замена переменных
Здесь вы создаете одноразовые привязки в XAML-шаблоне данных для отображения изображения и его метаданных вместо контента-заполнителя.
Однократные привязки предназначены только для чтения, неизменяемых данных, что означает, что они обладают высокой производительностью и их легко создавать, позволяя отображать большие наборы данных в GridView
и ListView
контролах.
Замените заполнители единовременными привязками
Откройте папку
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
выражения предоставляют значение свойству пользовательского интерфейса, получая значение из свойства объекта данных. В шаблонах указанное свойство является свойством того, к чему было установлено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 в шаблон данных и используйте трюк x:Bind IntelliSense для поиска свойства для отображения.
Часть 2. Использование привязки для подключения пользовательского интерфейса коллекции к изображениям
Здесь вы создадите однократные привязки на странице XAML для подключения представления коллекции к коллекции изображений, заменив существующий процедурный код, который выполняет это в коде программной части. Вы также создадите кнопку "Удалить", чтобы посмотреть, как изменится галерея представления при удалении изображений из коллекции. В то же время вы узнаете, как привязывать события к обработчикам событий, чтобы получить большую гибкость, чем та, которую обеспечивают традиционные обработчики событий.
Все привязки, описанные до сих пор, находятся внутри шаблонов данных и ссылаются на свойства класса, указанного значением 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
с именем MainCommandBar и добавьте новую кнопку перед кнопкой масштабирования. (Элементы управления масштабированием еще не работают. Вы подключитесь к ним в следующей части руководства.)<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
значения, которые работают почти так же. Они сообщают механизму привязки, что значение привязки является привязкой к свойству 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
. Вопросительный знак проверяет, является ли событиеPropertyChanged
null, то есть были ли добавлены какие-нибудь обработчики событий. Каждая односторонняя или двусторонняя привязка добавляет обработчик событий в скрытом режиме, но если здесь никто не слушает, ничего больше не произойдет. ЕслиPropertyChanged
не имеет значения NULL, тоInvoke
вызывается со ссылкой на источник событий (сама страница, представленная ключевым словомthis
) и объект event-args, указывающий имя свойства. Эта информация позволит односторонним или двусторонним привязкам к свойствуItemSize
быть в курсе всех изменений, чтобы они могли обновить привязанный пользовательский интерфейс.В 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
Добавьте метод
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 с надписью "<Новый обработчик событий>". Если нажать клавишу 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
, если вы используете шаблон данных. В этом случае метод — это знакомый методToString
.NET, к которому осуществляется доступ через свойство элемента страницы, а затем через свойствоExposure
элемента. (Это показывает, как можно привязаться к методам и свойствам, которые глубоко вложены в цепочку соединений.)Привязка функции — это идеальный способ форматирования значений для отображения, так как вы можете передавать другие источники привязки в качестве аргументов метода, и выражение привязки будет отслеживать изменения этих значений, как и ожидалось, при одностороннем режиме. В этом примере аргумент языка и региональных параметров
является ссылкой на неизменное поле, реализованное в коде, но это может быть так же легко, как свойство, которое вызывает события. В этом случае любые изменения значения свойства приведут к тому, что выражение 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
.)
Продвигаться дальше
Теперь, когда вы завершили эту лабораторную работу, у вас достаточно компетентных знаний, чтобы самостоятельно решить проблему.
Как вы могли заметить, если изменить уровень масштабирования на странице сведений, он сбрасывается автоматически при переходе назад, а затем снова выберите то же изображение. Можно ли выяснить, как сохранить и восстановить уровень масштабирования для каждого изображения по отдельности? Удачи!
У вас должны быть все необходимые сведения в этом руководстве, но если вам нужны дополнительные рекомендации, то документацию по привязке данных можно получить всего в один клик. Начните с этого: