Silverlight

Составные веб-приложения Prism

Шон Вайлдермут (Shawn Wildermuth)

Код загрузки доступны коллекции кода MSDN
Просмотреть код в интерактивном режиме

В этой статье обсуждаются:

  • Silverlight версии 2
  • Архитектура приложений
  • Введение зависимостей.
В данной статье использованы следующие технологии:
Silverlight 2 призмы

Содержание

Почему призмы?
Знакомство с введением зависимостей
Настройка запуска
Modularity
Создание пользовательского ИНТЕРФЕЙСА
Объединение событий
Команды делегата
Подведение итогов

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

Проблемы возникают, однако при попытке применить тесно связанных стиль больших приложений. При увеличении количества перемещения частей простой стиль разработки приложений попадает друг от друга. Часть рекомендация послойное размещение (см. мою статью "модель представление ViewModel В приложения Silverlight 2"), но тесно связанных архитектуры является только одним ряд проблем, которые должны быть устранены в больших проектах Silverlight.

В этой статье показано, как построить приложение, используя методы композиции составные приложения библиотеки из проекта призмы. Пример, я разработки — это простой редактор базы данных.

Изменения требований и прорабатывать проекта, полезно при изменении частей приложения без необходимости изменения каскад во всей системе. Modularizing приложения позволяет создавать компоненты приложения отдельно (и слабосвязанной) и изменения всей части приложения не влияя на остальной части кода.

Кроме того не следует загружать все части приложения одновременно. Представьте себе приложение управления клиента, в котором пользователям войти в систему и может затем управление их конвейера перспективный клиент, а также проверять электронную почту с любого из их перспективных клиентов. Если пользователь проверяет электронной почты несколько раз в день но управляет конвейера только каждый день или две причины загрузки кода для управления конвейера, пока понадобится? Было бы хорошо, если приложение поддерживает загрузку по требованию части приложения, ситуации, могут быть решены, modularizing приложения.

Шаблоны &Группа рекомендации корпорации Майкрософт создан проект, называемые призмы (CompositeWPF) предназначен для проблем адрес подобных приложений Windows Presentation Foundation (WPF) и обновления призмы для поддержки Silverlight также. Пакет призмы является сочетание структуры и руководство по созданию приложений. Платформы, называется компонент приложения библиотеки (CAL), включает следующее:

  • Модульность приложения: Создание приложений из секционированных компонентов.
  • Архитектура пользовательского ИНТЕРФЕЙСА: Позволяет слабосвязанной компоненты формы пользовательских интерфейсов без дискретные знания остальной части приложения.
  • Расположение службы: Отдельные горизонтальной службы (например, ведение журнала и проверки подлинности) из вертикальной служб (бизнес-логики) для продвижения новой послойного размещения приложения.

CAL записывается с эти же принципы проектирования, помнить и разработчикам приложений является платформу стиле buffet — что, необходимо, и оставьте остальные. Рисунок 1 показана базовая схема ЛИЦЕНЗИИ для собственного приложения.

Рисунок 1 составные приложения библиотеки

CAL поддерживает эти службы для упрощения составления приложения из небольших частей. Это означает CAL дескрипторы, какие части загружаются (и когда) также обеспечивает базовые функциональные возможности. Можно решить, какие из этих возможностей помогут сделать ваши задания и которого может быть получена образом.

Мой пример в этой статье использует в качестве объем ЛИЦЕНЗИИ максимально. Это приложение оболочки, CAL для загрузки нескольких модулей во время выполнения, поместите представления в области (как показано на рис. 2) и службы поддержки. Но прежде чем мы получить код, необходимо понять некоторые основные понятия о введение зависимостей (также называемые инверсия управления или IoC). Многие функции CAL полагаться на введение зависимостей, поэтому понимание основ поможет разрабатывать архитектуру проект Silverlight с призмы.

fig03.gif

Рисунок 2 архитектура составных приложений

Знакомство с введением зависимостей

В типичной разработки проекта начинается с точки входа (исполняемый файл, страницу default.aspx и т. д.). Может разработать приложение как один гигантский проект, но в большинстве случаев некоторый уровень модульности существует, приложение загружает число сборок, которые являются частью проекта. Основная сборка знает, какие сборки должен и создает жесткие ссылки на эти фрагменты. Во время компиляции основного проекта знает о указанные сборки и пользовательский интерфейс состоит из статических элементов управления. Приложение находится в элементе управления кода, которые требуется и обычно знает весь код может использовать. Это становится проблемой, однако, поскольку разработки выполняется внутри проекта главного приложения. Время построения по мере роста в объединенный приложение и конфликтующих изменений может замедлить разработки.

Введение зависимостей цель — отменить эту ситуацию, инструкциями, зависимости во время выполнения. Вместо проекта, управление этих зависимостей часть кода вызывается контейнером является ответственным за их внедрение.

Но почему это важно? Во-первых modularizing коде следует упрощают для тестирования. Возможность замены зависимостей проекта позволяет очистителя тестирования таким образом, что только код, проверяемое может быть источник сбоя вместо кода, где-нибудь в вложенных цепочке зависимостей теста. Ниже приведен конкретный пример. Представьте, что имеется компонент другим разработчикам использовать для поиска адресов для конкретной компании. Компонент зависит от компонент доступа данных, который извлекает данные для вас. При тестировании компонента, начните с тестирования в базе данных и некоторые тесты сбой. Но поскольку постоянно изменения схемы и построение базы данных, вы не знаете ли тесты сбои из-за свой собственный код или кода доступа к данным. Ваш компонент жесткого зависимость от данных становится ненадежный компонент доступа, тестирование приложения и причины churn при отслеживании ошибок в коде или в коде другого.

Компонент может выглядеть следующим образом:

public class AddressComponent
{
  DataAccessComponent data = new DataAccessComponent();

  public AddressComponent()
  {
  }

  ...
}

Вместо hard-wired компонента может принимать интерфейс, который представляет доступ к данным, как показано ниже:

public interface IDataAccess
{
  ...
}

public class AddressComponent
{
  IDataAccess data;

  public AddressComponent(IDataAccess da)
  {
    data = da;
  }

  ...
}

Обычно интерфейс используется, поэтому можно создавать версии, которая позволяет настраивать коде. Этот подход часто называется "макетирования." Макетирование означает создание реализацию зависимость, фактически не представляет реальные версии. Буквально вы создаете в макеты реализации.

Такой подход лучше, поскольку зависимость (IDataAccess) могут быть добавлены в проект во время создания объекта. Реализацию компонента IDataAccess будет зависеть от требования (тестирования или реальном).

По существу это как введение зависимостей работает, но как обрабатывается введения? Задание контейнера является обработка создания типов, которые она позволяет зарегистрировать типов и разрешении их. Например предположим, что имеется устойчивый класс, реализующий интерфейс IDataAccess. Во время запуска приложения можно сообщить контейнера для регистрации типа. Любое else в приложении, где необходимо типа, в контейнере, можно попросить разрешить тип, как показано ниже:

public void App_Startup()
{
  container.RegisterType<IDataAccess, DbDataAccess>();
}

...

public void GetData()
{
  IDataAccess acc = container.Resolve<IDataAccess>();
}

В зависимости от ситуации (тестирования или производства), могут замены реализации IDataAccess изменив регистрации. Кроме того контейнер может обрабатывать конструкции введения зависимостей. Если объект, который должна быть создана в контейнере конструктор принимает интерфейс контейнера, можно разрешить, он разрешает типа и передает его конструктору, как показано на рис. 3.

Тип разрешения на рис. 3 контейнером

public class AddressComponent : IAddressComponent
{
  IDataAccess data;

  public AddressComponent(IDataAccess da)
  {
    data = da;
  }
}

...

public void App_Startup()
{
  container.RegisterType<IAddressComponent, AddressComponent>();
  container.RegisterType<IDataAccess, DbDataAccess>();
}

public void GetAddresses()
{
  // When we ask the container to create the AddressComponent,
  // it sees that a constructor takes a IDataAccess object
  // so it automatically resolves that dependency
  IAddressComponent addr = container.Resolve<IAddressComponent>();
}

Обратите внимание, AddressComponent конструктор использует реализацию IDataAccess. Когда конструктор создает класс AddressComponent при разрешении, он автоматически создает экземпляр IDataAccess и передает его AddressComponent.

При регистрации типов с контейнером также сообщать контейнера для работы со временем жизни тип специальные способами. Например при работе с компонентом ведения журнала можно рассматривать как одноэлементный так, чтобы каждая часть приложения, которое требуется ведение журнала не получает собственную копию (это поведение по умолчанию). Чтобы сделать это, предоставлять реализацию абстрактного класса LifetimeManager. Поддерживается несколько диспетчеров время жизни. ContainerControlledLifetimeManager является одноэлементного множества каждого процесса и PerThreadLifetimeManager одноэлементный каждого потока. Для ExternallyControlledLifetimeManager контейнера содержит слабую ссылку одноэлементный. Если объект освобождается извне, контейнер создает новый экземпляр, в противном случае возвращает live объект, содержащийся в слабую ссылку.

Используйте класс LifetimeManager, указав при регистрации типа. Приведем пример:

container.RegisterType<IAddressComponent, AddressComponent>(  new ContainerControlledLifetimeManager());

В CAL, контейнер IoC основана на платформе единицы из шаблонов &Группа рекомендации. В следующих примерах я буду использовать контейнер единицы, но существуют также некоторого числа альтернатив открытым исходным кодом в контейнер IoC единицы, например Ninject, Spring.NET, Castle и StructureMap. Если вы знакомой с и уже используется контейнер IoC отличное от единицы, можно предоставить свой собственный контейнер (хотя и займет немного больше усилий).

Настройка запуска

Обычно в приложении Silverlight, поведение при запуске достаточно создать класс главной страницы XAML и присвоить его свойству RootVisual приложения. В составных приложениях это работать по-прежнему необходим, но вместо создания класса страницы XAML, составное приложение обычно использует загрузчик класса для обработки поведение при запуске.

Для запуска, требуется новый класс, производный от класса UnityBootstrapper. Этот класс является в сборке Microsoft.Practices.Composite.UnityExtensions. Загрузчик содержит переопределяемые методы, которые обрабатывают различные части поведение при запуске. Часто не переопределять каждый метод запуска, только единиц необходимости. Два метода, необходимо переопределить являются CreateShell и GetModuleCatalog.

Метод CreateShell является, где был создан основной класс XAML. Обычно это называется оболочки, поскольку визуальный контейнер для компонентов приложения. Мой пример включает загрузчика, создает новый экземпляр класса оболочки и присваивает его RootVisual перед возвратом этот новый класс оболочки, как показано ниже:

public class Bootstrapper : UnityBootstrapper
{
  protected override DependencyObject CreateShell()
  {
    Shell theShell = new Shell();
    App.Current.RootVisual = theShell;
    return theShell;
  }

  protected override IModuleCatalog GetModuleCatalog()
  {
    ...
  }
}

Метод GetModuleCatalog, я объясню, в следующем разделе возвращает список модулей для загрузки.

Теперь, когда загрузчик классов можно использовать в методе запуска приложения Silverlight. Обычно создайте новый экземпляр класса загрузчика и вызовите его метод Run, как показано на рис. 4.

На рисунке 4 при создании экземпляра загрузчика

public partial class App : Application
{

  public App()
  {
    this.Startup += this.Application_Startup;
    this.Exit += this.Application_Exit;
    this.UnhandledException += this.Application_UnhandledException;

    InitializeComponent();
  }

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

  ...
}

Загрузчик является также участвующих в Регистрация типов с контейнером, различные части требования приложения. Чтобы добиться этого, переопределите метод ConfigureContainer загрузчика. Это дает возможность зарегистрировать все типы, которые будут использоваться в остальной части приложения. Figure 5 shows the code.

Регистрация типов на рис. 5

public class Bootstrapper : UnityBootstrapper
{
  protected override void ConfigureContainer()
  {
    Container.RegisterType<IShellProvider, Shell>();
    base.ConfigureContainer();
  }

  protected override DependencyObject CreateShell()
  {
    // Get the provider for the shell
    IShellProvider shellProvider = Container.Resolve<IShellProvider>();

    // Tell the provider to create the shell
    UIElement theShell = shellProvider.CreateShell();

    // Assign the shell to the root visual of our App
    App.Current.RootVisual = theShell;

    // Return the Shell
    return theShell;
  }

  protected override IModuleCatalog GetModuleCatalog()
  {
    ...
  }
}

Здесь код регистрирует интерфейс для класса, реализующего интерфейс IShellProvider, который в нашем примере создается и не является частью платформы ЛИЦЕНЗИИ. Таким образом можно использовать его в нашей реализации метода CreateShell. Мы можно устранить интерфейс и затем использовать для создания экземпляра оболочки, чтобы мы могли назначить его RootVisual и вернуть его. Эту методологию может показаться дополнительную работу, но как углубимся в как CAL помогает построить приложение становится ясно как этот загрузчик помогая.

В типичной среде .NET сборка является основной единицей работы. Такое обозначение диапазона позволяет разработчикам работать на свой код отдельно друг от друга. В CAL каждый из этих единиц работы является модулем и для ЛИЦЕНЗИИ для использования модуля ему класса, могут взаимодействовать в модуле поведение при запуске. Этот класс также требуется для поддерживает интерфейс IModule. Интерфейс IModule требует один метод, именуемый инициализации позволит модулю настроить сам может использоваться в остальной части приложения. Пример включает ServerLogger модуль, содержащий возможности ведения журнала для нашего приложения. Класс ServerLoggingModule поддерживает интерфейс IModule, как показано ниже:

public class ServerLoggerModule : IModule
{
  public void Initialize()
  {
    ...
  }
}

Проблема в том что мы не знаем нам нужно инициализировать в нашем модуле. Поскольку это модуль ServerLogging кажется логических необходимо зарегистрировать тип, что ведение журнала для нас. Мы хотим использовать контейнер для регистрации типа побеждает требуется средство ведения журнала можно просто использовать наш реализации не зная точного типа ведения журнала, он выполняет.

Мы получили контейнера, создав конструктор, принимающий интерфейс IUnityContainer. Если вспомнить обсуждение введение зависимостей контейнера используется для добавления типов, известно о введения конструктор. IUnityContainer представляет контейнер в нашем приложении, поэтому, если мы добавить этот конструктор, мы может затем сохранить его и использовать его в нашей инициализации как так:

public class ServerLoggerModule : IModule
{
  IUnityContainer theContainer;

  public ServerLoggerModule(IUnityContainer container)
  {
    theContainer = container;
  }

  public void Initialize()
  {
    theContainer.RegisterType<ILoggerFacade, ServerBasedLogger>(
      new ContainerControlledLifetimeManager());
  }
}

После инициализации этого модуля отвечает для реализации ведения журнала для приложения. Но как этот модуль загрузка?

При использовании CAL для создания приложения, необходимо создать ModuleCatalog, содержащий все модули для приложения. Можно создать этот каталог, переопределение вызов загрузчика GetModuleCatalog. В Silverlight можно заполнить этот каталог с помощью кода или XAML.

С кодом создать новый экземпляр класса ModuleCatalog и заполнить его модулей. Например рассмотрим это:

protected override IModuleCatalog GetModuleCatalog()
{
  var logModule = new ModuleInfo()
  {
    ModuleName = "ServerLogger",
    ModuleType =       "ServerLogger.ServerLoggerModule, ServerLogger, Version = 1.0.0.0"
  };

  var catalog = new ModuleCatalog();
  catalog.AddModule(logModule);

  return catalog;
}

Здесь я просто добавить один модуль, называемый ServerLogger, тип, определенный в свойстве ModuleInfo ModuleType. Кроме того можно указать зависимости между модулями. Поскольку некоторые модули могут зависят другие, с помощью зависимостей помогает каталога знать порядок, в котором для переноса в зависимости. С помощью свойства ModuleInfo.DependsOn можно указать, какие модули именованных необходимы для загрузки другой модуль.

Можно загрузить каталог непосредственно из файла XAML, как показано ниже:

protected override IModuleCatalog GetModuleCatalog()
{
  var catalog = ModuleCatalog.CreateFromXaml(new Uri("catalog.xaml", 
                                                     UriKind.Relative));
  return catalog;
}

XAML-файл содержит сведения о одного и того же типа, можно создать с помощью кода. Преимуществом использования XAML является, его можно изменить на ходу. (Представьте получение XAML-файл с сервера или из другого расположения на основе на какой пользователь вошел в систему.) В рис. 6 показан пример файла catalog.xaml.

Образец файла Catalog.xaml 6 A рисунок

<m:ModuleCatalog 
  xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:sys="clr-namespace:System;assembly=mscorlib"
  xmlns:m="clr-namespace:Microsoft.Practices.Composite.Modularity; 
    assembly=Microsoft.Practices.Composite">
  <m:ModuleInfoGroup InitializationMode="WhenAvailable">
    <m:ModuleInfo ModuleName="GameEditor.Client.Data"
        ModuleType="GameEditor.Client.Data.GameEditorDataModule, 
          GameEditor.Client.Data, Version=1.0.0.0"/>
    <m:ModuleInfo ModuleName="GameEditor.GameList"
      ModuleType="GameEditor.GameList.GameListModule,
      GameEditor.GameList, Version=1.0.0.0"
      InitializationMode="WhenAvailable">
      <m:ModuleInfo.DependsOn>
        <sys:String>GameEditor.Client.Data</sys:String>
      </m:ModuleInfo.DependsOn>
    </m:ModuleInfo>
  </m:ModuleInfoGroup>
</m:ModuleCatalog>

В этот каталог XAML группы включает двух модулей и второй модуль зависит от первого. Можно использовать определенные XAML каталога, на основании ролей или разрешения, как можно с помощью кода.

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

Это происходит, когда этой услугой становится indispensible Silverlight. Хотя единица работы сборки, можно указать файл .XAP, содержащий модуля или модулей. Для этого необходимо указать значение ссылка ModuleInfo. На значение Ref — путь к .XAP файл, содержащий модуль:

protected override IModuleCatalog GetModuleCatalog()
{
  var logModule = new ModuleInfo()
  {
    ModuleName = "ServerLogger",
    ModuleType =
      "ServerLogger.ServerLoggerModule, ServerLogger, Version= 1.0.0.0",
    Ref = "ServerLogger.xap"
  };

  var catalog = new ModuleCatalog();
  catalog.AddModule(logModule);

  return catalog;
}

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

.Xap файлов, содержащих несколько модулей можно создать ModuleGroup, содержащего набор объектов ModuleInfo и задать Ref ModuleGroup, загрузить эти модули из файла одним .XAP:

var modGroup = new ModuleInfoGroup();
modGroup.Ref = "MyMods.xap";
modGroup.Add(logModule);
modGroup.Add(dataModule);
modGroup.Add(viewModule);

var catalog = new ModuleCatalog();
catalog.AddGroup(modGroup);

Для приложений Silverlight это способ составлять приложения из нескольких файлов .XAP, позволяющий версии различных разделов составного приложения отдельно.

При создании модулей Silverlight, размещает в файле .XAP, создайте приложение Silverlight (не библиотеке Silverlight). Затем можно ссылаться на все проекты модуля, следует поместить в файле .XAP. Необходимо удалить файлы app.xaml и page.xaml, так как этот файл .XAP не будет быть загрузки и выполнения как файл обычно .XAP. Файл .XAP является просто контейнером (может быть ZIP-файл, не имеет значения). Кроме того, если ссылаются проекты, которые уже ссылка в главном проекте, можно изменить эти ссылки на Copy Local = false в окне свойств, поскольку не требуется сборки в файле .XAP (главного приложения уже загружен, чтобы каталог не будет пытаться загрузить их еще раз.)

Однако загрузка огромный приложения с несколькими вызовами по проводному соединению не похоже может помочь производительности. Это происхождения ModuleInfo InitializationMode свойство в игру. InitializationMode поддерживает два режима: WhenAvailable, в котором .XAP файл загружается асинхронно и затем инициализировать (это поведение по умолчанию) и OnDemand, в котором .XAP загружается при явным образом запрошены. Поскольку каталог модуля не знает типы в модулях до инициализации, разрешения типов, которые инициализируются с помощью OnDemand произойдет сбой.

По требованию поддержку модули и группы позволяет загрузить некоторые функциональные возможности в большом размере приложения по мере необходимости. Ускорением время запуска и другие необходимый код может быть загружен как пользователи взаимодействуют с приложением. Эта функция очень, следует использовать при наличии авторизации для разделения частей приложения. Пользователям необходимы только несколько частей приложения не нужно загружать код, они никогда не будете использовать.

Загрузить модуль по требованию, необходим доступ к интерфейсу IModuleManager. Наиболее часто можно запросить это в конструкторе класса, который необходимо загрузить модуль по требованию. После этого использовать IModuleManager загрузить модуль, вызвав LoadModule, как показано на рис. 7.

Телефонная LoadModule на рис. 7

public class GameListViewModel : IGameListViewModel
{
  IModuleManager theModuleManager = null;

  public GameListViewModel(IModuleManager modMgr)
  {
    theModuleManager = modMgr;
  }

  void theModel_LoadGamesComplete(object sender, 
                                  LoadEntityCompleteEventArgs<Game> e)
  {
    ...

    // Since we now have games, let's load the detail pane
    theModuleManager.LoadModule("GameEditor.GameDetails");
  }
}

Модули являются просто единицей modularization в приложениях. В Silverlight обрабатывать модуль гораздо, будет проект библиотеки, но дополнительные работе инициализации модуля отделения модули из главного проекта.

Создание пользовательского ИНТЕРФЕЙСА

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

CAL поддерживает определение области непосредственно в XAML с помощью вложенного свойства класса RegionManager. Это свойство позволяет указать областей в вашей оболочки и указать представления не должно быть размещено в области. Например наши оболочки имеет две области, LookupRegion и DetailRegion, как показано ниже:

<UserControl 
  ...
  xmlns:rg=
    "clr-namespace:Microsoft.Practices.Composite.Presentation.Regions;
    assembly=Microsoft.Practices.Composite.Presentation">
  ...
  <ScrollViewer rg:RegionManager.RegionName="LookupRegion" />
  <ScrollViewer rg:RegionManager.RegionName="DetailRegion" />
</UserControl>

RegionName может быть применен к ItemsControl и его производных элементов управления (например, ListBox);Селектор и его производных элементов управления (например, TabControl);ContentControl и его производных элементов управления (например, ScrollViewer).

После определения областей, можно направить модули для загрузки их представления в области с помощью интерфейса IRegionManager, как показано ниже:

public class GameListModule : IModule
{
  IRegionManager regionManager = null;

  public GameListModule(IRegionManager mgr)
  {
    regionManager = mgr;
  }

  public void Initialize()
  {
    // Build the View
    var view = new GameListView();

    // Show it in the region
    regionManager.AddToRegion("LookupRegion", view);
  }
}

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

