Руководство разработчика Prism — часть 5, реализация паттерна MVVM

в 8:21, , рубрики: .net, microsoft, patterns and practices, Prism, silverlight, wpf, интерфейсы

Оглавление

  1. Введение
  2. Инициализация приложений Prism
  3. Управление зависимостями между компонентами
  4. Разработка модульных приложений
  5. Реализация паттерна MVVM
  6. Продвинутые сценарии MVVM
  7. Создание пользовательского интерфейса
  8. Навигация
  9. Способы коммуникации между слабосвязанными компонентами

Паттерн Model-View-ViewModel (MVVM) поможет вам разделить бизнес-логику и логику представления от его пользовательского интерфейса. Поддержка разделения ответственности между логикой приложения и UI может сделать ваше приложение более лёгким для тестирования, поддержки и развития. Это может также значительно улучшить возможности повторного использования кода и позволит разработчикам и дизайнерам легче сотрудничать при разработке соответствующих частей приложения.

Используя паттерн MVVM, пользовательский интерфейс приложения, логика представления и бизнес-логика разделяются на три отдельных класса: представление, которое инкапсулирует UI и его логику; модель представления, которая инкапсулирует логику представления и её состояния; и модель, которая инкапсулирует бизнес-логику приложения и данные.

Prism включает примеры и образцы реализации, которые показывают, как реализовать шаблон MVVM в Silverlight или в WPF приложениях. Библиотека Prism также предоставляет функции, которые могут помочь реализовать данный паттерн. Эти функции воплощают наиболее распространенные методы для реализации паттерна MVVM и разработаны, чтобы обеспечить тестируемость и совместимость с Expression Blend и Visual Studio.

Эта глава даёт краткий обзор паттерна MVVM и описывает, как его реализовать. Глава 6 описывает, как реализовать более сложные сценарии MVVM, используя библиотеку Prism.

Обязанности и характеристики классов

Паттерн MVVM является близкой разновидностью паттерна Presentation Model, оптимизированного для лучшего согласования с некоторыми базовыми возможностями WPF и Silverlight, такими как привязка данных, шаблоны данных, команды, и поведения.

В паттерне MVVM представление инкапсулирует UI и любую логику UI, модель представления инкапсулирует логику представления и её состояния, и модель инкапсулирует бизнес-логику и данные. Представление взаимодействует с моделью представления посредством привязки данных, команд и событий уведомления. Модель представления запрашивает данные у модели, или подписываться на уведомления об их изменении, и координирует обновления состояния модели, а также преобразует, валидирует и агрегирует при необходимости данные, для отображения их в представлении.

Следующая иллюстрация показывает три части шаблона MVVM и их взаимодействие.

Классы MVVM и их взаимодействие.

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

Класс представления (View)

Представление ответственно за определение того, что пользователь видит на экране. В идеале, code-behind представления содержит только конструктор, который вызывает метод InitializeComponent. В некоторых случаях, code-behind может содержать код логики UI, который реализует визуальное поведение, являющееся трудным или неэффективным для выражения в XAML, такое как сложные анимации, или когда код должен непосредственно управлять визуальными элементами, являющимися частью представления. Недопустимо помещать код логики, нуждающийся в тестировании, в представление. Как правило, код в code-behind представления может быть протестирован через автоматизацию UI.

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

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

Шаблоны данных могут считаться представлениями, у которых отсутствует code-behind. Они разрабатываются для привязки к определенным типам модели представления, когда она должна быть отображена в UI. Во время выполнения, представление, как определено шаблоном, автоматически инстанцируется и соответствующая модель представления устанавливается его контекстом данных.

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

Подводя итоги, у представления есть следующие ключевые характеристики:

  • Представление является визуальным элементом, таким как окно, страница, пользовательский элемент управления или шаблон данных. Представление определяет элементы управления, их компоновку и стиль.
  • Представление ссылается на модель представления через свое свойство DataContext. Элементы управления в представлении привязаны к свойствам и командам модели представления.
  • Представление может настроить поведение привязки данных между представлением и моделью представления. Например, представление может использовать преобразователи значений, чтобы отформатировать данные, которые будут показаны в UI, или использовать правила валидации, чтобы предоставить дополнительную проверку вводимых пользователем данных.
  • Представление задаёт и обрабатывает визуальное поведение UI, такое как анимации или переходы, которые могут быть инициированы изменением состояния модели представления, или через взаимодействие пользователя с UI.
  • Code-behind представления может определить логику UI, чтобы реализовать некоторое визуальное поведение, которое трудно выразить в XAML, или которое требует прямых ссылок на определенные элементы управления UI, определенные в представлении.

