Wednesday, December 19, 2007

Composite UI Application Block (CAB) and Smart Client Software Factory

Сперва парочку слов с чего я заинтересовался этой частью .Net'а.
Для меня данный раздел новый, а поскольку уже достаточно компаний которые идут в ногу с технологиямии используют оную решил я узнать что за зверь такой и с чем его едят.
Как всегда - все гениальное просто, и данная технология не исключение.Рассмотрим ее на примере.


CAB - это реализация шаблонов и концепций предназначеных для создания сложных desctop приложений, словом надстройка над Win Forms.Идея CAB заключается в жесткой модульности создаваемого приложения, т.е. создаются максимально независимые друг от друга модули и реализовывается рабочий процесс внутри приложения (intra-application workflow).Жесткая модульность предназначена для повторного использования кода, что есть жЫрный +.Оболочка (UI элементы) слабо связаны с самими составными частями приложения.

Существует всего 4 слона на которых держится САВ, если говорить о САВ сервисах:
1. Загрузчик модулей и сервисы перечисления модулей
2. Сервис Event Broker
3. Сервис аутентификации
4. Сервис сохранения состояния

При создании САВ проекта сперва необходимо создать САВ-модуль, кот имеет свою точку входа.Модули находся внутри собственной DLL. Модули не требуют прямых ссылок на них из проекта оболочки в Visual Studio и непосредственной привязки к исполняемому файлу. Они загружаются в период выполнения загрузчиком модулей.

CAB-приложения базируются на шаблоне MVP/MVC (Model-View-Presenter/Model-View-Controller), который состоит из 3 элементов:
1. Presenter
2. SmartPart
3. State

Сервисы подключаются по мере необходимости в соответствии с логикой приложения. Это позволяет разработчику динамически создавать новые экземпляры объектов или возвращать объекты, уже созданные внутри контейнера. Для CAB этот контейнер является рабочим элементом и одним из первых создается разработчиком модуля. Компоненты CAB, например клиентские сервисы и рабочие пространства, можно добавлять в контейнер явно с помощью AddNew:
_rootWorkItem.WorkItems.AddNew();
То же самое можно сделать через конфигурационный файл или декларативно:

[CreateNew]
public ShipNewOrderViewPresenter Presenter{
set {
_presenter = value;
_presenter.View = this;
}
}

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

[ServiceDependency]
public WorkItem WorkItem{
set {
_workItem = value;
}
}

Главное что пожалуй стоит усвоить, это то что ключевым компонентом является OBjectBuilder.С помощью Object Builder(не без помощи концепции Dependency Injection) можно создавать объекты какого-либо класса или возвращать существующие, если они подходят;сопоставлять свойства и методы с аттрибутами, которые вляют на создание и именование новго объекта; удобно удалять параметры из существующих обхектов,двигаясь обратно по цепочке операций.За включение сервисов в наш модуль отвечает Dependency Injection.

Для CAB-приложения характерен следующий порядок работы.
1. Пользователь дважды щелкает EXE-файл, отображающий оболочку.
2. Оболочка появляется на рабочем столе, загружает заданные CAB-сервисы, отображает UI-элементы и загружает все сконфигурированные модули.
3. По окончании загрузки каждый модуль добавляет по рабочему элементу в свой родительский элемент, чтобы управлять подприложением и выдавать запросы к рабочему элементу на показ его содержимого в поддерживаемом рабочем пространстве.
4. Когда пользователь выбирает какой-либо элемент меню, срабатывает соответствующая команда.
5. Обработчик команд реагирует на эту команду созданием дочернего рабочего элемента, который управляет другим подприложением и отображает его представления.6. Рабочий элемент отображает свое представление с помощью Smart Part.
7. Пользователь взаимодействует с этим интерфейсом, который в свою очередь обращается к контроллеру (или его презентатору).
8. Контроллер изменяет общие данные (состояние) и связывается с хостом, используя Event Broker.
9. Event Broker отправляет актуальную информацию (контекст) другим модулям или компонентам.

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

1. Создаем новое Windows Forms приложение.
2. Добавляем в проект следующие ссылки на сборки:

