Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Начальство разработчика Prism — часть 9, взаимодействие между слабо связанными компонентами

Anna | 18.06.2014 | нет комментариев

При создании большого и трудного приложения, обыкновенным подходом является распределение функциональности по отдельным модулям. Также желанно минимизировать число статических ссылок между этими модулями. Вследствие этому, модули дозволено будет самостоятельно разрабатывать, тестировать, развёртывать и обновлять. Всё это ведёт к необходимости модулей взаимодействовать друг с ином слабо связанным образом.При построении модели взаимодействия между модулями, нужно знать различия между подходами, Дабы знать, какой из них применить в определенном сценарии. Библиотека Prism предоставляет следующие подходы:

  • Применение команд (Solution commanding). Используйте для реагирования на действия пользователя.
  • Контекст региона (Region context). Используйте для передачи контекстной информации от host-элемента управления к представлениями в регионе. Данный подход в некотором роде аналогичен DataContext, но не полагается на него.
  • Всеобщие службы (Shared services). Вы можете вызвать способ на сервисе, тот, что, в свою очередь, сгенерирует событие, на которое могут быть подписаны получатели. Используйте данный подход в том случае, если все остальные подходы не применимы.
  • Агрегация событий (Event aggregation). Для передачи сообщений между моделями представлений, презентерами, либо контроллерами при отсутствии ожиданий о непосредственной реакции на сообщение.

Применение команд (Solution commanding)

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

WPF предоставляет маршрутизируемые команды (RoutedCommand), которые отлично подходят для связи элемента управления, вызывавшего команду, такого как кнопка, либо элемент меню, с обработчиком этой команды, ассоциированным с нынешним элементом, имеющим фокус клавиатуры.

Впрочем в сценариях составного приложения, обработчиком события Зачастую является модель представления, либо не имеющая каких-либо ассоциированных элементов в визуальном дереве, либо которые не имеют фокуса. Для поддержки таких сценариев, Prism предоставляет класс DelegateCommand, тот, что разрешает вызывать делегат при выполнении команды, и класс CompositeCommand, разрешающий комбинировать несколько команд в одну. Эти классы отличаются от встроенного класса RoutedCommand, маршрутизирующего обработку команды вниз и вверх по визуальному дереву. Они разрешают инициировать команду в визуальном дереве, а обработать её на больше высоком ярусе.

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

Класс CompositeCommand поддерживает активацию. При появлении события CanExecuteChanged, он сам генерирует это событие. Позже этого, ассоциированный элемент управления вызывает способ CanExecute наCompositeCommandCompositeCommand опрашивает все дочернии команды, вызывая их способ CanExecute. Если какая-либо команда возвращает falseCompositeCommand тоже возвращает false, деактивируя, таким образом, элемент управления.

Приложения, базирующиеся на библиотеке Prism, могут иметь всеобщии команды CompositeCommand, определённые в оболочке, имеющие толк по каждому приложению, такие как SaveSave All и Cancel. Модули могут регистрировать свои локальные команды в этих глобальных командах принимая, таким образом, участие в их выполнении.

Заметка про WPF Routed Events (маршрутизируемые события) и Routed Commands(маршрутизируемые команды).
Маршрутизируемые события отличаются от обыкновенных событий тем, что обрабатывать их могут несколько обработчиков, расположенных в разных местах дерева элементов. Маршрутизируемые события WPF переносят сообщения между элементами визуального дерева. Элементы не находящиеся в визуальном дереве, не могут обрабатывать эти команды. Маршрутизируемые события могут применяться для коммуникации между элементами визуального дерева, так как данные о событии сохраняются для всякого элемента в маршруте. Один элемент может изменить что-то в этих данных, и это метаморфоза будет доступно для дальнейшего элемента в маршруте.

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

Создание команды-делегата (Delegate command)

Для создания команды-делегата нужно сделать и инициализировать поле типа DelegateCommand в конструкторе модели представления и сделать его доступным через качество типа ICommand.

public class ArticleViewModel : NotificationObject {
    private readonly ICommand _showArticleListCommand;

    public ArticleViewModel(INewsFeedService newsFeedService,
                            IRegionManager regionManager,
                            IEventAggregator eventAggregator) {
        _showArticleListCommand = new DelegateCommand(this.ShowArticleList);
    }

    public ICommand ShowArticleListCommand { get { return _showArticleListCommand; } }
}

Заметка.
Для редко используемых команд Зачастую имеет толк «неохотно» создавать DelegateCommandнепринужденно в способе get свойства.

public class ArticleViewModel : NotificationObject {
    private readonly ICommand _showArticleListCommand;

    public ArticleViewModel(INewsFeedService newsFeedService,
                            IRegionManager regionManager,
                            IEventAggregator eventAggregator) { }

