Шаблон Model-View-ViewModel

Примечание.

Эта электронная книга была опубликована весной 2017 года и с тех пор не была обновлена. Есть много в книге, которая остается ценным, но некоторые из материалов устарели.

Интерфейс Xamarin.Forms разработчика обычно включает в себя создание пользовательского интерфейса в XAML, а затем добавление кода позади, которое работает в пользовательском интерфейсе. По мере изменения приложений и увеличения размера и область могут возникнуть сложные проблемы с обслуживанием. Эти проблемы включают тесное взаимодействие между элементами управления пользовательским интерфейсом и бизнес-логикой, что повышает затраты на изменение пользовательского интерфейса и трудности модульного тестирования такого кода.

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

Шаблон MVVM

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

The MVVM pattern

Рис. 2-1. Шаблон MVVM

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

Преимущества использования шаблона MVVM приведены ниже.

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

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

Представления

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

Xamarin.Forms В приложении представление обычно является производным или ContentViewпроизводным классомPage. Однако представления также могут быть представлены шаблоном данных, который указывает элементы пользовательского интерфейса, которые будут использоваться для визуального представления объекта при отображении. Шаблон данных в виде представления не имеет программной части и предназначен для привязки к определенному типу модели представления.

Совет

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

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

ViewModel

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

Совет

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

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

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

Совет

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

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

Для коллекций предоставляется понятное ObservableCollection<T> представление. Эта коллекция реализует уведомление об изменении коллекции, что позволяет разработчику реализовать INotifyCollectionChanged интерфейс в коллекциях.

Модель

Классы модели — это не визуальные классы, которые инкапсулируют данные приложения. Поэтому модель можно рассматривать как представляющую модель домена приложения, которая обычно включает модель данных вместе с бизнес-логикой и логикой проверки. Примерами объектов модели являются объекты передачи данных (DTOs), обычные старые объекты CLR (POC), а также созданные сущности и прокси-объекты.

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

Подключение представления моделей в представления

Модели просмотра можно подключить к представлениям с помощью возможностей Xamarin.Formsпривязки данных. Существует множество подходов, которые можно использовать для создания представлений и представлений моделей и связывания их во время выполнения. Эти подходы делятся на две категории, известные как первая композиция представления и первая композиция модели представления. Выбор между первой композицией представления и моделью представления — это проблема предпочтения и сложности. Однако все подходы используют одну и ту же цель, что и для представления, назначаемой модели представления свойству BindingContext.

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

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

Совет

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

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

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

Самый простой подход заключается в том, чтобы представление декларативно создать экземпляр соответствующей модели представления в XAML. При построении представления также будет создан соответствующий объект модели представления. Этот подход демонстрируется в следующем примере кода:

<ContentPage ... xmlns:local="clr-namespace:eShop">  
    <ContentPage.BindingContext>  
        <local:LoginViewModel />  
    </ContentPage.BindingContext>  
    ...  
</ContentPage>

ContentPage При создании экземпляр LoginViewModel создается автоматически и устанавливается в качестве представленияBindingContext.

Это декларативное построение и назначение модели представления по представлению имеет преимущество, что это просто, но имеет недостаток, что он требует конструктора по умолчанию (без параметров) в модели представления.

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

Представление может иметь код в файле кода программной части, который приводит к назначению модели представления свойству BindingContext . Это часто выполняется в конструкторе представления, как показано в следующем примере кода:

public LoginView()  
{  
    InitializeComponent();  
    BindingContext = new LoginViewModel(navigationService);  
}

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

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

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

Автоматическое создание модели представления с помощью указателя модели представления

Указатель модели представления — это пользовательский класс, который управляет экземпляром моделей представления и их связью с представлениями. В мобильном приложении ViewModelLocator eShopOnContainers класс имеет присоединенное свойство, AutoWireViewModelкоторое используется для связывания моделей представления с представлениями. В XAML представления это присоединенное свойство имеет значение true, чтобы указать, что модель представления должна быть автоматически подключена к представлению, как показано в следующем примере кода:

viewModelBase:ViewModelLocator.AutoWireViewModel="true"