Класс модели представления (View Model)

Модель представления в паттерне MVVM инкапсулирует логику представления и данные для отображения. У неё нет прямых ссылок на представление или знаний о реализации или типе представления. Модель представления реализует свойства и команды, к которым представление может привязать данные и уведомляет представление о любых изменениях состояния через события уведомления. Свойства и команды, которые предоставляет модель представления, определяют функциональность, полагающуюся UI, но представление определяет, как эта функциональность будет преподнесена пользователю.

Модель представления ответственна за координирование взаимодействия представления с необходимыми классами модели. Зачастую, между моделью представления и классами модели бывают отношение один-ко-многим. Модель представления может подставлять классы модели непосредственно в представления так, чтобы элементы управления в представлении могли привязать данных непосредственно к ним. В этом случае, классы модели должны быть разработаны так, чтобы поддерживать привязку данных и соответствующие события уведомления об изменениях. Для получения дополнительной информации об этом сценарии, смотрите раздел «Привязка данных» далее в этой теме.

Модель представления может преобразовывать или управлять данными модели так, чтобы представление могло легко их использовать. Модель представления может иметь дополнительные свойства для специфичных нужд представления. Эти свойства обычно не могут быть частью модели. Например, модель представления может объединить значение двух полей, чтобы облегчить их вывод на экран, или может вычислить количество оставшихся символов при вводе для полей с ограниченной длиной строки. Модель представления может также реализовать логику валидации данных, чтобы гарантировать их непротиворечивость.

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

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

В итоге, модель представления имеет следующие ключевые характеристики:

  • Является невизуальным классом и не наследуется ни от какого базового класса WPF или Silverlight. Она инкапсулирует логику представления, необходимую для поддержки пользовательских действий в приложении. Модель представления тестируется независимо от представления и модели.
  • Обычно непосредственно не ссылается на представление. Она реализует свойства и команды, к которыми представление может привязать данные. Она уведомляет представление о любых изменениях состояния через события уведомления интерфейсов INotifyPropertyChanged и INotifyCollectionChanged.
  • Координирует взаимодействие представления с моделью. Она может преобразовать или управлять данными так, чтобы их можно было легко использовать представлению, и может реализовать дополнительные свойства, которые, возможно, не присутствуют в модели. Она может также реализовать валидацию данных через интерфейсы IDataErrorInfo или INotifyDataErrorInfo.
  • Может определить логические состояния, которые представление может визуально представить пользователю.

Заметка
Представление или модель представления?
Часто определение того, где должна быть реализована определенная функциональность, не очевидно. Общее правило гласит: Что-либо касающееся определенного визуального отображения и что может быть модернизировано позже (даже если вы в настоящий момент не планируете модернизировать это), должно войти в представление; что-либо, что важно для логики приложения, должно войти в модель представления. Кроме того, так как у модели представления не должно быть никаких явно заданных знаний об определенных визуальных элементах в представлении, код, чтобы программно управлять визуальными элементами в пределах представления, должен находиться в code-behind представления или инкапсулироваться в поведениях (Behaviors). Точно так же, код для получения или управления элементами данных, которые должны быть показаны в представлении посредством привязки данных, должен находиться в модели представления. Например, цвет выделения выбранного пункта в поле списка должен быть определен в представлении, но список элементов для отображения и ссылка на выбранный пункт, должны быть определены в модели представления.

Класс модели (Model)

Модель в паттерне MVVM инкапсулирует бизнес-логику и данные. Бизнес-логика определяется как любая логика приложения, которая касается извлечения и управления данными приложения и для того, чтобы удостовериться, что удовлетворяются бизнес-правила, гарантирующие непротиворечивость и валидность данных. Чтобы максимизировать возможности повторного использования, модели не должны содержать специфичной для представления информации, логики, или поведения.

Как правило, модель представляет сущность предметной области клиентской части приложения. Она может содержать структуры данных, основанные на модели данных приложения и любую бизнес и валидационную логику. Модель может также иметь доступ к данным и кэшу, хотя обычно для этого используется отдельный репозиторий данных, или соответствующая служба. Часто, модель и слой доступа к данным являются автоматически сгенерированными, как часть инфраструктуры ADO.NET Entity Framework, WCF Data Services, или WCF RIA Services.

