Wednesday, April 30, 2014

Введение в Prism 5. Bootstrapper

Эта статья является продолжением эпопеи об использовании Prism 5 в своих WPF/Silverlight приложениях. В предыдущей статье Введение в Prism 5 уже был рассмотрен пример с использованием возможностей набора библиотек Prism. Но отличие предыдущего примера от того, который мы рассмотрим сейчас, заключается в том, что пример, который приведен по ссылке выше, написан для того чтобы вы могли взглянуть на Prism как замену разным MVVM тулкитам. Эта тема отличается тем, что упор будет сделан на использование загрузчика (Bootstrapper). Компонент загрузчика используется приложением для инициализации различных компонентов и служб Prism. Он используется для инициализации DI контейнера, регистрации служб и компонентов уровня приложения. Он также используется для настройки и инициализации модуля каталогов, представления оболочки и её модели представления или Presenter. По умолчанию в Prism 5 включает два загрузчика: UnityBootstrapper для работы с IoC контейнером Unity и MefBootstrapper для использования в приложении библиотеки Managed Extensibility Framework (MEF). Можно также реализовать свой bootstapper, если вы используете какой-то другой IoC контейнер. А проще всего скачать готовые расширения через NuGet Packages.
Построение приложений с помощью Prism 5 не изменилось. Оно представляет из себя такой процесс:
Для новичков эта картинка выглядит страшновато. Но на самом деле там нет ничего особо сложного. В призме есть некоторые термины, которые вам нужно знать. Приведу перевод тех терминов с официальной документации, которые мы будем использовать для нашего приложения.
Оболочка (Shell) – это хост-приложение, в которое загружаются модули. Оболочка определяет расположение элементов пользовательского интерфейса и структуру приложения, но обычно не знает о том, какие модули она будет использовать. Она обычно реализует общие прикладные службы и инфраструктуру, но большая часть функциональности приложения реализована внутри модулей. Оболочка также обеспечивает высокоуровневое окно или визуальный элемент, который размещает различные компоненты UI, поставляемые загруженными модулями.
Представления (Views) – это элементы управления UI, которые инкапсулируют пользовательский интерфейс для определенной функции или функциональной области приложения. Представления используются вместе с MVVM или MVP паттерном, чтобы обеспечить разделение ответственности между UI, логикой представления и данными. Представления используются, чтобы инкапсулировать UI и определить поведение взаимодействия с пользователем, таким образом, позволяя представлению быть обновленными или замененными независимо от базовой функциональности приложения. Представления используют привязку данных, чтобы взаимодействовать с моделью представления или классами презентатора.
Регионы (Regions) – это логические заполнители, определенные в пределах пользовательского интерфейса приложения (в оболочке или в представлениях), в которых выводятся на экран представления. Регионы позволяют обновлять внешний вид UI, не требуя изменений в логике приложения. В качестве регионов могут использоваться такие элементы управления, как ContentControl, ItemsControl, ListBox, или TabControl, позволяя представлениям автоматически выводиться на экран в виде контента этих элементов. Представления могут быть выведены на экран в пределах региона программно или автоматически. Prism также предоставляет поддержку навигации с использованием регионов. Регионы могут быть заданы через компонент RegionManager, который использует адаптеры регионов (RegionAdapter) и поведения регионов (RegionBehavior) для координации отображения представлений в пределах определенных регионов.
Пока мы рассмотрим только использование оболочки Shell и UnityBootstrapper. Для этого создадим простое WPF-приложение и назовем его PrismBootstrapperSample.
Я предпочитаю сразу в проекте создавать структуру, которую использую в дальнейшем для паттерна MVVM.
В полнофункциональном бизнес-приложении с использованием Prisme 5 ваша архитектура будет разбита на большее количество частей. Добавятся папки или библиотеки для работы с регионами (Regions), с составными командами (CompositeCommands) и для модулей (Modules). Преждевременно не беспокойтесь, все это мы использовать пока не будем. Поэтому приведенной выше структуры проекта более чем достаточно для классического паттерна MVVM для призма.
Затем наш класс MainWindow.xaml мы переименуем в Shell.xaml и переместим в нашу папку Views, если вы раньше этого не сделали. Затем смотрим, чтобы наш класс стал иметь следующий вид. Примечание: укажите также правильно namespace, для того чтобы у вас была четкая логика работы.
namespace PrismBootstrapperSample.Views
{
       /// <summary>
       /// Interaction logic for MainWindow.xaml
       /// </summary>
       public partial class Shell : Window
       {
             public Shell()
             {
                    InitializeComponent();
             }
       }
}
Следующим этапом мы зайдем непосредственно в наш класс Shell.xaml и исправим название класса.
x:Class="PrismBootstrapperSample.Views.Shell"
После проделанных действий добавим в наше окно, например, контрол TextBlock.
<Window x:Class="PrismBootstrapperSample.Views.Shell"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <TextBlock>Using UnityBootstrapper in Prism 5</TextBlock>
    </Grid>