Microsoft.Practices.CompositeUI
Microsoft.Practices.CompositeUI.WinForms
Microsoft.Practices.ObjectBuilder

3. Затем создаем класс приложения, в котором определяется WorkItem
public class MyFirstCABApplication :
FormShellApplication{ }

4. Реализуем метод Main:
[STAThread]public static void Main(){
new MyFirstCABApplication.Run();
}

5. Перегрузим AfterShellCreated, и вызовем его базовую реализацию и инициализируйте интерфейса

protected override void AfterShellCreated(){
base.AfterShellCreated();
// Регистрируем узел расширения,

// в данном случае - File Menu
ToolStripMenuItem fileItem = (ToolStripMenuItem) Shell.MainMenuStrip.Items["File"]; MyWorkItem.UIExtensionSites.RegisterSite( "File", fileItem.DropDownItems);
// Создаем и добавляем элементы меню в узел File Menu
ToolStripMenuItem item = new ToolStripMenuItem("Show Customer"); MyWorkItem.UIExtensionSites["File"].Add(item);
// Добавляем обработчик события "щелчок" в элемент меню

// с именем ShowCustomer MyWorkItem.Commands["ShowCustomer"].AddInvoker(item, "Click");

}

6. Создаем Smart-Part. Вводим в проект пользовательский элемент управления и добавим ссылку на пространство имен Microsoft.Practices.CompositeUI.SmartParts.После этого, добвим в свой класс аттрибут SmartPart:
[SmartPart]public partial class CustomerSmartPart : UserControl{ ...}

7. Теперь впихнем обработчик команд и отобразим SamrtPart с помощью DeckWorkspace:
[CommandHandler("ShowCustomer")]
public void ShowCustomer(object sender, EventArgs e){
Form1 mainForm = new Form1();
CustomerSmartPart sp = MyWorkItem.Items.AddNew(); mainForm.deckWorkspace1.Show(sp);
// deckWorkspace1 — элемент управления,

// перенесенный на Form1
}

В окно инструментария (toolbox) можно добавить любое рабочее пространство CAB и перенести
его на проектируемую форму подобно обычному элементу управления.

Создание модуля и инициализатора

1. Создадим библиотеку классов или элементов управления.
2. Добавим ссылки: Microsoft.Practices.CompositeUI Microsoft.Practices.ObjectBuilder
3. Добавим в проект ModuleAttribute, чтобы другие модули могли идентифицировать этот модуль в коде.
4. Добавляем в свой файл AssemblyInfo.cs:[assembly: Microsoft.Practices.CompositeUI.Module("MyFirstModule")]
5. Создаем новый класс и наследуем его от Microsoft.Practices.CompositeUI.ModuleInit
Перегружаем AddServices, чтобы иметь возможность добавлять САВ-сервисы в период выполнения, и перегружаем Load для отображения любого UI.
6. Чтобы наш модуль мог загружаться, создадим XML файл ProfileCatalog.xml, который ссылается на наш новый DDL файл с модулем. Добавим этот XML файл в основной EXE проект.
7. Каталог профиля будет выглядеть так:


8. Добавим метод Load и реализуем его в своем новом классе ModuleInit. Создаем объект WorkItem в нашем модуле с использованием метода AddNew в контексте RootWorkItem. Затем вызываем метод Run нового объекта WorkItem. Итого, мы получаем рабочее пространство по имени и передаем это рабочее пространство методу Run. В данном примере целевым является пространство DeckWorkspace с именем deckWorkspace1:

public override void Load(){
base.Load();
MyWorkItem myWorkItem = RootWorkItem.WorkItems.AddNew();
myWorkItem.Run(parentWorkItem.Workspaces["deckWorkspace1"]);
}

9. Реализуем метод Run нашего класса MyWorkItem, чтобы запустить свой собственный код:
public void Run(IWorkspace deckWorkspace){
// Используйте любой Smart Part
IMyView view = this.Items.AddNew();
deckWorkspace.Show(view);
}


1 comment:

dima said...

Спасибо за статью! А не могли бы выложить проект студийный для вашего примера или другие примеры! Заранее благодарен!