Как правило, модель реализует средства, облегчающие привязку к представлению. Это обычно означает, что поддерживаются уведомления об изменениях свойств или коллекций через интерфейсы INotifyPropertyChanged и INotifyCollectionChanged. Классы моделей, которые предоставляют наборы объектов, обычно наследуются от класса ObservableCollection<T>, который предоставляет реализацию интерфейса INotifyCollectionChanged. Модель может также поддерживать валидацию данных и сообщение об ошибках через интерфейсы IDataErrorInfo (или INotifyDataErrorInfo). Эти интерфейсы позволяют посылать WPF и Silverlight уведомления для обновления UI, когда значения привязанных данных изменяются. Они также предоставляют поддержку валидации данных и сообщения об ошибках в уровне UI.

Заметка
Что, если Ваши классы модели не реализуют необходимые интерфейсы?
Иногда Вы должны будете работать с объектами модели, которые не реализуют интерфейсы INotifyPropertyChanged, INotifyCollectionChanged, IDataErrorInfo, или INotifyDataErrorInfo. В этих случаях модель представления, возможно, должна будет обернуть объекты модели и предоставить необходимые свойства представлению. Значения для этих свойств будут предоставлены непосредственно объектами модели. Модель представления сможет реализовать необходимые интерфейсы для свойств, которые она предоставляет так, чтобы представление могло легко привязать к ним данные.

У модели есть следующие ключевые характеристики:

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

Взаимодействие классов

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

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

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

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

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

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

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

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

Следующие разделы описывают, как реализовать необходимые интерфейсы для поддержки привязки данных в пределах паттерна MVVM.

Реализация INotifyPropertyChanged

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

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"));
                }
            }
        }
    }
}

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

public class NotificationObject : INotifyPropertyChanged {
   public event PropertyChangedEventHandler PropertyChanged;
   ...
   protected void RaisePropertyChanged<T>(
                          Expression<Func<T>> propertyExpression) {...}

   protected virtual void RaisePropertyChanged(string propertyName) {...}
}

Унаследованный класс модели представления может сгенерировать событие изменения свойства как вызовом RaisePropertyChanged с определенным именем свойства, так и использованием лямбда-выражения, которое обращается к свойству, как показано ниже.

public string CurrentState {
    get { return this.currentState; }
    set {
        if (this.currentState != value) {
            this.currentState = value;
            this.RaisePropertyChanged(() => this.CurrentState);
        }
    }
}

Заметка
Использование лямбда-выражений таким образом вызывает небольшую потерю производительности, потому что лямбда-выражение должно быть вычислено для каждого запроса. Преимуществом является то, что данный подход обеспечивает безопасность типов во время компиляции и поддержку рефакторинга, когда вы переименовываете свойство. Хотя потеря производительности является небольшой и обычно не влияет на ваше приложение, затраты могут накапливаться, если у вас имеется много уведомлений об изменении. В этом случае, следует рассмотреть использование перегрузки метода, принимающей строку.

Заметка
Свежии версии Resharper поддерживают рефакторинги даже с использованием перегрузки, принимающей имя свойства в виде строки.

Часто ваша модель или модель представления будут содержать свойства, значения которых вычисляются в зависимости от других свойств модели или модели представления. Генерируя событие изменения свойства, убедитесь, что также генерируются события уведомлений для любых зависимых свойств. К примеру, если у вас есть поля Price1, Price2 и SumPrice, представляющее сумму первых двух полей, то в сеттерах полей Price1 и Price2 должен помимо всего вызываться метод RaisePropertyChanged(“SumPrice”).

Реализация 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 будет автоматически создаваться CollectionView по умолчанию всякий раз, когда элемент управления привязывается к коллекции. В Silverlight оно будет автоматически создаваться, только если привязанная коллекция поддерживает интерфейс ICollectionViewFactory.

Классы представления коллекции могут использоваться моделью представления, чтобы отследить важную информацию о состоянии для базовой коллекции, поддерживая разделение обязанностей между UI в представлении и базовыми данными в модели. В действительности, CollectionViews являются моделями представления, которые специально разработаны, чтобы поддерживать коллекции.

Поэтому, если вы должны реализовать фильтрацию, сортировку, группировку, или отслеживание выбора элементов в коллекции изнутри вашей модели представления, то ваша модель представления должна создать экземпляр класса представления коллекции для каждой коллекции, которая будет предоставлена представлению. Тогда можно будет подписаться на события выбора, такие как CurrentChanged, или управлять фильтрацией, сортировкой, или группировкой, используя методы класса представления коллекции внутри вашей модели представления.

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

