Опубликован: 13.12.2011 | Доступ: свободный | Студентов: 1021 / 34 | Оценка: 4.29 / 4.57 | Длительность: 13:56:00
Лекция 9:

Применение паттерна MVVM как оптимального при проектировании WPF и Silverlight приложений

Привязка данных

Привязка данных играет очень важную роль в паттерне MVVM. WPF и Silverlight предоставляют мощные возможности для привязки данных. Ваша модель представления и (в идеале) ваши классы модели должны быть разработаны так, чтобы поддерживать привязку данных. Как правило, это означает, что они должны реализовать корректные интерфейсы.

Привязка данных Silverlight и WPF поддерживает разнообразные режимы. С односторонней привязкой данных, элементы управления UI могут быть связаны с моделью представления так, чтобы они отражали значения базовых данных при отрисовке. Двухсторонняя привязка данных автоматически обновит базовые данные, когда пользователь изменит их в UI.

Чтобы гарантировать, что UI обновляется, когда данные изменяются в модели представления, она должна реализовать соответствующий интерфейс уведомления об изменениях. Если она определяют свойства, к которым могут быть привязаны данные, то она должна реализовать интерфейс INotifyPropertyChanged. Если модель представления представляет коллекцию, она должна реализовать INotifyCollectionChanged или наследоваться от класса ObservableCollection <T>, который реализует этот интерфейс. Оба этих интерфейса определяют событие, которое генерируется всякий раз, когда базовые данные изменяются. Любые связанные элементы управления будут автоматически обновлены, когда эти события будут сгенерированы.

Во многих случаях, модель представления содержит свойства, которые возвращают объекты (которые, в свою очередь, могут содержать свойства, которые возвращают дополнительные объекты). Привязка данных WPF и Silverlight поддерживает привязку к вложенным свойствам через свойство Path.

Реализация INotifyPropertyChanged

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

public class Questionnaire : INotifyPropertyChanged
    private string favoriteColor;
    public event PropertyChangedEventHandler PropertyChanged;
    ...
    public string FavoriteColor
    {
        get { return this.favoriteColor; }
        set
        {
            if (value != this.favoriteColor)
            {
                this.favoriteColor = value;
                if (this.PropertyChanged != null)
                {
                    this.PropertyChanged(this,
                          new PropertyChangedEventArgs("FavoriteColor"));
                }
            }
        }
    }
}

Реализация INotifyCollectionChanged

Ваш класс модели представления или модели может представлять коллекцию элементов, или может иметь одно или более свойств, возвращающих коллекцию. В другом случае вы, вероятно, будете хотеть отобразить коллекцию в ItemsControl, таком как ListBox, или в DataGrid в представлении. Эти элементы управления могут быть связанными с моделью представления, которая предоставляет коллекцию или свойство, возвращающее коллекцию через свойство ItemSource.

<DataGrid
 ItemsSource="{Binding Path=LineItems}
  />

Чтобы должным образом поддерживать запросы уведомления об изменении, классы модели представления или модели, если его предоставляет коллекция, должны реализовать интерфейс INotifyCollectionChanged (в дополнение к интерфейсу INotifyPropertyChanged). Если класс модели представления или модели определяют свойство, которое возвращает ссылку на коллекцию, возвращаемый класс коллекции должен реализовывать интерфейс INotifyCollectionChanged.

Однако, реализация интерфейса INotifyCollectionChanged может быть сложной, потому что она должна посылать уведомления, когда элементы добавляются, удаляются, или изменяются в пределах коллекции. Вместо того, чтобы непосредственно реализовать интерфейс, часто легче использовать или наследоваться от класса коллекции, который уже реализует его. Класс ObservableCollection<T> обеспечивает реализацию этого интерфейса и обычно используется или в качестве базового класса, или в свойствах, представляющих коллекцию элементов.