Свойство AutoWireViewModel является привязываемым свойством, инициализируемым значением false, и при изменении OnAutoWireViewModelChanged его значения вызывается обработчик событий. Этот метод разрешает модель представления для представления. В следующем примере кода показано, как это достигается.

private static void OnAutoWireViewModelChanged(BindableObject bindable, object oldValue, object newValue)  
{  
    var view = bindable as Element;  
    if (view == null)  
    {  
        return;  
    }  

    var viewType = view.GetType();  
    var viewName = viewType.FullName.Replace(".Views.", ".ViewModels.");  
    var viewAssemblyName = viewType.GetTypeInfo().Assembly.FullName;  
    var viewModelName = string.Format(  
        CultureInfo.InvariantCulture, "{0}Model, {1}", viewName, viewAssemblyName);  

    var viewModelType = Type.GetType(viewModelName);  
    if (viewModelType == null)  
    {  
        return;  
    }  
    var viewModel = _container.Resolve(viewModelType);  
    view.BindingContext = viewModel;  
}

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

  • Модели просмотра находятся в той же сборке, что и типы представлений.
  • Представления находятся в . Просматривает дочернее пространство имен.
  • Модели просмотра находятся в . Представление дочернего пространства имен ViewModels.
  • Имена моделей представления соответствуют именам представлений и заканчиваются именем ViewModel.

Наконец, OnAutoWireViewModelChanged метод задает BindingContext тип представления разрешенным типом модели представления. Дополнительные сведения о разрешении типа модели представления см. в разделе "Разрешение".

Этот подход имеет преимущество, что приложение имеет один класс, отвечающий за создание экземпляров моделей представления и их подключение к представлениям.

Совет

Используйте указатель модели представления для простоты подстановки. Указатель модели представления также можно использовать в качестве точки подстановки для альтернативных реализаций зависимостей, таких как модульное тестирование или данные времени разработки.

Обновление представлений в ответ на изменения в базовой модели представления или модели

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

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

  • Всегда вызывайте PropertyChanged событие, если значение общедоступного свойства изменяется. Не предполагайте, что повышение PropertyChanged события можно игнорировать из-за знаний о том, как происходит привязка XAML.
  • Всегда вызывайте PropertyChanged событие для любых вычисляемых свойств, значения которых используются другими свойствами в модели представления или модели.
  • Всегда вызывайте PropertyChanged событие в конце метода, который изменяет свойство, или когда объект, как известно, находится в безопасном состоянии. Вызов события прерывает операцию, вызывая обработчики события синхронно. Если это происходит в середине операции, объект может предоставлять функции обратного вызова, если он находится в небезопасном, частично обновленном состоянии. Кроме того, можно запускать каскадные изменения событиями PropertyChanged . Каскадные изменения обычно требуют завершения обновлений перед выполнением каскадного изменения.
  • Никогда не вызывает PropertyChanged событие, если свойство не изменяется. Это означает, что перед созданием PropertyChanged события необходимо сравнить старые и новые значения.
  • Никогда не вызывайте PropertyChanged событие во время конструктора модели представления, если вы инициализируете свойство. Элементы управления с привязкой к данным в представлении не будут подписаны на получение уведомлений об изменениях на данный момент.
  • Никогда не вызывайте несколько PropertyChanged событий с одним аргументом имени свойства в одном синхронном вызове общедоступного метода класса. Например, учитывая NumberOfItems свойство, резервное хранилище которого является _numberOfItems полем, если метод увеличивается _numberOfItems пятьдесят раз во время выполнения цикла, он должен вызывать уведомление NumberOfItems об изменении свойства только один раз, после завершения работы. Для асинхронных методов вызовите PropertyChanged событие для заданного имени свойства в каждом синхронном сегменте асинхронной цепочки продолжения.

Мобильное приложение eShopOnContainers использует ExtendedBindableObject класс для предоставления уведомлений об изменениях, которое показано в следующем примере кода:

public abstract class ExtendedBindableObject : BindableObject  
{  
    public void RaisePropertyChanged<T>(Expression<Func<T>> property)  
    {  
        var name = GetMemberInfo(property).Name;  
        OnPropertyChanged(name);  
    }  