Следующий пример показывает использование PagedCollectionView в Silverlight для отслеживания клиента, выбранного в настоящий момент.

public class MyViewModel : INotifyPropertyChanged {
    public ICollectionView Customers { get; private set; }

    public MyViewModel(ObservableCollection<Customer> customers) {
        Customers = new PagedCollectionView(customers);
        Customers.CurrentChanged +=
                           new EventHandler(SelectedItemChanged);
    }

    private void SelectedItemChanged(object sender, EventArgs e) {
        Customer current = Customers.CurrentItem as Customer;
        ...
    }
}

В представлении можно связать ItemsControl, такой как ListBox, со свойством Customers в модели представления через свойство ItemsSource, как показано ниже.

<ListBox ItemsSource="{Binding Path=Customers}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <TextBlock Text="{Binding Path=Name}"/>
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

Когда пользователь выберет клиента в UI, модель представления будет проинформирована и сможет выполнять команды, касающиеся только выбранного клиента. Модель представления может также программно изменить текущий выбор в UI, вызывая методы на объекте представления коллекции, как показано в следующем примере.

Customers.MoveCurrentToNext();

Когда в представлении коллекции изменяется выбранный элемент, UI автоматически обновляется, чтобы визуально обозначить выбранное состояние элемента. Реализация подобна и для WPF, хотя PagedCollectionView в предыдущем примере обычно будет заменён классом ListCollectionView, или BindingListCollectionView, как показано ниже.

Customers = new ListCollectionView(_model);
Customers.CurrentChanged += new EventHandler(SelectedItemChanged);

Команды

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

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

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

Заметка
Поведения являются мощным и гибким механизмом расширяемости, который может использоваться для инкапсуляции логики взаимодействия и поведения, которое может быть декларативно связано с элементами управления в представлении. CommandBehavior могут использоваться для связи объектов или методов команды с элементами управления, которые не были специально разработаны для взаимодействия с командами.

Следующие разделы описывают, как реализовать команды в вашем представлении как методы или объекты команды, и как связать их с элементами управления в представлении.

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

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

Реализация интерфейса ICommand сравнительно проста. Однако есть много реализаций этого интерфейса, который можно с готовностью использовать в своем приложении. Например, можно использовать класс ActionCommand из SDK Expression Blend, или класс DelegateCommand, предоставленный Prism.

Класс DelegateCommand инкапсулирует два делегата, каждый из которых ссылается на метод, реализованный в пределах вашего класса модели представления. Он наследуется от класса DelegateCommandBase, который реализует методы Execute и CanExecute интерфейса ICommand, вызывая эти делегаты. Вы определяете делегатов в своих методах модели представления в конструкторе класса DelegateCommand, который выглядит следующим образом.

public class DelegateCommand<T> : DelegateCommandBase {
    public DelegateCommand(Action<T> executeMethod,Func<T,bool> canExecuteMethod)
		:base((o) => executeMethod((T)o), (o) => canExecuteMethod((T)o)) {
        ...
    }
}

Например, следующий код показывает, как экземпляр DelegateCommand, который представляет команду Submit, создается, создавая делегатов на методы модели представления OnSubmit и CanSubmit. Команда передаётся представлению через свойство только для чтения, которое возвращает ссылку на ICommand.

public class QuestionnaireViewModel {
    public QuestionnaireViewModel() {
       this.SubmitCommand = new DelegateCommand<object>(
                                        this.OnSubmit, this.CanSubmit);
    }

    public ICommand SubmitCommand { get; private set; }

    private void OnSubmit(object arg)   {...}
    private bool CanSubmit(object arg)  { return true; }
}

Когда метод Execute вызывают на объекте DelegateCommand, вызов просто перенаправляется в метод в вашем классе модели представления через делегат, который вы определили в конструкторе. Точно так же, когда вызывают метод CanExecute, вызывается соответствующий метод в вашем классе модели представления. Делегат на метод CanExecute в конструкторе является не обязательным. Если делегат не будет определен, то DelegateCommand будет всегда возвращать true для CanExecute.

Класс DelegateCommand является универсальным типом. Параметр типа определяет тип параметра команды, передаваемый методам Execute и CanExecute. В предыдущем примере параметр команды имеет тип object. Не универсальная версия класса DelegateCommand также предоставляется Prism для использования в том случае, когда параметр команды не требуется.