Поведение области могут быть различными в зависимости от типа элемента управления размещенные. В примере ScrollViewer, что один и только одно представление могут быть добавлены в область. В отличие от ItemControl области позволяют несколько представлений. По мере добавления каждого представления он показывает как новый элемент в элемент управления ItemsControl. Это средство упрощает создание функциональные возможности, как панели мониторинга.

При использовании MVVM шаблон для определения представлений смешивать регионов и расположение службы аспекты контейнер представлений и просмотр моделей относительно друг друга и позволяют объединить их во время выполнения модуля. Например при изменении GameListModule, я могут зарегистрировать представления и представления модели контейнера и объединить их перед применением представления области, как показано на рис. 8.

На рисунке 8 объединение представлений в области

public class GameListModule : IModule
{
  IRegionManager regionManager = null;
  IUnityContainer container = null;

  public GameListModule(IUnityContainer con, IRegionManager mgr)
  {
    regionManager = mgr;
    container = con;
  }

  public void Initialize()
  {
    RegisterServices();

    // Build the View
    var view = container.Resolve<IGameListView>();

    // Get an Implemenation of IViewModel
    var viewModel = container.Resolve<IGameListViewModel>();

    // Marry Them
    view.ApplyModel(viewModel);

    // Show it in the region
    regionManager.AddToRegion("LookupRegion", view);
  }

  void RegisterServices()
  {
    container.RegisterType<IGameListView, GameListView>();
    container.RegisterType<IGameListViewModel, GameListViewModel>();
  }
}

Такой подход позволяет использовать ИНТЕРФЕЙС композиции сохраняя строгой разделение MVVM.

Объединение событий

После нескольких представлений в приложениях через ИНТЕРФЕЙС композицию, сталкиваются типичная проблема. Даже если построения независимых представлений для поддержки более тестирования и разработки существуют часто точки касания, где представления не может быть полностью изолирован. Они являются логически сочетании поскольку им необходимо взаимодействовать, но вы хотите сохранить их как слабо максимально независимо от логического увязки.

Чтобы включить слабых соединений и связи между представления, CAL поддерживает службу, именуемую статистической обработки событий. Статистической обработки событий разрешает доступ к другой части кода для издателей и потребителей глобальные события. Такой доступ предоставляет простой способ связи без тесно связаны и осуществляется с помощью интерфейса IEventAggregator CAL. IEventAggregator позволяет публиковать и подписываться на события в различных модулях приложения.

Прежде чем возможность связи необходимо класс, производный от EventBase. Обычно создается простой событие, которое является производным от CompositePresentationEvent < T >класс. Универсальный класс позволяет указать полезных данных вы собираетесь опубликовать событие. В этом случае GameListViewModel собирается опубликовать событие после выбора игры, других элементов управления, необходимо изменить контексте, как пользователь выбирает одну из игр можно подписаться на событие. Наш класс событий выглядит следующим образом:

public class GameSelectedEvent : CompositePresentationEvent<Game>
{
}

После определения события агрегации событий можно опубликовать событие путем вызова его метода GetEvent. Получает событие одноэлементный, собирается выполнить статистическую обработку. Первый, кто вызывает этот метод создает singleton. Из события могут вызвать метод опубликовать, чтобы создать событие. Публикация событие похоже обработки события. Необходимо опубликовать событие, пока ему необходимо отправить данные. Например при выборе игру в GameList в нашем примере публикует выбранного игры, используя новое событие:

// Fire Selection Changed with Global Event
theEventAggregator.GetEvent<GameSelectedEvent>().Publish(o as Game);

В других частях составного приложения можно подписаться на событие, чтобы быть вызван после публикации события. Метод Subscribe событие позволяет указать метод, вызываемый при публикации события, параметр, позволяет запрашивать потоков семантику для вызова события (например, в поток интерфейса пользователя часто используется), и необходимость на статистическую функцию удерживайте ссылку переданные в данные так, чтобы не мусора:

// Register for the aggregated event
aggregator.GetEvent<GameSelectedEvent>().Subscribe(SetGame, 
                                                  ThreadOption.UIThread, 
                                                  false);

В качестве подписчика можно задать фильтры, которые вызываются только в конкретных ситуациях. Представьте, что событие, которое возвращает состояние приложения и фильтр, который вызывается только во время определенных состояниям данных.

Агрегации событий позволяет иметь связь между модулями, не вызывая тесную связь. Если подписаться на событие, которое никогда не публикуется или опубликовать событие, которое никогда не подписана, код никогда не произойдет.

Команды делегата

В Silverlight (в отличие от WPF) true инфраструктуры команд не существует. Это заставляет использование с выделенным кодом в представлениях задач будет осуществляться более легко непосредственно в XAML, с помощью команд инфраструктуры. До Silverlight поддерживает это средство, CAL поддерживает класс, который помогает решить эту проблему: DelegateCommand.

Чтобы начать работу с DelegateCommand, необходимо определить DelegateCommand в вашей ViewModel, таким образом, можно осуществить данных привязку к нему. В ViewModel следует создать новый DelegateCommand. DelegateCommand ожидает тип данных, передаваемых ему (часто объекта если данных используется) и одного или двух методов обратного вызова (или функций лямбда). Первый из этих методов является действие для выполнения при запускается команда. При необходимости можно указать второй обратный вызов вызываться для проверки создается команда. Идея является включение, отключение объектов в интерфейсе пользователя (например, кнопки) при недопустимо для вызова команды. Например наши GameDetailsViewModel содержит команды для поддержки сохранение данных:

// Create the DelegateCommand
SaveCommand = new DelegateCommand<object>(c => Save(), c => CanSave());

При выполнении SaveCommand вызывает метод сохранения на наш ViewModel. Убедитесь, что команда допустимым затем вызывается метод CanSave. Это позволяет DelegateCommand отключать пользовательский ИНТЕРФЕЙС при необходимости. Состояние изменения представления можно вызвать метод DelegateCommand.RaiseCanExecuteChanged заставить новые проверки CanSave метод, чтобы включить или отключить пользовательский ИНТЕРФЕЙС при необходимости.

Чтобы связать этот XAML, используйте Click.Command вложенное свойство в пространстве имен Microsoft.Practices.Composite.Presentation.Commands. Затем привязки значение команды быть команды в ViewModel, следующим образом:

<Button Content="Save"
        cmd:Click.Command="{Binding SaveCommand}"
        Style="{StaticResource ourButton}"
        Grid.Column="1" />

Теперь при инициировании события Click наша команда выполняется. Если требуется, можно указать параметр команды для отправки команды таким образом можно повторно использовать.

Как вам может интересно, только команды, которая существует в CAL является события нажатия для кнопки (или других выделения). Однако классы можно использовать для написания собственных команд являются довольно просто. Образец кода содержит команды для SelectionChanged на ListBox или ComboBox. Эта команда называется SelectorCommandBehavior и является производным от CommandBehaviorBase < T >класс. Рассмотрение реализации поведение пользовательской команды предоставит вам с начала место написать собственные команды поведения.

Подведение итогов

Существуют определенные боль точек при разработке больших приложений Silverlight. При построении приложения с помощью слабых соединений и модульность, получить преимущества и могут быть гибкой в отвечать на запросы для изменения. Проект призмы от Майкрософт предоставляет средства и руководство позволяет перейти в область проекта, гибкости. Хотя призмы не подход к one-size-fits-all, модульность CAL означает, что использовать что помещается в определенных ситуациях и оставьте остальные.

Шон Wildemuth — Microsoft MVP (C#) и основатель Вилдермусом консультационной службе. Он написал несколько книг и много статей. Кроме того Шон в настоящее время выполняется демонстрацию Silverlight рассказывать Silverlight 2 по всей стране. С ним можно связаться по адресу shawn@wildermuthconsulting.com.