четверг, 24 июня 2010 г.

Trigger Caliburn action status update through Prism's IEventAggregator

И Caliburn и Prism, в принципе решают одни и те же задачи - создание композитных приложений на WPF. Но, вот Роб Эйзенберг (создатель Caliburn) не считает, что они друг друга взаимоисключают, а я и подавно с ним согласен.
Я использую следующие "фичи" Prism'a:

  • modularity;
  • region manager;
  • event aggregator;
, а из Caliburn следующие:
  • actions engine;
  • application & model framework, даже не смотря на то, что с первого взгляда model framework кажется "overarchitected";
  • convention over configuration;
Вот сейчас я вам расскажу как расширить actions в Caliburn, при помощи Event Aggregator из Prism.
Главный способ расширения - это написание фильтров, которые вставляются в последовательность выполнения action. С их помощью можно проверять предусловия или состояние данных (к примеру можно проверить обладает ли Principal достаточными правами для выполнения действия), дабы определить статус action и обновить эффекты, применяющиеся к UI элементам. Примером такого фильтра является поставляющийся с framework фильтр Dependencies, который вызывает исполнение всей последовательности фильтров, на основании изменения какого-либо свойства, имя которого передается в качестве параметра ему. Но что, если вам нужно просто обновлять статус action, не связанный ни с каким полем в объекте, на основании какого-либо события/условия. Вы можете завести специальное поле и передергивать его значение или даже еще лучше - не заводите никакого поля, а просто кидайте нотификацию с именем несуществующего поля, переданного фильтру. Не знаю как вам, а у меня от таких решений зудит в одном месте.

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

DependsOnEvent




Фильтр применяется как атрибут, которому передается тип события. Тут есть одно ограничение - событие должно быть унаследовано от generic типа CompositePresentationEvent закрытого типом object, т.е. CompositePresentationEvent_Of_Object. В методе Initialize происходит магия с помощью reflection. Дело в том, что IEventAggregator Prism имеет исключительно generic интерфейс, за что можно разработчиков его только поругать. Мы получаем событие переданного типа и
подписываемся на него. Вот именно для этого и приходится колдовать с волшебным зеркальцем, т.к. заранее тип события не известен. Часть, относящуюся к запуску обработки action, я содрал из кода упомянутого выше Dependencies фильтра.


Здесь мы создаем Observer, который будет следить за изменениями зависимых свойств. Т.к. реально никакие свойства не меняются я просто использую трюк с несуществующим свойством - в Initialize при подписке на событие я передаю делегат, который кидает событие обновления свойства Update, которого-то как раз и нет. Observer, поймав это событие запускает всю цепочку обновления статуса action.




PublishEventAfter
Ну и напоследок, фильтр, выполняющий все ровно наоборот, после выполнения action он кидает событие, которое обрабатывает EventAggregator.




Удачи!