Пример PRISM и MefBootstrapper или модульное приложение на Silverlight

WPF и Silverlight | создано: 05.12.2010 | опубликовано: 05.12.2010 | обновлено: 13.01.2024 | просмотров: 7817 | всего комментариев: 4

Совершенно недавно вышла новая версия PRISM 4.0 (данное детище выпустила команда Pattern & Practice), которая уже в себя включает не только UnityContainer, но и MefContainer. То есть модульность в приложении может бы реализована не только по средствам Unity, но теперь можно модульность реализовать и при помощи MEF.

Скажу честно, с Unity мне пока не довелось поработать, потому что в силу некоторых обстоятельств у меня не хватило времения для самостоятельного изучения, а в рабочем проекте данная технология не довелось запользовать.

MEF (Manadged Extensability Framwork) - на мой взгляд, одно из самых своевременных, полезных и очень нужных изобретений! Эта технология стала следующей после LINQ, которая заслуживает внимания. В этой статье покажу пример использования PRISM для реализации модульности на примере MefBootstrapper.

Введение

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

Создание проекта

Для начала создадим обыкновенныц Silverlight-проект в Visual Studio. Теперь, первоочередное действие, которое требуется для начала пограммирования с использованием PRISM, это добавление сборок в проект:

1) Microsoft.Practices.Prism.dll
2) Microsoft.Practices.Prism.MefExtensions.dll

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

System.ComponentModel.Composition.dll 
System.ComponentModel.Composition.Initialization.dll

MefBootstrapper

Следующим этапом будет создание файла Bootstrapper.cs и некоторая правка App.cs. Да бы исключить лишние буквы в данной статье, сразу приведу содержание (причем полное) файла Bootstrapper.cs:

public class Bootstrapper : MefBootstrapper
{
    protected override IModuleCatalog CreateModuleCatalog()
    {
        return Modularity.ModuleCatalog.CreateFromXaml(new Uri("/Musorka;component/ModulesCatalog.xaml", UriKind.Relative));
    }

    protected override void ConfigureAggregateCatalog()
    {
        base.ConfigureAggregateCatalog();
        AggregateCatalog.Catalogs.Add(new AssemblyCatalog(this.GetType().Assembly));
    }

    protected override DependencyObject CreateShell()
    {
        return Container.GetExportedValue<Shell>();
    }

    protected override void InitializeShell()
    {
        base.InitializeShell();
        Application.Current.RootVisual = (Shell)this.Shell;
    }
}

Ах, да. Забыл сказать, что проект я назвал Musorka. Ну а теперь несколько коментариев к выше указанному коду. Во-первых, переопределяем CreateModuleCatalog(), в котором получаем конфигурацию каталога из файла ModulesCatalog.xaml. Во-вторых, настраиваем каталог в методе ConfigureAggregateCatalog(). В-третьих, и самое главное переопределяем метод CreateShell(), который и является ключевым моментом. Именное в этом методе и происходит получение импортов MEF. И последнее что мне пришлось сделать, чтобы мое приложение заработало, так это переопределить метод InitializeShell(). В этом методе мы задаем стартовую оболочку.

Теперь про изменения в файле App.cs. Метод Application_Startup стал выглядеть так:

private void Application_Startup(object sender, StartupEventArgs e)
{
    Bootstrapper bootstapper = new Bootstrapper();
    bootstapper.Run();
}           

Ага, этот метод и запускает всю хрень в работу.

Кофигурация модулей из XAML

Конфигурирование модулей это конечно же отдельная тема для статьи, но если принять как один из вариантов, то таким образом можно сократить статью, а значить сконцентрироваться на конкретном примере. Итак, файл конфигурации для моего приложения выглядеть так:

