Использование DataBinding и DataTemplate при помощи Expression Blend в Silverlight - Реализация приложения и его интерфейса
ОГЛАВЛЕНИЕ
Реализация приложения и его интерфейса
Нашей целью является создание n-уровнего приложения, где у нас будет три основных уровня:
- Уровень пользовательского интерфейса
- Бизнес уровень
- Уровень хранилища данных
Уровень пользовательского интерфейса является той частью, которую видит пользователь и с которой он взаимодействует - мы создадим данный уровень в Expression Blend при помощи элементов управления Silverlight.
Бизнес-уровень управляет логикой вашего приложения - мы создадим его в Visual Studio при помощи C#.
Для данного приложения бизнес-уровень будет инкапсулирован в три относительно простых класса:
- Library представляет собой набор книг, полученных из более объемного набора. Больший набор может быть представлен в виде базы данных, веб-сервиса либо какого нибудь другого механизма хранения.
- Book является неким уровнем представления книги, хотя он может также представлять другие версии печати. Он содержит такие свойства, как автор (author), заголовок (title), издатель (publisher), дату публикации (publication date).
- Author представляет собой то, что нам необходимо знать об авторе книги. Автор также необходим для книги, поэтому он вынесен в отдельный класс.
Нашими целями являются:
- Привязка данных (Data Binding): привязка элементов списка к набору Books в Library
- Шаблон данных (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.