</Window>
Нам необходимо убрать это окно явно при старте приложения с файла App.xaml. Для этого переходим в данный файл и находи там строку
StartupUri="MainWindow.xaml"
Эту строку нужно удалить. Файл должен быть такой, как на примере ниже.
<Application x:Class="PrismBootstrapperSample.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Application.Resources>
        
    </Application.Resources>
</Application>
На этом работа с внешним видом пока закончена. Чтобы проверить, что все компилируется, сделаем Rebuild для нашего приложения.
Если все прошло успешно, то переходим к следующему этапу с установкой библиотек Prism 5 в свой проект, если же у вас что-то не получилось, то вы, вероятно, допустили ошибку с переименованием. Тогда просто удалите ваше окно Shell.xaml, нажмите на папку Views правой клавишей и выберите создание нового окна.
Назовите его Shell.xaml, как и старое окно.
Затем в созданное окно можете добавить какой-либо контрол, или с примера выше – TextBlock с текстом. После проделанных действий приступим к установке библиотек Prism. Раньше для этой цели нам приходилось устанавливать Prism, а только затем копировать библиотеки в проект. С выходом NuGet Packages эта процедура намного упростилась. Для этого просто в NuGet Packages введем в поиске слово "Prism".
Если же вы предпочитаете использовать Package Manager Console, то вам будет достаточно указать команду
PM> Install-Package Prism
Приступим к созданию нашего класса Bootstrapper, который наследуем от UnityBootstrapper. Только есть одна маленькая проблема. Разработчики нового Prism 5 почему-то убрали UnityBootstrapper и MefBootstrapper, и теперь их нужно устанавливать отдельно, как и другие расширения для Prism. Для этого перейдем в NuGet Packages и найдем Prism.UnityExtensions.
После этого установим данное расширение. У вас будет установлен кроме данного расширения также IoC контейнер Unity для управления внедрением зависимостей.
Дальше нам нужно будет указать пространство имен,
using Microsoft.Practices.Prism.UnityExtensions;
чтобы мы могли использовать наш Bootstrapper.
public class Bootstrapper : UnityBootstrapper
{
       protected override DependencyObject CreateShell()
       {
             throw new NotImplementedException();
       }
}
По умолчанию нам будет предложено создать заглушку для метода CreateShell(), что и было сделано в примере. Для того чтобы использовать созданную нами оболочку Shell.xaml, мы можем пойти двумя путями. Первый – использовать контейнер Unity. Для этого добавим пространство имен 
using Microsoft.Practices.Unity;
Затем использовать методом Resolve данного контейнера
public class Bootstrapper : UnityBootstrapper
{
       protected override DependencyObject CreateShell()
       {
             return Container.Resolve<Shell>();
       }
}
Либо вариант номер два с использованием паттерна ServiceLocator, который вы, наверняка, уже видели в примерах с Prism 5 или более ранних версий.
Заметка. Вы будете часто видеть, что для получения экземпляров типов, вместо определенного контейнера внедрения зависимости, используется ServiceLocator. ServiceLocator реализован так, что перенаправляет вызовы контейнеру, поэтому его использование является хорошим выбором для написания кода, не зависящего от выбранного контейнера. Можно также ссылаться и использовать непосредственно контейнер, а не ServiceLocator.

Для использования ServiceLocator нужно добавить пространство имен
using Microsoft.Practices.ServiceLocation;
Реализация будет выглядеть, как в примере ниже.
public class Bootstrapper : UnityBootstrapper
{
       protected override DependencyObject CreateShell()
       {
             return ServiceLocator.Current.GetInstance<Shell>();
       }
}
Используйте тот вариант, который вам больше по душе. Для данного примера я не вижу преимущества использования ServiceLocator. Следующим шагом для запуска нашего приложения нам нужно в классе Bootstrapper переопределить метод InitializeShell().
protected override void InitializeShell()
{
       Application.Current.MainWindow = (Window)Shell;
       Application.Current.MainWindow.Show();
}
Весь код будет иметь следующий вид:
public class Bootstrapper : UnityBootstrapper
{
       protected override DependencyObject CreateShell()
       {
             return ServiceLocator.Current.GetInstance<Shell>();
       }

       protected override void InitializeShell()
       {
             Application.Current.MainWindow = (Window)Shell;
             Application.Current.MainWindow.Show();
       }
}
Теперь осталось использовать наш созданный класс. Для этого перейдем в класс App.xaml.cs и переопределим метод OnStartup.
protected override void OnStartup(StartupEventArgs e)
{
       Bootstrapper bootstrapper = new Bootstrapper();
       bootstrapper.Run();
}
Когда мы проделали все эти действия, можем запустить наш проект и посмотреть, что у нас получилось.

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