Если вы должны создать коллекцию для привязки данных к представлению, и вам не нужно отслеживать выбор пользователя или поддерживать фильтрацию, сортировку, или группировку элементов в коллекции, можно просто определить свойство в своей модели представления, которое возвращает ссылку на экземпляр ObservableCollection<T>.

public class OrderViewModel: INotifyPropertyChanged
{
    public OrderViewModel( IOrderService orderService )
    {
        this.LineItems = new ObservableCollection<OrderLineItem>(
                               orderService.GetLineItemList() );
    }
 
    public ObservableCollection<OrderLineItem> LineItems { get; private set;}
}

Если вы получаете ссылку на класс коллекции (например, от другого компонента или службы, которая не реализует INotifyCollectionChanged), можно обернуть эту коллекцию в экземпляр ObservableCollection<T>, используя один из конструкторов, которые принимают в качестве параметра IEnumerable <T> или List<T>.

Реализация ICollectionView

Предыдущий пример кода показывает, как реализовать простое свойство модели представления, которое возвращает коллекцию элементов, которые могут быть показаны через связанные элементы управления в представлении. Поскольку ObservableCollection<T> класс реализует интерфейс INotifyCollectionChanged, элементы управления в представлении будут автоматически обновлены при добавлении или удалении элементов.

Часто вы будете должны более точно управлять тем, как коллекция элементов отображается в представлении, или отслеживать взаимодействие пользователя с отображаемой коллекцией элементов непосредственно внутри модели представления. Например, вы, возможно, должны позволить коллекции элементов фильтроваться или сортироваться согласно логике представления, реализованной в модели представления, или вы, возможно, должны отследить выбранный в настоящий момент пункт в представлении так, чтобы команды, реализованные в модели представления, могли действовать на выбранный элемент.

WPF и Silverlight поддерживают такие сценарии, предоставляя различные классы, которые реализуют интерфейс ICollectionView. Этот интерфейс обеспечивает свойства и методы, чтобы позволить коллекции быть отфильтрованной, отсортированной, или сгруппированной, и позволяет отследить или изменить элемент, выбранный в настоящий момент. И Silverlight, и WPF предоставляют реализации этого интерфейса, Silverlight даёт класс PagedCollectionView и WPF – класс ListCollectionView.

Классы представления коллекции работают, обертывая базовую коллекцию элементов так, чтобы они могли обеспечить автоматическое отслеживание выбранного элемента, сортировку, фильтрацию и оповещение. Экземпляр этих классов может быть создан программно или декларативно в XAML, используя класс CollectionViewSource.

Команды

В дополнение к предоставлению доступа к данным, которые будут отображены или отредактированы в представлении, модель представления, вероятно, определит одно или более действий или операции, которые могут быть выполнены пользователем. В WPF и Silverlight действия или операции, которые пользователь может выполнить через UI, обычно определяются как команды. Команды обеспечивают удобный способ представить действия или операции, которые могут быть легко связаны со средствами управления в UI. Они инкапсулируют код, реализующий необходимые действия, и помогают сохранить его отделённым от визуального представления.

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

Модель представления может реализовать команды как Command Method, или как Command Object (объект, который реализует интерфейс ICommand). В любом случае, взаимодействие представления с командой может быть определено декларативно, не требуя сложного кода обработки событий в code-behind представления. Например, определенные элементы управления в WPF и Silverlight поддерживают команды и предоставляют свойство Command, которое может быть связано с объектом ICommand модели представления. В других случаях, поведение команды может использоваться для связи элемента управления с методом команды или объектом команды модели представления.

Реализация Command Objects

Объект команды является объектом, который реализует интерфейс ICommand. Этот интерфейс определяет метод Execute, который инкапсулирует необходимую работу, и метод CanExecute, который указывает, может ли команда быть вызвана в определённое время. Оба этих метода принимают единственный аргумент в качестве параметра для команды. Инкапсуляция логики реализации необходимой работы в объекте команды означает, что она может легче тестироваться и поддерживаться.

Реализация интерфейса ICommand является понятной, более подробно она рассматривалась в предыдущих лекциях.