    private MemberInfo GetMemberInfo(Expression expression)  
    {  
        ...  
    }  
}

Класс Xamarin.Form BindableObject реализует INotifyPropertyChanged интерфейс и предоставляет OnPropertyChanged метод. Класс ExtendedBindableObject предоставляет RaisePropertyChanged метод для вызова уведомления об изменении свойства и использует функциональные возможности, предоставляемые классом BindableObject .

Каждый класс модели представления в мобильном приложении eShopOnContainers является производным от ViewModelBase класса, который, в свою очередь, является производным от ExtendedBindableObject класса. Таким образом, каждый класс модели представления использует RaisePropertyChanged метод в ExtendedBindableObject классе для предоставления уведомления об изменении свойств. В следующем примере кода показано, как мобильное приложение eShopOnContainers вызывает уведомление об изменении свойств с помощью лямбда-выражения:

public bool IsLogin  
{  
    get  
    {  
        return _isLogin;  
    }  
    set  
    {  
        _isLogin = value;  
        RaisePropertyChanged(() => IsLogin);  
    }  
}

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

Взаимодействие пользовательского интерфейса с помощью команд и поведения

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

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

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

Реализация команд

Модели просмотра обычно предоставляют свойства команд для привязки из представления, которые являются экземплярами объектов, реализующими ICommand интерфейс. Ряд Xamarin.Forms элементов управления предоставляет Command свойство, которое может быть привязано к ICommand объекту, предоставленному моделью представления. Интерфейс ICommand определяет Execute метод, который инкапсулирует саму операцию, CanExecute метод, который указывает, может ли вызываться команда, и CanExecuteChanged событие, возникающее при возникновении изменений, влияющих на выполнение команды. Классы Command и классы, предоставляемые Xamarin.Formsинтерфейсом, реализуют ICommand интерфейс, где T является тип аргументов и ExecuteCanExecute.Command<T>

В модели представления должен быть объект типа Command или Command<T> для каждого общедоступного свойства в модели представления типа ICommand. Command<T> Для Command конструктора требуется объект обратного Action вызова, который вызывается при ICommand.Execute вызове метода. Метод CanExecute является необязательным параметром конструктора и является Func возвращающим объект bool.

В следующем коде показано, как Command экземпляр, представляющий команду регистрации, создается путем указания делегата Register в метод модели представления:

public ICommand RegisterCommand => new Command(Register);

Команда предоставляется представлению с помощью свойства, возвращающего ссылку на объект ICommand. Execute При вызове метода в Command объекте он просто перенаправит вызов метода в модели представления с помощью делегата, указанного в конструктореCommand.

Асинхронный метод можно вызвать с помощью команды и asyncawait ключевое слово при указании делегата Execute команды. Это означает, что обратный вызов является и Task должен ожидаться. Например, в следующем коде показано, как Command экземпляр, представляющий команду входа, создается путем указания делегата в SignInAsync метод модели представления:

public ICommand SignInCommand => new Command(async () => await SignInAsync());

Параметры можно передать в Execute и CanExecute действия с помощью Command<T> класса для создания экземпляра команды. Например, в следующем коде показано, как Command<T> используется экземпляр для указания того, что NavigateAsync методу потребуется аргумент типа string:

public ICommand NavigateCommand => new Command<string>(NavigateAsync);

В обоих CommandCommand<T> классах делегат метода в каждом конструкторе является необязательным CanExecute . Если делегат не указан, Command возвращаетсяtrue.CanExecute Однако модель представления может указывать на изменение состояния команды CanExecute путем вызова ChangeCanExecute метода в объекте Command . Это приводит CanExecuteChanged к возникновению события. Все элементы управления в пользовательском интерфейсе, привязанные к команде, затем обновят их состояние, чтобы отразить доступность команды, привязанной к данным.

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

В следующем примере кода показано, как Grid привязывается LoginView к RegisterCommandLoginViewModel классу с помощью экземпляра TapGestureRecognizer :

<Grid Grid.Column="1" HorizontalOptions="Center">  
    <Label Text="REGISTER" TextColor="Gray"/>  
    <Grid.GestureRecognizers>  
        <TapGestureRecognizer Command="{Binding RegisterCommand}" NumberOfTapsRequired="1" />  
    </Grid.GestureRecognizers>  
</Grid>

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

Реализация поведения

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

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

Xamarin.Forms Поведение — это класс, производный от Behavior или Behavior<T> класса, где T тип элемента управления, к которому должно применяться поведение. Эти классы предоставляют OnAttachedTo и OnDetachingFrom методы, которые следует переопределить, чтобы обеспечить логику, которая будет выполняться при присоединении поведения к элементам управления и отсоединения от элементов управления.

В мобильном приложении BindableBehavior<T> eShopOnContainers класс является производным от Behavior<T> класса. Цель BindableBehavior<T> класса — предоставить базовый класс для Xamarin.Forms поведения, требующего BindingContext задания поведения присоединенным элементом управления.

Класс BindableBehavior<T> предоставляет переопределимый метод, который задает BindingContext поведение, и переопределимый OnAttachedToOnDetachingFrom метод, который очищает BindingContext. Кроме того, класс хранит ссылку на присоединенный элемент управления в свойстве AssociatedObject.

Мобильное приложение eShopOnContainers включает EventToCommandBehavior класс, который выполняет команду в ответ на событие. Этот класс является производным от BindableBehavior<T> класса, чтобы поведение может привязаться к свойству и выполнить ICommand его Command при использовании поведения. Следующий пример кода демонстрирует класс EventToCommandBehavior:

public class EventToCommandBehavior : BindableBehavior<View>  
{  
    ...  
    protected override void OnAttachedTo(View visualElement)  
    {  
        base.OnAttachedTo(visualElement);  

        var events = AssociatedObject.GetType().GetRuntimeEvents().ToArray();  
        if (events.Any())  
        {  
            _eventInfo = events.FirstOrDefault(e => e.Name == EventName);  
            if (_eventInfo == null)  
                throw new ArgumentException(string.Format(  
                        "EventToCommand: Can't find any event named '{0}' on attached type",   
                        EventName));  

            AddEventHandler(_eventInfo, AssociatedObject, OnFired);  
        }  
    }  

    protected override void OnDetachingFrom(View view)  
    {  
        if (_handler != null)  
            _eventInfo.RemoveEventHandler(AssociatedObject, _handler);  

        base.OnDetachingFrom(view);  
    }  

    private void AddEventHandler(  
            EventInfo eventInfo, object item, Action<object, EventArgs> action)  
    {  
        ...  
    }  

    private void OnFired(object sender, EventArgs eventArgs)  
    {  
        ...  
    }  
}

OnDetachingFrom Методы OnAttachedTo используются для регистрации и отмены регистрации обработчика событий для события, определенного в свойствеEventName. Затем при срабатывании OnFired события вызывается метод, который выполняет команду.

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

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

Это EventToCommandBehavior особенно полезно для присоединения команды к элементу управления, который не поддерживает команды. Например, используется ProfileViewEventToCommandBehavior для выполнения OrderDetailCommand события при ItemTapped срабатывании события в ListView списке заказов пользователя, как показано в следующем коде:

<ListView>  
    <ListView.Behaviors>  
        <behaviors:EventToCommandBehavior             
            EventName="ItemTapped"  
            Command="{Binding OrderDetailCommand}"  
            EventArgsConverter="{StaticResource ItemTappedEventArgsConverter}" />  
    </ListView.Behaviors>  
    ...  
</ListView>

Во время выполнения EventToCommandBehavior ответит на взаимодействие с ListViewним. При выборе элемента в ListViewItemTapped объекте событие будет срабатывает, которое будет выполняться OrderDetailCommand в элементе ProfileViewModel. По умолчанию аргументы событий для события передаются команде. Эти данные преобразуются при передаче между источником и целевым преобразователем, указанным в EventArgsConverter свойстве, который возвращает Item значение ListView из объекта ItemTappedEventArgs. Поэтому при OrderDetailCommand выполнении выбранный параметр Order передается в зарегистрированное действие.

Дополнительные сведения о поведении см. в разделе "Поведение".

Итоги

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

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