Использование DataBinding и DataTemplate при помощи Expression Blend в Silverlight - Реализация приложения и его интерфейса

ОГЛАВЛЕНИЕ

Реализация приложения и его интерфейса

Нашей целью является создание n-уровнего приложения, где у нас будет три основных уровня:

  • Уровень пользовательского интерфейса
  • Бизнес уровень
  • Уровень хранилища данных

Уровень пользовательского интерфейса является той частью, которую видит пользователь и с которой он взаимодействует - мы создадим данный уровень в Expression Blend при помощи элементов управления Silverlight.

Бизнес-уровень управляет логикой вашего приложения - мы создадим его в Visual Studio при помощи C#.  

Для данного приложения бизнес-уровень будет инкапсулирован в три относительно простых класса:

  1. Library представляет собой набор книг, полученных из более объемного набора. Больший набор может быть представлен в виде базы данных, веб-сервиса либо какого нибудь другого механизма хранения.
  2. Book является неким уровнем представления книги, хотя он может также представлять другие версии печати. Он содержит такие свойства, как автор (author), заголовок (title), издатель (publisher), дату публикации (publication date).
  3. Author представляет собой то, что нам необходимо знать об авторе книги. Автор также необходим для книги, поэтому он вынесен в отдельный класс.

Нашими целями являются:

  1. Привязка данных (Data Binding): привязка элементов списка к набору Books в Library
  2. Шаблон данных (Data Template): указание элементу ListBox как отображать (некоторые) свойства каждой книги в списке

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

Начните с удаления ListBox (и его содержимого) из LayoutRoot (мы сделаем другой по ходу статьи).

Blend не самое лучшее место для создания классов бизнес-уровня, поэтому щелкните по File-Save all (Файл - Сохранить все)и затем щелкните правой кнопкой мыши по приложению и выберите Edit in Visual Studio (Редактировать в Visual Studio), как это показано на рисунке 7-11.

 

Рисунок 7-11. Переход от Blend к Visual Studio для создания классов

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

// Author.cs
public class Author
{
  private string privateName;
  public string Name
  {
  get
  {
  return privateName;
  }
  set
  {
  privateName = value;
  }
  }

  // Book.cs
  public class Book : INotifyPropertyChanged
  {
  public event PropertyChangedEventHandler PropertyChanged;

  public Book()
  {

  }

  public Book(string title, ObservableCollection authorList, double coverPrice, string isbn10, string publisher, int edition, int printing, string pubYear, double rating)
  {

  this.Title = title;

  this.Authors = new ObservableCollection();
  foreach (Author auth in authorList)
  {
  this.Authors.Add(auth);
  }

  this.CoverPrice = coverPrice;
  this.ISBN10 = isbn10;
  this.Publisher = publisher;
  this.Edition = edition;
  this.Printing = printing;
  this.PubYear = pubYear;
  this.Rating = rating;

  }

  private string privateTitle;
  public string Title
  {

  get
  {
  return privateTitle;
  }
  set
  {
  privateTitle = value;
  NotifyPropertyChanged("Title");
  }
  }

  public string NumAuthors
  {
  get
  {
  return privateAuthors.Count.ToString();
  }
  }

  private ObservableCollection privateAuthors;
  public ObservableCollection Authors
  {
  get
  {
  return privateAuthors;
  }
  internal set
  {
  privateAuthors = value;
  NotifyPropertyChanged("Authors");
  }
  }

  private double privateCoverPrice;
  public double CoverPrice
  {
  get
  {
  return privateCoverPrice;
  }
  set
  {
  privateCoverPrice = value;
  NotifyPropertyChanged("CoverPrice");
  }
  }

  private string privateISBN10;
  public string ISBN10
  {
  get
  {
  return privateISBN10;
  }
  set
  {
  privateISBN10 = value;
  NotifyPropertyChanged("ISBN10");
  }
  }

  private string privatePublisher;
  public string Publisher
  {
  get
  {
  return privatePublisher;
  }
  set
  {
  privatePublisher = value;
  NotifyPropertyChanged("Publisher");
  }
  }

  private int privateEdition;
  public int Edition
  {
  get
  {
  return privateEdition;
  }
  set
  {
  privateEdition = value;
  NotifyPropertyChanged("Edition");
  }
  }

  private int privatePrinting;
  public int Printing
  {
  get
  {
  return privatePrinting;
  }
  set
  {
  privatePrinting = value;
  NotifyPropertyChanged("Printing");
  }
  }

  private string privatePubYear;
  public string PubYear
  {
  get
  {
  return privatePubYear;
  }
  set
  {
  privatePubYear = value;
  NotifyPropertyChanged("PubYear");
  }
  }

  private double privateRating;
  public double Rating
  {
  get
  {
  return privateRating;
  }
  set
  {
  privateRating = value;
  NotifyPropertyChanged("Rating");
  }
  }

  private void NotifyPropertyChanged(string propertyName)
  {
  if (PropertyChanged != null)
  PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
  }
  }
}

// Library.cs
public class Library : INotifyPropertyChanged
{
  public event PropertyChangedEventHandler PropertyChanged;

  public Library()
  {
  PrivateName = string.Empty;
  PrivateBooks = new ObservableCollection();

  if (HtmlPage.IsEnabled == false)
  {
  GenerateDummyData();
  }

  }

  public void GenerateDummyData()
  {
  Books = new ObservableCollection();

  ObservableCollection Authors = new ObservableCollection();
  Author Author1 = new Author();
  Author Author2 = new Author();
  Author1.Name = "Jesse Liberty";
  Author2.Name = "Mark Twain";
  Authors.Add(Author1);
  Authors.Add(Author2);

  Book newBook = new Book("Programming Silverlight", Authors, 49.99, "0123456789", "O'Reilly", 1, 1, "2000", 5.0);
  Books.Add(newBook);

  Author2.Name = "Cormac McCarthy";
  newBook = new Book("Programming Without Shoes", Authors, 149.99, "0123456789", "O'Reilly Media", 1, 1, "1955", 4.5);
  Books.Add(newBook);

  Author2.Name = "Ian McEwan";
  newBook = new Book("Programming Without A Spec", Authors, 149.99, "0123456789", "O'Reilly Media", 1, 1, "1955", 4.3);
  Books.Add(newBook);

  Author2.Name = "Stephen King";
  Books.Add(new Book("Programming Without A Conscience", Authors, 149.99, "0123456789", "O'Reilly Media", 1, 1, "1955", 4.1));
  }

  private string PrivateQuery;
  public string Query
  {
  get
  {
  return PrivateQuery;
  }
  set
  {
  PrivateQuery = value;
  if (PropertyChanged != null)
  PropertyChanged(this, new PropertyChangedEventArgs("Query"));
  }
  }

  private string PrivateName;
  public string Name
  {
  get
  {
  return PrivateName;
  }
  set
  {
  PrivateName = value;
  if (PropertyChanged != null)
  PropertyChanged(this, new PropertyChangedEventArgs("Name"));
  }
  }

  private ObservableCollection PrivateBooks;  
  public ObservableCollection Books
  {
  get
  {
  if (PrivateBooks.Count < 1)
  {
  GetData();
  }
  return PrivateBooks;
  }
  set
  {
  PrivateBooks = value;
  if (PropertyChanged != null)
  PropertyChanged(this, new PropertyChangedEventArgs("Books"));
  }
  }