Модель представления может указать на изменение состояния метода CanExecute команды, вызывая метод RaiseCanExecuteChanged на объекте DelegateCommand. Это вызовет событие CanExecuteChanged. Любые элементы управления в UI, которые связаны с командой, обновят свое состояние, чтобы отразить доступность связанной команды.

Доступны другие реализации интерфейса ICommand. Класс ActionCommand в SDK Expression Blend, подобен классу Prism DelegateCommand, описанному ранее, но поддерживает только единственный делегат метода Execute. Prism также предоставляет класс CompositeCommand, который позволяет группировать DelegateCommands для исполнения. Для получения дополнительной информации об использовании класса CompositeCommand, см. «Составные команды» в главе 6, «Продвинутые сценарии MVVM».

Вызов объектов команд из представления

Есть много способов, которыми элементы управления в представлении могут быть связаны с объектом команды, предоставленным моделью представления. Элементы управления WPF и Silverlight 4, особенно наследуемые от ButtonBase, такие как Button, RadioButton, Hyperlink, или наследуемые от MenuItem, могут быть легко связаны с объектом команды через свойство Command. WPF также поддерживает привязку ICommand к KeyGesture.

<Button Command="{Binding Path=SubmitCommand}" CommandParameter="SubmitOrder"/>

Дополнительно может быть задан параметр команды, используя свойство CommandParameter. Тип ожидаемого параметра определяется в целевых методах Execute и CanExecute. Элемент управления автоматически вызовет целевую команду, когда пользователь будет взаимодействовать с этим элементом управления, и параметр команды, если он есть, будет передан как параметр методу Execute команды. В предыдущем примере кнопка автоматически вызовет SubmitCommand, когда по ней щелкнут. Дополнительно, если будет определен обработчик CanExecute, то кнопка будет автоматически отключена, если CanExecute возвратит false и будет включена, если true.

Альтернативный подход должен использовать триггеры взаимодействия Expression Blend и поведение InvokeCommandAction.

<Button Content="Submit" IsEnabled="{Binding CanSubmit}">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Click">
            <i:InvokeCommandAction Command="{Binding SubmitCommand}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</Button>

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

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

Заметка
Поддерживающие команды элементы управления WPF и Silverlight 4, позволяют вам декларативно привязывать элемент управления к команде. Эти элементы управления вызовут указанную команду, когда пользователь будет взаимодействовать с ними. Например, для элемента управления Button, команда будет вызвана, когда пользователь нажмет кнопку. Это событие, связанное с командой, фиксировано и не может быть изменено.

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

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

Если вы должны использовать только поддерживающие команды элементы управления, и если вы довольны стандартными событиями вызова команды, поведения могут даже не потребоваться. Точно так же, если ваши разработчики или разработчики UI не будут использовать Expression Blend, можно выбирать только поддерживающие команды элементы управления (или пользовательские присоединенные поведения) из-за дополнительного синтаксиса, требуемого для использования поведений Expression Blend.

Вызов методов команд из представления

Альтернативным подходом к реализации команд как объектов ICommand, является реализация их просто как методов в модели представления и затем использование поведения, чтобы вызвать эти методы непосредственно из представления.

Это может быть достигнуто похожим на вызов команд из поведения способом, как было показано в предыдущем разделе. Однако, вместо того, чтобы использовать InvokeCommandAction, используется CallMethodAction. Следующий пример вызывает метод (без параметров) Submit на базовой модели представления.

<Button Content="Submit" IsEnabled="{Binding CanSubmit}">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Click">
            <i:CallMethodAction TargetObject="{Binding}" Method="Submit"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</Button>

TargetObject связывается с базовым контекстом данных (который является моделью представления) при использовании выражения {Binding}. Параметр Method определяет метод для вызова.

Заметка
CallMethodAction не поддерживает параметры. Если вы должны передать параметры к целевому методу, необходимо передать значение свойству модели представления, переключиться на использование команды с InvokeCommandAction, или написать собственную версию CallMethodAction, который принимает параметры.

Валидация данных и сообщения об ошибках

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

Silverlight и WPF предоставляют поддержку для управления ошибками валидации данных, которые происходят когда изменяются отдельные свойства, связанные с элементами управления в представлении. Для отдельных свойств, которые связываются с элементом управления, модель представления или модель могут сигнализировать об ошибке валидации данных в пределах метода set свойства, отклоняя входящее плохое значение и вбрасывая исключение. Если свойство ValidatesOnExceptions на объекте привязки данных будет равно true, то механизм привязки данных в WPF и Silverlight обработает исключение и покажет пользователю визуальный индикатор о наличии ошибки валидации.

Однако, выдачу исключений свойствами таким образом, нужно избегать где только возможно. Альтернативным подходом является реализация интерфейса IDataErrorInfo или INotifyDataErrorInfo в классах модели представления или модели. Эти интерфейсы позволяют модели представления или модели выполнять валидацию для значений одного или более свойств и возвращать сообщение об ошибке представлению так, чтобы пользователь мог быть уведомлен о ней.

Реализация IDataErrorInfo

Интерфейс IDataErrorInfo предоставляет базовую поддержку валидации данных и сообщения об ошибках. Он определяет два свойства только для чтения: свойство индексатора, с названием свойства в качестве параметр индексатора, и свойство Error. Оба свойства возвращают строковое значение.

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

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

Когда привязывая элементы управления в представлении к свойствам, вы хотите проверить их через интерфейс IDataErrorInfo, установите свойство ValidatesOnDataErrors в привязке данных в true. Это гарантирует, что механизм привязки данных запросит информацию об ошибке из связанного свойства.

<TextBox Text="{Binding Path=CurrentEmployee.Name, Mode=TwoWay, ValidatesOnDataErrors=True, NotifyOnValidationError=True }"/>

Реализация INotifyDataErrorInfo

Интерфейс INotifyDataErrorInfo более гибок, чем интерфейс IDataErrorInfo. Он поддерживает множественные ошибки для свойства, асинхронное подтверждение правильности данных, и возможность уведомить представление об изменении состояния ошибки для объекта. Однако, INotifyDataErrorInfo в настоящий момент поддерживается только в Silverlight 4 и не доступен в WPF 4.

Заметка
Данный интерфейс начал поддерживаться и в WPF в .NET Framework 4.5.

Интерфейс INotifyDataErrorInfo определяет свойство HasErrors, которое позволяет модели представления указывать, существует ли ошибка (или несколько ошибок) для каких-либо свойств, и метод GetErrors, который позволяет модели представления возвратить список сообщений об ошибках для определенного свойства.

Интерфейс INotifyDataErrorInfo также определяет событие ErrorsChanged. Оно поддерживает асинхронные сценарии проверки допустимости, позволяя модели представления или представлению сигнализировать об изменении состояния ошибки для определенного свойства через событие ErrorsChanged. Значение свойства может быть изменено разными способами, не только через привязку данных, а, например, в результате запроса веб-сервиса или фонового вычисления. Событие ErrorsChanged позволяет модели представления сообщать представлению об ошибке, как только ошибка валидации данных была идентифицирована.

Для поддержки INotifyDataErrorInfo, вы должны будете хранить список ошибок для каждого свойства. Model-View-ViewModel Reference Implementation (MVVM RI) демонстрирует один способ сделать это с использованием класса коллекции ErrorsContainer, который отслеживает все ошибки валидации в объекте. Она также генерирует события уведомления, если список ошибок изменяется. Следующий пример показывает DomainObject (корневой объект модели) и реализацию INotifyDataErrorInfo, используя класс ErrorsContainer.

public abstract class DomainObject : INotifyPropertyChanged, INotifyDataErrorInfo {
    private ErrorsContainer<ValidationResult> errorsContainer =
                    new ErrorsContainer<ValidationResult>(
                       pn => this.RaiseErrorsChanged(pn));

    public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

    public bool HasErrors {
        get { return this.ErrorsContainer.HasErrors; }
    }

    public IEnumerable GetErrors(string propertyName) {
        return this.errorsContainer.GetErrors(propertyName);
    }

    protected void RaiseErrorsChanged(string propertyName) {
        var handler = this.ErrorsChanged;
        if (handler != null) {
            handler(this, new DataErrorsChangedEventArgs(propertyName));
        }
    }
   ...
}

В Silverlight любые данные элементов управления, связанные со свойствами модели представления, автоматически подпишутся на событие INotifyDataErrorInfo и покажут информацию об ошибке в элементе управления, если свойство будет содержать ошибку.

Создание и агрегация

