Pull to refresh

Создание приложения для Windows Phone 7 от начала до конца. Часть 10. Преобразование значений, создание классов данных

Reading time8 min
Views4.5K
Original author: Microsoft Developer Guidance team
Предыдущая часть

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

Преобразование значений


Вы могли заметить, что в примерах в предыдущей части приведены привязки, которые включают настройку Converter. Этот параметр позволяет элементу управления отображать значения привязанного свойства в некотором другом формате. В этих конкретных примерах значения отображаются с использованием формата строки, указанного в настройке ConverterParameter. Следующий XAML-код показывает Converter и ConverterParameter для Date TextBlock.
  1. <TextBlock Style="{StaticResource SummaryStyle}"
  2.     Text="{Binding Date, Converter={StaticResource     StringFormatter}, ConverterParameter=\{0:d\} }"
  3.     Width="105" TextWrapping="Wrap"/>
* This source code was highlighted with Source Code Highlighter.

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

В предыдущих примерах свойство Converter имеет значение объекта, определённого как ресурс. Следующий код XAML создает новый экземпляр StringFormatter и объявляет его доступным для Grid как StaticResource под названием «StringFormatter».
  1. <Grid.Resources>
  2.     <local:StringFormatter x:Key="StringFormatter" />
  3. </Grid.Resources>
* This source code was highlighted with Source Code Highlighter.

Для того, чтобы выполнять преобразования значений, класс StringFormatter должен реализовывать интерфейс IValueConverter, который определяет методы Convert и ConvertBack. Поскольку привязка является OneWay, класс StringFormatter нуждается только в методе Convert, который форматирует строку, используя метод String.Format, как показано в следующем коде:
  1. public class StringFormatter : IValueConverter
  2. {
  3.     public object Convert(object value, Type targetType, object parameter,
  4.         CultureInfo culture)
  5.     {
  6.         // Retrieve the format string and use it to format the value.
  7.         var formatString = parameter as string;
  8.         if (!string.IsNullOrEmpty(formatString))
  9.         {
  10.             return string.Format(culture, formatString, value);
  11.         }
  12.  
  13.         // If the format string is null or empty, simply call ToString()
  14.         // on the value.
  15.         return value.ToString();
  16.     }
  17.  
  18.     // No need to implement converting back on a one-way binding
  19.     public object ConvertBack(object value, Type targetType,
  20.         object parameter, CultureInfo culture)
  21.     {
  22.         throw new NotImplementedException();
  23.     }
  24. }
* This source code was highlighted with Source Code Highlighter.

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

image

Другой пример преобразования значения в файле ZeroFormatter.cs. Приложение Fuel Tracker использует класс ZeroFormatter для отображения нулевых значений в виде пустых строк. Это удобно при привязке к объектам, для которых пользователи будут указывать значения во время ввода данных. Например, целочисленные свойства для новых объектов устанавливаются равными нулю по умолчанию, но элементы управления, которые отображают эти свойства должны появляться пустыми до того, как пользователь введёт значение.

Создание классов данных


Перед тем, как вы сможете отобразить данные в пользовательском интерфейсе, вам обычно требуется организовать данные в классы.
Вы можете создавать классы данных различными способами. Например, в приложениях, которые взаимодействуют с внешними источниками данных, вы можете использовать классы данных, сгенерированные с помощью ADO.NET Entity Framework или WCF Data Services. Для простых приложений вы можете использовать классы простых старых объектов CLR (POCO — plain old CLR object), которые создаются вручную. Эти классы часто содержат немного больше, чем свойства, для хранения данных и небольшой код уведомления об изменениях.

Совет:
В случае, если ваше приложение не является простым и автономным (self-contained), и ожидается его дальнейшая разработка, оно может выиграть от применения более современной архитектуры. Обычно это означает, инкапсуляцию дополнительных видов кода в отдельные классы или слои, с тем чтобы свести к минимуму побочные эффекты будущих изменений, облегчить процесс отладки, а также реализовать поддержку unit-тестирования.

В XAML-приложениях общим шаблоном для использования является Model-View-ViewModel (MVVM). Поскольку приложение Fuel Tracker относительно простое, оно не реализует шаблон MVVM, и эта статья не описывает эту модель более подробно. Для дополнительной информации по этой теме вы можете перейти по следующим ссылкам: Implementing the Model-View-ViewModel Pattern in a Windows Phone Application и Patterns and Practices Windows Phone 7 Development Guide.

Fuel Tracker представляет собой простое приложение и использует CLR объекты для своей модели данных. На следующем изображении показаны классы Car, Fillup, и CarDataStore для приложения Fuel Tracker. Есть и другие классы в приложении, но эти являются основными.

image

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

Обратите внимание, что классы Car и Fillup реализуют интерфейс INotifyPropertyChanged. INotifyPropertyChanged требуется для большинства типов привязки данных, чтобы всегда отображать пользовательский интерфейс в актуальном состоянии. Дополнительные сведения о привязке данных смотрите в предыдущей части «Отображение данных».

Уведомление об изменении


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

Для того, чтобы предоставить возможность уведомлений об изменениях, классы должны реализовывать интерфейс INotifyPropertyChanged. Уведомление об изменении не всегда необходимо, но это требуется в очень многих распространенных сценариях, так что полезно реализовать INotifyPropertyChanged просто на всякий случай. Реализация проста, как показано в следующем примере кода из Car.cs.
  1. public class Car : INotifyPropertyChanged
  2. {
  3.     private string _name;
  4.     public string Name
  5.     {
  6.         get { return _name; }
  7.         set
  8.         {
  9.             _name = value;
  10.             NotifyPropertyChanged("Name");
  11.         }
  12.     }
  13.  
  14.     // ... other properties ...
  15.  
  16.     public event PropertyChangedEventHandler PropertyChanged = delegate { };
  17.  
  18.     private void NotifyPropertyChanged(string propertyName)
  19.     {
  20.         PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
  21.     }
  22. }
* This source code was highlighted with Source Code Highlighter.

Когда вы привязываете объекты к пользовательскому интерфейсу, binding engine подписывается на событие объекта PropertyChanged так, чтобы он смог обновить все привязанные элементы управления всякий раз, когда произойдут изменения значений свойств. Обратите внимание, что событие PropertyChanged инициализируется пустым делегатом, чтобы гарантировать, что оно никогда не равно null. Это позволяет методу NotifyPropertyChanged вызывать событие без необходимости предварительной проверки, существуют ли у него какие-либо подписчики. Если вы не знакомы с этим интерфейсом, вы можете просто повторить этот шаблон для реализации.

Уведомление об изменении для коллекции


Для полной поддержки уведомлений об изменении для коллекции, например, о добавлении или удалении из коллекции, коллекция должна реализовывать интерфейс INotifyCollectionChanged. ObservableCollection является динамической коллекцией данных, предоставленной framework'ом, она может содержать generic типы. ObservableCollection реализует как INotifyPropertyChanged, так и INotifyCollectionChanged, так что самый простой способ поддержки уведомлений об изменении для коллекции — просто поместить свои элементы в ObservableCollection. Элементы, которые вы поместили в ObservableCollection, должны реализовывать INotifyPropertyChanged, если вы хотите также получать уведомления об изменении свойств для объектов в коллекции.

Приложение Fuel Tracker использует одну коллекцию с именем FillupHistory, содержащую информацию обо всех заправках. FillupHistory является ObservableCollection из объектов Fillup, как показано в следующем примере кода из Car.cs.
  1. public ObservableCollection<Fillup> FillupHistory
  2. {
  3.     get { return _fillupHistory; }
  4.     set
  5.     {
  6.       _fillupHistory = value;
  7.         if (_fillupHistory != null)
  8.         {
  9.             _fillupHistory.CollectionChanged += delegate
  10.             {
  11.                 NotifyPropertyChanged("AverageFuelEfficiency");
  12.             };
  13.         }
  14.         NotifyPropertyChanged("FillupHistory");
  15.         NotifyPropertyChanged("AverageFuelEfficiency");
  16.     }
  17. }
* This source code was highlighted with Source Code Highlighter.

Этот код также демонстрирует, как использовать уведомления об изменениях для взаимодействия с другими свойствами. Изменение истории заправок (fill-up history) или любых элементов внутри повлияет на расчет средней эффективности использования топлива (average fuel efficiency). Поэтому установка свойства FillupHistory вызывает уведомление об изменении для себя и для свойства AverageFuelEfficiency. Кроме того, установив свойство связанным с обработчиком события CollectionChanged для новой коллекции, вы получите уведомление об изменении для AverageFuelEfficiency всякий раз, когда элемент в коллекции будет добавлен, удалён или изменён.

Следующая часть
Tags:
Hubs:
Total votes 31: ↑19 and ↓12+7
Comments2

Articles