Разработка приложения Hello MEF на Silverlight 4 — Часть I

Автор: Glenn Block
Оригинал: Building the Hello MEF dashboard in Silverlight 4 — Part I

В моем прошлом посте я продемонстрировал основы Managed Extension Framework с помощью приложения Hello MEF, которое я использовал на PDC. В этой серии постов, мы создадим приложение с нуля о чем и будем говорить в будущем. Это займет несколько статей, я пока не уверен в этом, давайте посмотрим ;-) Мы создадим приложение в итеративном стиле, по пути пересматривая отдельные компоненты чтобы вносить новую функциональность.

До того как вы продолжите, сделаю анонс тем, которые мы по пути рассмотрим:

  • Экспорт / Импорт и инициализатор компонентов.
  • Экспорт метаданных.
  • Настраиваемый экспорт.
  • Переопределение инициализатора компонентов.
  • Динамическая загрузка XAP.

В этом посте мы рассмотрим только первый пункт.
Что-бы продолжить вам необходим Silverlight 4 и Silverlight 4 Toolkit. Вы можете скачать Silverlight 4 здесь и toolkit здесь.

Приложение

Ниже снимок экрана приложения.

Может оно и выглядит не очень сложно, но на самом деле это довольно обманчиво. Приложение представляет собой простую оболочку. Два виджета «Hello MEF» которые вы видите на самом деле обнаружены и экспортированы с помощью MEF. Само по себе приложение не знает о том что нужно искать, но оно умеет размещать предоставленные эй элементы, для этого оно содержит разные области на экране. Если вам знакома технология Prism, принципиально они очень похожи, но вместо модулей контент помещается в области, MEF достает контент отовсюду где это возможно. Подробнее об этом далее.

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

Шаг 1 — Создание приложения

Тут ничего сложного, просто создайте новое Silverlight приложение. Введите «HelloMEF» в качестве имени.

Оставьте отмеченным пукт «Host the Silverlight application in a new Web Site». Мы используем это позже для разделения XAP.

Шаг 2 — Создание интерфейса панели

Для нашей панели мы создадим что нибудь очень и очень простое. Но так чтобы оно работало. Мы создадим StackPanel с двумя элементами управления, которые будут содержать наши виджеты. Перейдите к вашему MainPage.xaml и вставьте следующее.
<UserControl x:Class=«HelloMEF.MainPage»
xmlns=«http://schemas.microsoft.com/winfx/2006/xaml/presentation»
xmlns:x=«http://schemas.microsoft.com/winfx/2006/xaml»
xmlns:d=«http://schemas.microsoft.com/expression/blend/2008″
xmlns:mc=«http://schemas.openxmlformats.org/markup-compatibility/2006″
mc:Ignorable=«d»
d:DesignHeight=«300» d:DesignWidth=«400»>
<StackPanel x:Name=«LayoutRoot» Background=«Black»>
<Border Background=«Cyan»>
<ItemsControl x:Name=«TopWidgets» Height=«Auto» FontSize=«30»></ItemsControl>
</Border>
<Border Background=«Yellow»>
<ItemsControl x:Name=«BottomWidgets» Height=«Auto» FontSize=«30»></ItemsControl>
</Border>
</StackPanel>
</UserControl>

Шаг 3 Поиск и отображение виджетов — множественный Импорт

Наша панель должна сама заполнять себя виджетами. Первое о чем мы должны подумать это что же такое виджет ;-) В MEF когда у нас есть концепт контракта. Контракт представляет собой нечто что может обеспечивать (exported) или потреблять (imported). Самый легкий путь подумать об этом сейчас это задуматься о типе расширения. В случае нашей панели давайте допустим что каждый виджет это UserControl. Мы могли бы создать новый базовый тип или интерфейс, но нам это не нужно.
Это хорошо сделает сам UserControl.
Итак мы должны указать для MEF что нам нужна коллекция UserControl, те мы хотим импортировать много UserControl. И для этого у нас есть атрибут ImportMany. Для начала добавим ссылки на сборки System.ComponentModel.Composition.dll и  System.ComponentModel.Composition.Initialization.dll. Для поиска их вам необходимо отрыть директорию Silverlight SDK (C:\Program Files\Microsoft SDKs\Silverlight\v4.0\Libraries\Client1). Далее перейдите в code-behind класса MainPage и перепишите класс следующим кодом:

using System;
using System.Windows.Controls;
using System.ComponentModel.Composition;

namespace HelloMEF
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
}

[ImportMany]
public UserControl[] Widgets { get; set; }
}
}

По сути мы говорим «MEF, дай мне виджеты»
Как только мы получим виджеты, мы должны показать их на экране. Сейчас давайте предположим что все виджеты будут запущены в одном ItemsControl. Давайте добавим логику в конструктор.

using System;
using System.Windows.Controls;
using System.ComponentModel.Composition;

namespace HelloMEF
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
foreach (var widget in Widgets)
TopWidgets.Items.Add(widget);
}