Паттерн MVVM помогает вам отделить UI от представления, бизнес-логики и данных, поэтому размещение правильного кода в правильном классе является важным первым шагом в эффективном использовании данного паттерна. Управление взаимодействием между классами модели представления и представления посредством привязки данных и команд является также важным аспектом для рассмотрения. Следующий раздел покажет, как представление, модель представления, и классы модели создаются и взаимодействуют друг с другом во время выполнения.

Заметка
Выбор правильной стратегии в этом шаге особенно важен, если вы используете контейнер внедрения зависимостей в своем приложении. Managed Extensibility Framework (MEF) и Unity Application Block (Unity) и обеспечивают возможность определять зависимости между представлением, моделью представления, и классами модели и выполнить их контейнером. Для более усовершенствованных сценариев, см. главу 6, «Продвинутые сценарии MVVM».

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

Забота должна также быть проявлена при создании и соединении модели представления и представления, чтобы гарантировать сохранение слабой связанности. Как отмечено в предыдущем разделе, модель представления не должна зависеть ни от какой определенной реализации представления. Точно так же, представление не должно зависеть ни от какой определенной реализации модели представления.

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

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

Создание модели представления через XAML

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

Подход на основе XAML демонстрируется в файле QuestionnaireView.xaml в Basic MVVM QuickStart. В том примере экземпляр QuestionnaireViewModel определяется в XAML QuestionnaireView, как показано ниже.

<UserControl.DataContext>
    <my:QuestionnaireViewModel/>
</UserControl.DataContext>

Когда QuestionnaireView будет создан, экземпляр QuestionnaireViewModel автоматически создастся и установится как контекст данных представления. Этот подход требует, чтобы у вашей модели представления был конструктор по умолчанию (без параметров).

У декларативного создания и присвоения модели представления представлением есть преимущество в том, что он прост и хорошо работает в инструментах времени проектирования, таких как Microsoft Expression Blend или Microsoft Visual Studio. Недостаток этого подхода в том, что представление знает о соответствующей модели представления.

Создание модели представления программно

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

public QuestionnaireView() {
    InitializeComponent();
    this.DataContext = new QuestionnaireViewModel();
}

У программного создания и присвоения модели представления в пределах code-behind представления есть преимущество в том, что этот способ прост и хорошо работает в инструментах времени проектирования, таких как Expression Blend или Visual Studio. Недостаток этого подхода в том, что представление должно знать о соответствующем типе модели представления, и что он требует кода в code-behind представления. Используя контейнер внедрения зависимости, такой как Unity или MEF, можно обеспечить слабую связь между моделью представления и представлением. Для получения дополнительной информации, см. главу 3, «Управление зависимостями между компонентами.»

Создание представления, определенного как шаблон данных

Представление может быть определено, как шаблон данных и связано с типом модели представления. Шаблоны данных могут быть определены как ресурсы, или как встроенные, в пределах элемента управления, который отображает модель представления. «Контент» элемента управления является экземпляром модели представления, и шаблон данных используется для её визуального представления. WPF и Silverlight автоматически создаёт шаблон данных и установят его контекст данных в экземпляр модели представления во время выполнения. Этот метод является примером ситуации, в которой вначале создаётся модель представления, а уже потом — представление.

Шаблоны данных гибки и легковесны. Разработчик UI может использовать их, чтобы легко определить визуальное представление модели представления, без какого-либо сложного кода. Шаблоны данных ограничиваются представлениям, которые не требуют никакой логики UI (code-behind). Для визуальной разработки и редактирования шаблонов данных, может использоваться Microsoft Expression Blend.

Следующий пример показывает ItemsControl, который связан со списком клиентов. Каждый объект потребителя в базовой коллекции является экземпляром модели представления. Представление для клиента определяется встроенным шаблоном данных. В следующем примере представление для каждой потребительской модели представления состоит из StackPanel с меткой и текстовым полем, связанным со свойством Name модели представления.

<ItemsControl ItemsSource="{Binding Customers}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <TextBlock VerticalAlignment="Center" Text="Customer Name: " />
                <TextBox Text="{Binding Name}" />
            </StackPanel>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

Можно также задать шаблон данных как ресурс. Следующий пример показывает шаблон данных, определённый как ресурс и применённый к элементу управления через расширение разметки StaticResource.

<UserControl ...>
    <UserControl.Resources>
        <DataTemplate x:Key="CustomerViewTemplate">
            <local:CustomerContactView />
        </DataTemplate>
    </UserControl.Resources>

    <Grid>
        <ContentControl Content="{Binding Customer}"
                        ContentTemplate="{StaticResource CustomerViewTemplate}" />
    </Grid>