    public ICommand ShowArticleListCommand {
        get {         
            return _showArticleListCommand ??
                ( _showArticleListCommand = new DelegateCommand(this.ShowArticleList) ); 
        } 
    }
}

Создание комбинированный команды (Composite command)

Для создания комбинированный команды нужно сделать и инициализировать поле типа CompositeCommand в конструкторе модели представления и сделать его доступным через качество типа ICommand.

public class MyViewModel : NotificationObject {
    private readonly CompositeCommand _saveAllCommand;

    public ArticleViewModel(INewsFeedService newsFeedService,
                            IRegionManager regionManager,
                            IEventAggregator eventAggregator) {
        _saveAllCommand = new CompositeCommand();
        _saveAllCommand.RegisterCommand(new SaveProductsCommand());
        _saveAllCommand.RegisterCommand(new SaveOrdersCommand());
    }

    public ICommand SaveAllCommand { get { return _saveAllCommand; } }
}

Создание глобально доступных команд

Обыкновенно, для создания глобально доступной команды, создаётся экземпляр DelegateCommand, либоCompositeCommand и делается доступным через статический класс.

public static class GlobalCommands {
    public static CompositeCommand MyCompositeCommand = new CompositeCommand();
}

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

GlobalCommands.MyCompositeCommand.RegisterCommand(command1);
GlobalCommands.MyCompositeCommand.RegisterCommand(command2);

Заметка.
Для совершенствования тестируемости кода, вы можете применять класс-посредник для доступа к глобально доступной команде и мокировать его в тестах.

Привязка к глобально доступным командам

Дальнейший пример кода показывает, как сделать привязку кнопки к всеобщей команде в WPF.

<Button Name="MyCompositeCommandButton" 
        Command="{x:Static local:GlobalCommands.MyCompositeCommand}"
        Content="Execute My Composite Command">    
</Button>

Silverlight не предоставляет поддержку для x:static, следственно для привязки кнопки в Silverlight, нужно исполнить следующие шаги:

  1. В модели представления нужно сделать public качество для приобретения команды, заданной в статическом классе.
    public ICommand MyCompositeCommand { get { return GlobalCommands.MyCompositeCommand; } }
    
  2. Обыкновенно, модели представления ассоциируются с представлениями через DataContext (что делается в файле отделённого кода).
    view.DataContext = model;
    
  3. Удостоверитесь, что в корневой элемент добавлено следующее пространство имён XML.
    xmlns:prism="clr-namespace:Microsoft.Practices.Prism.Commands;assembly=Microsoft.Practices.Prism"
    
  4. Привяжите кнопку к команде, применяя присоединённое качество Click.Command.
    <Button Name="MyCommandButton" 
            prism:Click.Command="{Binding MyCompositeCommand}"
            Content="Execute MyCommand"/>    
    </Button>
    

Заметка.
Иным подходом является сохранение команды как источник в файле App.xaml в разделеApplication.Resources. Позже этого в представлении сделать к нему привязку.

<Button Name="MyCommandButton" 
        prism:Click.Command="{Binding MyCompositeCommand, Source={StaticResource GlobalCommands}}
        Content="Execute MyCommand"/>    
</Button>

Контекст региона (Region context)

Существует уйма сценариев, когда может потребоваться передавать контекстную информацию между представлением, ассоциированным с регионом (хостом), и представлениями, находящимися в этом регионе. Для примера, в сценарии master-detail представление отображает сущность и регион, в котором выводится добавочная информация по этой сущности. Prism использует концепт под наименованием RegionContext для передачи объекта между хостом региона и представлениями, загруженными в данный регион, как показано на иллюстрации ниже.