[ImportMany]
public UserControl[] Widgets { get; set; }
}
}

Шаг 4 — Создание виджета — Экспорт.

Теперь, когда мы получили базовую конфигурацию панели, давайте создадим виджет. Правым кликом мыши на проекте «HelloMEF» добавьте новый Silverlight UserControl с именем Widget1.

Наш виджет будет очень простой кнопкой.  Откройте Widget1.xaml и вставьте следующий код.

<UserControl x:Class=«HelloMEF.Widget1″
xmlns=«http://schemas.microsoft.com/winfx/2006/xaml/presentation»
xmlns:x=«http://schemas.microsoft.com/winfx/2006/xaml»
xmlns:d=«http://schemas.microsoft.com/expression/blend/2008″
xmlns:mc=«http://schemas.openxmlformats.org/markup-compatibility/2006″
mc:Ignorable=«d»
d:DesignHeight=«300» d:DesignWidth=«400»>
<Grid x:Name=«LayoutRoot» Height=«Auto»>
<Button x:Name=«Button» Content=«Hello MEF!» Width=«300» Height=«100»></Button>
</Grid>
</UserControl>

Теперь необходимо указать MEF что мы хотим обеспечить импорт наших виджетов в элементы типа UserControl. Для того мы экспортируем их. Откройте Widget.xaml.cs и замените его содержимое этим:

using System;
using System.ComponentModel.Composition;
using System.Windows.Controls;
using System.Windows;

namespace HelloMEF
{
[Export(typeof(UserControl))]
public partial class Widget1 : UserControl
{
public Widget1()
{
InitializeComponent();
}
}
}

Как говорилось выше мы добавили атрибут экспорта и указали тип UserControl. Если мы не  будем передавать тип, тогда MEF сделает экспорт Widget1 как он есть, что не имеет особого смысла потому как панель никогда не найдет его! Бывают моменты когда имеет смысл не использовать конкретный тип, но это не тот случай ;-)

Шаг 5 — Сборка панели

Хорошо, мы реализовали нашу Панель, указав импорт и создав виджеты. Это означает что если мы запустим приложение, мы увидим виджеты, так? Неверно ;-) Попробуйте запустить и вы увидите неприятную ошибку.

Что произошло? Наше свойство с коллекцией виджетов пустое, но почему? Причина ли тому то что мы указали MEF сделать с ними что нибудь? Да мы добавили атрибут ImportMany, но для MEF необходимо что-бы наш класс MainPage знал что свойство нужно для предоставления чего нибудь. Что возвращает нас к прошлой концепции.

В MEF необходимо сделать 3 базовых вещи. Во-первых и во-вторых, объявить экспорт и импорт, что мы уже сделали. В-третьих, указать для MEF начать делать компоновку что-бы выполнить все импорты. Места для этого (вы можете сделать это более чем один раз) находятся там где есть один и более импортов и нет экспорта. В данном случае экспорт виджетов мы делаем в классе MainPage, поэтому оно и будет таким местом.

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

В общем когда у вас будут пользовательские контролы созданные в XAML, вы будете использовать этот способ. Указание MEF скомпоновать Silverlight занимает всего 12 строк кода. Это действительно очень простой код и 12 строк строк стоят этого.
Шутка ;-)

Что-бы указать MEF о компоновке вы должны сделать единственный вызов класса  CompositionInitializer.SatisfyImports(this). В терминах MEF все что импортируется или экспортируется мы будем считать Компонентом, which is why the name2. Ниже код который добавлен в MainPage.cs.

using System;
using System.Windows.Controls;
using System.ComponentModel.Composition;

namespace HelloMEF
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
CompositionInitializer.SatisfyImports(this);
foreach (var widget in Widgets)
TopWidgets.Items.Add(widget);
}

[ImportMany]
public UserControl[] Widgets { get; set; }
}
}3

Теперь когда мы запустим приложение, мы увидим наш первый виджет.

Выводы

Хорошо, уже 2:17 АМ4, время ложиться спать.  Предполагаю что разработка приложения растянется на несколько постов. :-)
В этой статье мы увидели основы экспорта, импорта и компоновки в MEF. Мы также коснулись концепции контрактов и компонентов, в которую мы углубимся в будущем.
В следующей статье мы затронем следующие темы:

  • Обеспечение / Доступ к экспорту метаданных.
  • Другие пути импорта и более подробное рассмотрение контрактов.
  • Обеспечение настраиваемого экспорта.

ПС: Это не мой обычный стиль повествования. Я следую примеру моего наставника, гуру и вообще классному парню Брэд Адамс. Пожалуйста не стесняйтесь писать фидбак о том как Я могу стать лучше. Жду с нетерпением продолжения этого путешествия с вами.

Исходники прилагаются

Сноски:

1 В 64 битных операционных системах — C:\Program Files (x86)\Microsoft SDKs\Silverlight

2 Непонятно как перевести.

3 В MEF_Preview 9 PartInitializer переименован в CompositionInitializer

4 02:17 ночи