<Modularity:ModuleCatalog
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:sys="clr-namespace:System;assembly=mscorlib"
    xmlns:Modularity="clr-namespace:Microsoft.Practices.Prism.Modularity;assembly=Microsoft.Practices.Prism">
    <Modularity:ModuleInfo
        Ref="Quiz.xap"
        ModuleName="ModuleQuiz"
        ModuleType="Quiz.ModuleQuiz, ModuleQuiz, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
    <Modularity:ModuleInfo
        Ref="Facts.xap"
        ModuleName="ModuleFacts"
        ModuleType="Facts.ModuleFacts, ModuleFacts, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</Modularity:ModuleCatalog>

Вернемся к середине

Если Вы обратили внимание на метод InitializeShell(), то заметили, что есть некое упоминание про Shell. Вот про него и поговорим. После создания проекта, у нас кроме файла App.xaml есть еще один файл MainPage.xaml (плюс еще и код-behind к нему). Есть два способа: 1) переименовать этот самый MainPage.xaml; 2) Удалить этот MainPage.xaml и сздать новый файл (Silverlight User Control) с именем Shell:

[Export]
public partial class Shell : UserControl, INotifyPropertyChanged
{
    [Import(AllowRecomposition = false)]
    public IModuleManager ModuleManager;

    [Import(AllowRecomposition = false)]
    public IRegionManager RegionManager;

    /* скрытый код */

}   

Я хочу про интерфейс INotifyPropertyChanged сказать, что добавил его дабы реализовать уведомление UI об изменениях свойств, но это требуется моему пректу (может и вам тоже пригодится). А что касается двух методов, поверьте, без них не будет ничего работать.

 Итак, основное приложение готово. Это приложение уже умеет находить и собирать в кучу все найденные модули, а значит оно понимает что такое "Modularity". :)

Модули PRISM

Теперь сделаем пару-тройку модулей. У меня их два: ModuleQuiz и ModuleFacts. Это написано в файле конфигурации. Сначало модуль номер один:

namespace Quiz
{
    [ModuleExport(typeof(ModuleQuiz))]
    public class ModuleQuiz : IModule
    {
        [Import]
        public IRegionManager regionManager;

        public void Initialize()
        {
            this.regionManager.RegisterViewWithRegion("ListRegion", typeof(QuizView));
        }
    }
}

и второй номер это:

namespace Facts
{
    [ModuleExport(typeof(ModuleFacts))]
    public class ModuleFacts : IModule
    {
        [Import]
        public IRegionManager regionManager;

        public void Initialize()
        {
            this.regionManager.RegisterViewWithRegion("ListRegion", typeof(FactsView));
        }
    }
}

Ключевым моментом можно назвать реализацию интерфейса IModule и получение импортов. Практически всё то же самое что и с UnityBootstrapper,  только теперь главный контейнер - это MEF.

Заключение

Не вижу необходимости в выкладывании данного проекта в общий доступ. Дело в том, что когда я начинал пробывать сей тест, то я в интернете не нашел ничего подобного (а поиски были продолжительными), и вот именно поэтому я написал статью, чтобы было полегче тем, кто только "на старте". Возможно всеобщими усилиями данная технология будет более полезнее, чем наоборот! 

Я попробывал, и у меня получилось. Я решил поделиться и поэтому выкладываю в интернет. Пусть будет данная статья подспорьем тем, кто хочет начать пользовать PRISM MefBootstrapper.

Комментарии к статье (4)

Была бы статья полнее (с кодом Shell.xaml, где указываются регионы), и с приаттаченным проектом - она была бы очень полезна. А так - вот мне не хватило информации чтобы запустить всё у себя.

Уважаемый, Alex, дело в том, что как только Вы скачаете и "установите" PRISM, у в каталоге, куда он распакуется, будет очень большое количество примеров его использования, в том числе и модульность на основе MefBootstrapper. Я же показал, только как и что нужно добавить в Silverlight-приложение, чтобы получилось нечто похожее на то, что показано в примерах.

И всё равно спасибо за комментарий. Учту при написании будущих статей.

Все таки проект, действительно сдесь был бы к месту. (Потому что примеров действительно много, и они не маленького размера)

Я на днях (если будет время и если не забуду) повторю код из статьи в новом приложении и прикреплю этот проект к статье.