</Window>

Здесь, шаблон данных обертывает конкретный тип представления, что позволяет представлению определять поведение code-behind. Таким образом, шаблонный механизм данных может использоваться для того, чтобы поверхностно предоставить связи между представлением и моделью представления. Хотя предыдущий пример показывает шаблон в ресурсах UserControl, его часто помещают в ресурсы приложения или в ResourceDictionary для повторного использования. Можно увидеть, что пример использования шаблонов данных создаёт представления и связывает их с их моделями представления в файле QuickStart MVVM QuestionnaireView.xaml.

Ключевые решения

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

  • Выберите подход для создания представлений и моделей представления, который вы будете использовать. Вы должны решить, создаёт ли ваше приложение сначала представления или модели представления, и использовать ли контейнер внедрения зависимости, такой как Unity или MEF. Обычно желательно, чтобы это было непротиворечиво для всего приложения. Для получения дополнительной информации, см. раздел, «Advanced Construction and Wire-Up» в этой главе и раздел «Advanced MVVM Scenarios» в главе 6, «Advanced MVVM Scenarios.»
  • Решите, будете ли вы представлять команды как методы команд или объекты команд. Методы команд просто реализовать и они могут быть вызваны через поведения в представлении. Объекты команды могут аккуратно инкапсулировать команду и логику включения/отключения и могут быть вызваны через поведение или через свойство Command в ButtonBase элементах управления. Чтобы облегчить работу ваших разработчиков и дизайнеров, будет хорошей идеей сделать это выбором для всего приложения. Для получения дополнительной информации, смотрите раздел «Команды» в этой главе.
  • Решите, как ваши модели представления и модели будут сообщать об ошибках представлению. Ваши модели могут или поддерживать интерфейс IDataErrorInfo, или INotifyDataErrorInfo. Возможно, что не все модели должны будут сообщать информацию об ошибках, но для тех, которые это делают, предпочтительно иметь непротиворечивый подход для ваших разработчиков. Для получения дополнительной информации, смотрите раздел «Валидация и сообщения об ошибках» в этой главе.
  • Решите, важна ли поддержка информации времени проектирования Microsoft Expression Blend для вашей команды. Если вы будете использовать Expression Blend для разработки UI и хотите видеть данные во время проектирования, удостоверьтесь, что ваши представления и модели представления имеют конструкторы без параметров, и что ваши представления устанавливают контекст данных во время проектирования. Альтернативно, рассмотрите использование функций времени проектирования, предоставляемых Microsoft Expression Blend, использующих атрибуты времени проектирования, такие как d:DataContext и d:DesignSource. Для получения дополнительной информации, смотрите «Guidelines for Creating Designer Friendly Views» в главе 7 «Composing the User Interface».

Дополнительная информация

Для получения дополнительной информации о привязке данных в WPF, смотрите «Data Binding» на MSDN: http://msdn.microsoft.com/en-us/library/ms750612.aspx.

Про привязку данных в Silverlight, осмотрите «Data Binding» на MSDN: http://msdn.microsoft.com/en-us/library/cc278072(VS.95).aspx.

Для получения дополнительной информации о привязке к коллекциям в WPF, смотрите «Binding to Collections» в «Data Binding Overview» на MSDN: http://msdn.microsoft.com/en-us/library/ms752347.aspx.

Для получения дополнительной информации о привязке к коллекциям в Silverlight, смотрите «Binding to Collections» в «Data Binding» на MSDN: http://msdn.microsoft.com/en-us/library/cc278072(VS.95).aspx.

Для получения дополнительной информации о паттерне Presentation Model, смотрите «Presentation Model» на сайте Мартина Фаулера: http://www.martinfowler.com/eaaDev/PresentationModel.html

Для получения дополнительной информации о шаблонах данных, смотрите «Data Templating Overview» на MSDN: http://msdn.microsoft.com/en-us/library/ms742521.aspx

Для получения дополнительной информации о MEF, смотрите «Managed Extensibility Framework Overview» на MSDN: http://msdn.microsoft.com/en-us/library/dd460648.aspx

Для получения дополнительной информации о Unity, смотрите «Unity Application Block» на MSDN: http://www.msdn.com/unity

Для получения дополнительной информации о DelegateCommand и CompositeCommand, смотрите часть 9, "Communicating Between Loosely Coupled Components."

Автор: Unrul

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js