//тут введение. можно пошутить, но лучше не стоит.
Пора описать реализацию.
Object model.
Для того, чтобы System.AddIn framework мог обнаружить и хост мог подключить плагины, мы должны должны реализовать требуемый framework'ом pipeline, состоящий и 5 компонент:
- контракта плагина и, если необходимо, хоста;
- представления хоста (то как хост видит плагин - изолирует хост и от контракта и от самого плагина подавно);
- представления плагина (то как плагин видит хост - -//-);
- адаптеры стороны хоста, транслирующие вызовы представления хоста в вызовы контракта - наследники представления хоста; адаптеры, транслирующие вызовы контракта в вызовы представления хоста - наследники контракта;
- адаптеры стороны плагина, транслирующие вызовы контракта в вызовы представления плагина - наследники контракта; адаптеры, транслирующие вызовы представления плагина в вызовы контракта - наследники представления хоста.
Contracts.

IGadgetAddInContract - контракт, с помощью которого хост общается с плагином (функциональность, которую хост требует от плагинов).
IHostContract - контракт, с помощью которого плагин может общаться с хостом (функциональность, которую хост предоставляет плагину).
IAddInTunedContract - вспомогательный контракт для передачи настроек плагина хосту (подробнее о нем я напишу в одной из следующих частей).
IServiceLocatorContract - обертка для передачи набора сервис-локаторов плагину.
Метод Initialize, согласно его имени, предназначен для инициаллизации плагина. В качестве параметра ему передается контракт объекта хоста. Возвращает же этот метод некий id, позволяющий однозначно идентифицировать плагин.

метод Initialize TestGadgetAddIn.
InitializeServices предназначен для инициализации сервисной инфраструктуры плагина, загруженного в другой домен или процесс (я использую ServiceLocator как форму IoC).

метод InitializeServices TestGadgetAddIn.
Эти два метода могут вызываться из фоновых потоков приложения, улучшая тем самым отклик приложения, т.к. процесс инициализации изолированного плагина довольно длительная процедура из-за remoting-вызовов.
Последний метод контракта обязательно должен вызываться из основного потока приложения (UI-поток), т.к. он возвращает представление (gadget) - UI контрол, который будет добавлен в визуальное дерево представления хоста, что само собой влечет за собой требование идентичности Dispatcher'ов контролов.

HostViews.

Представление хоста, как я уже писал изолирует хост от контракта, позволяя изменять версии контракта и соответственно плагина безболезненно для хоста (т.е. он сможет работать даже с более новыми плагинами, реализующими другой, может быть более полный контракт), без перекомпиляции. Для этого лишь понадобится написать новый адаптер. Абстрактный класс представления плагина хостом наследует и реализует адаптер хоста. INativeHandleContract - это обертка, с помощью которой команде WPF удалость передать визуальный объект через границы домена. Реально в визуальном дереве хоста появится не ожидаемый контрол (gadget), который создает плагин, а специальный контрол AddInHost - наследник HwndHost, т.е. контрол позволяющий хостить внутри себя не-WPF контент. Отсюда вывод, что наш gadget будет рендериться не WPF-engine, а Win32. О проблемах, которые это за собой влечет и решении я напишу в одной из следующих частей.
HostAdapters.
Адаптеры предназначены для введения промежуточного слоя преобразователей вызовов от хоста к плагину и обратно.

Адаптер контракта плагина к представлению хоста.

Адаптер хоста к контракту.
Интересный момент здесь, то как передаются настройки - полученный контракт заворачивается в AddInTunedProxy, который восстанавливает UI-специфичные свойства из их сериализованного представления (свойство Image) и публикует настроечный UI при помощи встроенного класса FrameworkElementAdapters, определенного в System.Windows.Presentation.

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

AddInAdapters.

Адаптер плагина к его контракту.
Просто делегирует вызовы контракту.

Адаптер контракта хоста к его представлению.
Наследует абстрактному классу представления хоста плагином. Настройки передаются в виде контракта AddInTunedToContractAdapter, который публикует proxy Tuned для AddInTunedProxy и UI.

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