  public void GetData()
  {
  Name = "Liberty Books";
  ObservableCollection Authors = new ObservableCollection();
  Author Author1 = new Author();
  Author Author2 = new Author();
  Author Author3 = new Author();

  Author1.Name = "Jesse Liberty";
  Author2.Name = "Tim Heurer";
  Authors.Add(Author1);
  Authors.Add(Author2);

  Book newBook = new Book("Programming Silverlight", Authors, 49.99, "TBD", "O'Reilly Media", 1, 1, "2009", 5.0);

  PrivateBooks.Add(newBook);

  Authors.Remove(Author2);
  Author2 = new Author();
  Author2.Name = "Alex Horovitz";
  Authors.Add(Author2);

  newBook = new Book("Programming .NET 3.5", Authors, 49.99, "0-596-51039-X", "O'Reilly Media", 1, 2, "2008", 4.7);
  PrivateBooks.Add(newBook);

  Authors.Remove(Author2);
  Author2 = new Author();
  Author2.Name = "Dan Hurwitz";
  Author3.Name = "Brian Macdonald";
  Authors.Add(Author2);
  Authors.Add(Author3);

  newBook = new Book("Learning ASP.NET 3.5", Authors, 44.99, "0-596-51845-5", "O'Reilly Media", 2, 1, "2008", 4.8);
  PrivateBooks.Add(newBook);

  Authors.Remove(Author2);
  Authors.Remove(Author3);
  Author2 = new Author();
  Author2.Name = "Donald Xie";
  Authors.Add(Author2);
  PrivateBooks.Add(new Book("Programming C# 3.0", Authors, 44.99, "0-596-51845-5", "O'Reilly Media", 5, 2, "2008", 4.3));

  Authors.Remove(Author2);
  Author2 = new Author();
  Author2.Name = "Dan Hurwitz";
  Authors.Add(Author2);
  PrivateBooks.Add(new Book("Programming .NET Windows Apps", Authors, 49.95, "0596003218", "O'Reilly Media", 1, 1, "2003", 3.7));

  Authors.Remove(Author2);
  Author2 = new Author();
  Author2.Name = "Brad Jones";
  Authors.Add(Author2);
  PrivateBooks.Add(new Book("Teach Yourself C++ in 1 Hour", Authors, 44.99, "0672327112", "Sams", 6, 1, "2008", 2.9));

  Authors.Remove(Author2);
  Author2 = new Author();
  Author2.Name = "David Horvath";
  Authors.Add(Author2);
  PrivateBooks.Add(new Book("Teach Yourself C++ in 24 Hours", Authors, 34.99, "0672326817", "Sams", 4, 1, "2004", 2.4));
   
  Authors.Remove(Author2);
  PrivateBooks.Add(new Book("Programming VB.NET", Authors, 39.95, "0596004389 ", "O'Reilly Media", 2, 1, "2003", 3.2));
  PrivateBooks.Add(new Book("Visual C# 2005 Dev Notebook", Authors, 29.95, "059600799X", "O'Reilly Media", 1, 1, "2005", 3.7));
  PrivateBooks.Add(new Book("Clouds To Code", Authors, 41.95, "1861000952", "Wrox", 1, 1, "1997", 4.1));

  }
}

Сохраните проект в Visual Studio и до того, как вы вернетесь в Blend давайте распакуем данные классы и полностью в них разберемся.

ObservableCollection и INotifyPropertyChanged

Вы наверняка заметили следующие объявления,

public class Library : INotifyPropertyChanged
{
   //...
   private ObservableCollection PrivateBooks;

public class Book : INotifyPropertyChanged
{
   //....
   private ObservableCollection privateAuthors;

Данный набор более похож на список, за исключением того, что он реализовывает событие INotifyPropertyChanged, запуская данное событие при каждом изменении набора. Это делается без каких-либо усилий и наиболее полезно, потому-то все пользовательские элементы управления (UIControls) имеют в себе встроенный код регистрации для данного события. Если необходимо обновлять элементы управления при изменениях в нижележащем наборе, то вам не надо писать и строки кода - это все делается автоматически, что очень удобно.

Класс Library содержит конструктор, который всего лишь устанавливает в пустое значение и определяет место для пустого просматриваемого набора объектов типа Book.

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

if (HtmlPage.IsEnabled == false)
{
   GenerateDummyData();

За конструктором Library следуют два свойства - Name и Books, где последний является очень умным набором объектов observableCollection. Умный он потому, что когда вы пытаетесь вернуть набор, он проверяет существование объектов Book, и если их нет, то он вызывает вспомогательный метод GetBooks

public ObservableCollection<Book> Books
{
  get
  {
   if (PrivateBooks.Count < 1)
   {
    GetData();
   }
   return PrivateBooks;
  }
   set
   {
     PrivateBooks = value;
     if (PropertyChanged != null)
       PropertyChanged(this, new PropertyChangedEventArgs("Books"));
   }

GetBooks свободно может перейти к веб-сервису и выполнить запрос или считать книги из XML-файла, или же вообще делать все что хочет. Тем не менее, он заполнит набор PrivateBooks выбранными объектами Book. Это удобно, потому что позволяет нам сфокусироваться на привязке данных, а не на получении объектов Book.