Использование RegionContext

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

  • Задание RegionContext региона, применяя XAML.
  • Задание RegionContext региона в коде.
  • Приобретение RegionContext от представубликация событий
    В следующих разделах описывается, как создавать, публиковать и подписываться на событияCompositePresentationEvent, применяя интерфейс IEventAggregator.

    Создание события

    Класс CompositePresentationEvent<TPayload> предуготовлен служить базовым классом для событий, характерных для модуля, либо приложения. TPayLoad является типом пригодной нагрузки события. Пригодная нагрузка является доводом, тот, что будет передан подписчику при публикации события.

    Для примера, дальнейший код показывает класс TickerSymbolSelectedEvent в Stock Trader RI. Пригодной нагрузкой является строка, содержащая символ компании. Обратите внимание, что тело класса пусто.

    public class TickerSymbolSelectedEvent : CompositePresentationEvent<string> { }
    

    Заметка.
    В комбинированных приложениях, события нередко доступны нескольким модулям, следственно их нужно помещать в общедоступном месте. В Stock Trader RI, все они помещены в плане StockTraderRI.Infrastructure.

    Публикация события

    Издатели публикуют события с поддержкой приобретения объекта события через EventAggregator и вызова способа Publish. Для приобретения EventAggregator, вы можете применять внедрение зависимости, добавив параметр типа IEventAggregator в конструктор класса.

    Для примера, дальнейший код показывает публикацию события TickerSymbolSelectedEvent.

    this.eventAggregator
        .GetEvent<TickerSymbolSelectedEvent>()
        .Publish("STOCK0");
    

    Подписка на события

    Подписчики могут подписаться на событие, применяя одну из перегрузок способа Subscribe классаCompositePresentationEvent. Существует несколько путей подписаться наCompositePresentationEvents. Следующие критерии могут подмогнуть предпочесть особенно подходящий.

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

    В следующих разделах эти варианты будут рассмотрены подробнее.

    Подписка в потоке UI

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

    По умолчанию, подписчик получает событие в потоке издателя. Если издатель посылает событие из потока UI, подписчик может вольно обновлять UI. Впрочем если поток издатель является фоновым, подписчик не сумеет напрямую обновлять элементы UI. В этом случае, подписчику нужно будет наметить обновление UI, применяя класс Dispatcher.

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

    public void Run() {
        ...
        this.eventAggregator
           .GetEvent<TickerSymbolSelectedEvent>()
           .Subscribe(ShowNews, ThreadOption.UIThread);
    }
    
    public void ShowNews(string companySymbol) {
        this.articlePresentationModel.SetTickerSymbol(companySymbol);
    }
    

    Перечисление ThreadOption содержит следующие члены:

    • PublisherThread. События будут получены в потоке издателя. Это является стандартным поведением.
    • BackgroundThread. Событие будет получено асинхронно в потоке из пула потоков .NET Framework.
    • UIThread. Событие будет получено в потоке UI.
    Фильтрация при подписке

    Подписчики могут не хотеть обрабатывать всякий экземпляр опубликованного события. В таких случаях, подписчик должен применять параметр filter. Данный параметр имеет типSystem.Predicate<TPayLoad> и разрешает задать делегат, тот, что будет вызываться при публикации события, для определения, следует ли реагировать на это событие.

    Нередко, фильтр представляет собой лямбда-выражение, как показано ниже.

    FundAddedEvent fundAddedEvent = this.eventAggregator.GetEvent<FundAddedEvent>();
    
    fundAddedEvent.Subscribe(
        FundAddedEventHandler,
        ThreadOption.UIThread,
        false,
        fundOrder => fundOrder.CustomerId == this.customerId);
    

    Заметка.
    Из-за модели безопасности Silverlight, способ фильтра должен быть публично доступным, если вы хотите применять слабые ссылки при подписке (что является поведением по умолчанию для CompositePresentationEvent). Так как лямбда-выражения и неизвестные делегаты генерируются как приватные классы и способы, они не могут применяться совместно со слабыми ссылками в Silverlight. Взамен этого, вы обязаны применять либо public способы, либо установить параметр keepSubscriberReferenceAlive в true, Дабы принудить применять мощные ссылки при подписке (глядите пример ниже).

    Из-за модели безопасности Silverlight, вы обязаны будете вызывать обособленный public способ, как показано на примере ниже, либо подписываться, применяя крепкие ссылки (будет показано дальше в примерах).

    public bool FundOrderFilter(FundOrder fundOrder){
        return fundOrder.CustomerId == this.customerId;
    }
    ...
    
    FundAddedEvent fundAddedEvent = this.eventAggregator.GetEvent<FundAddedEvent>();
    
    subscriptionToken = fundAddedEvent.Subscribe(
        FundAddedEventHandler, 
        ThreadOption.UIThread, 
        false, 
        FundOrderFilter);
    

    Заметка.
    Способ Subscribe возвращает токен подписки типаMicrosoft.Practices.Prism.Events.SubscriptionToken, тот, что может применяться для удаления подписки на событие. Данный токен исключительно пригоден при применении неизвестных делегатов, либо лямбда-выражений, либо когда вы используете один и тот же обработчик, но с различными фильтрами.

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

    Подписка с применением крепких ссылок (Strong references)

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

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

    Для создания подписки с применением мощной ссылки, используйте параметрkeepSubscriberReferenceAlive способа Subscribe, как показано на примере ниже.

    FundAddedEvent fundAddedEvent = eventAggregator.GetEvent<FundAddedEvent>();
    
    bool keepSubscriberReferenceAlive = true;
    
    fundAddedEvent.Subscribe(
        FundAddedEventHandler, 
        ThreadOption.UIThread, 
        keepSubscriberReferenceAlive, 
        fundOrder => fundOrder.CustomerId == _customerId);
    

    Параметр keepSubscriberReferenceAlive типа bool:

    • При установке в true, экземпляр события хранит крепкую ссылку на экземпляр подписчика, не разрешая ему тем самым стать достигаемым для сборщика мусора. Про отписку от события, глядите раздел дальше с статье.
    • При установке в false (что являетс

Источник: programmingmaster.ru

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB