Как сделать несколько оболочек в моем приложении Prism (например, MS Office)? - PullRequest
19 голосов
/ 11 марта 2011

Я пытаюсь создать приложение, которое имеет поведение окна как MS Office, например Word / Excel. Пользователь открывает приложение, и при нажатии на новое, должно появиться совершенно новое окно с видом приложения.

Самое близкое, что я нашел до сих пор, это: Ссылка

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

Edit: Я также нашел следующее: Ссылка , но где и как назвать этот код?

Ответы [ 2 ]

33 голосов
/ 12 марта 2011

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

Когда и как создать новую оболочку

Способ Prism - это, конечно, DelegateCommand обрабатывать создание новой оболочки. Учитывая, что эта команда не относится строго к какой-либо конкретной модели ViewModel (я бы сказал, что она имеет область действия всего приложения), для меня лучше иметь public static class ApplicationWideCommands со статическим свойством CreateNewShellCommand. Затем вы можете либо связать его с XAML с помощью {x:Static}, либо выполнить его из-за кода при необходимости.

Эта команда должна позаботиться о двух вещах:

  1. Создайте новый Window (на самом деле, Shell)
  2. Создание нового IRegionManager для новой оболочки, чтобы не возникало конфликта в именах регионов между регионами в существующей оболочке и в именах в новой оболочке
  3. Укажите регионы в новой оболочке, что они принадлежат новой IRegionManager

Я займусь этим первым, потому что это легче объяснить.

Предоставление новой оболочке нового RegionManager

При объявлении региона в Prism вы можете указать, что менеджер региона будет использоваться в дополнение к названию региона. Обычно вам не нужно этого делать, но здесь нам нужно выбрать, какой RegionManager использовать, потому что имена регионов должны быть уникальными в рамках одного менеджера регионов. Поскольку имена регионов жестко закодированы в XAML представлений, и было бы очень сложно назначить их другим способом, нам нужно изменить другую половину уравнения: экземпляр менеджера регионов, используемый каждой оболочкой. Так что внутри Shell.xaml может быть что-то вроде этого:

<ContentControl
  regions:RegionManager.RegionManager="{Binding RegionManager}"
  regions:RegionManager.RegionName="ExampleRegion"
/>

Это будет указывать «WorkspaceRegion» в каждой оболочке, что он принадлежит к IRegionManager, предоставленному привязкой. Поскольку оболочка обычно не имеет DataContext, мы можем объявить свойство RegionManager в самом классе оболочки:

public partial class Shell : Window
{
    public Shell(IRegionManager regionManager)
    {
        this.RegionManager = regionManager;
        InitializeComponent();
    }

    public IRegionManager RegionManager { get; private set; }
}

Так что теперь нам просто нужно убедиться, что каждый Shell экземпляр получает свой RegionManager. Для «первой» оболочки это будет сделано BootStrapper. (В приведенном ниже коде для разрешения объектов используется контейнер DI, а в примерах используется UnityContainer. Если вы используете MEF для внедрения зависимостей, просто мысленно преобразуйте эквивалентный код.)

protected override DependencyObject CreateShell()
{
    // I am assuming you have a reference to the DI container
    var regionManager = this.Container.Resolve<IRegionManager>();
    return new Shell(regionManager);
}

Для остальных оболочек это будет сделано CreateNewShellCommand:

private static ExecuteCreateNewShellCommand()
{
    // I am assuming you have a reference to the DI container
   var regionManager = this.Container.Resolve<IRegionManager>();
   ver newRegionManager = regionManager.CreateRegionManager();
   var shell = new Shell(newRegionManager);

   // The rest is easy, for example:
   shell.Show();
}

Здесь есть важное предупреждение: RegionManager зарегистрирован в контейнере как одиночный. Это означает, что всякий раз, когда вы решите IRegionManager , вы получите тот же экземпляр . По этой причине мы создаем новый экземпляр, вызывая метод IRegionManager.CreateRegionManager (применяется Prism v4; я не уверен насчет v2).

На данный момент вы знаете, как создать любое количество новых Shell экземпляров и соответствующим образом соединить регионы.

Детали композиции пользовательского интерфейса

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

Это означает, что вы должны явно настроить менеджер регионов на использование, как мы это делали в примере ContentControl выше, для всех регионов во всех представлениях в вашем приложении. К счастью, это делается довольно легко, потому что:

  1. Все представления будут потомками Shell в визуальном дереве
  2. Shell уже выставляет правильное RegionManager как свойство, поэтому мы можем привязаться к этому

Вы бы сделали это так:

<ItemsControl
  regions:RegionManager.RegionManager="{Binding RegionManager, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Shell}}}"
  regions:RegionManager.RegionName="AnotherRegion"
/>

Все готово!

Теперь вы должны быть готовы к работе.

0 голосов
/ 17 марта 2011

Вот реализация, которую я сейчас использую для создания нескольких оболочек с несколькими EventAggregators.Я передаю контейнер в загрузчик.В моем особом случае субмодуль слушает событие, чтобы получить параметр пути, с которым работает эта оболочка.Субмодуль может затем создать файл данных узла в местоположении пути или загрузить из расположения пути в зависимости от параметров параметра.

public static class AppCommands
    {
        public static Container;

        public static ICommand NewCommand = new DelegateCommand(CreateShell);

        private static void CreateShell(object state)
        {
            var regionManager = Container.Resolve<IRegionManager>();
            var newRegionManager = regionManager.CreateRegionManager();
            var neweventAggregator = new EventAggregator();
            Container.RegisterInstance<EventAggregator>(neweventAggregator);
            var shell = new Shell(newRegionManager, neweventAggregator);

            shell.Show();
            SomeEventParameter parameter = new SomeEventParameter ();
            //Add sth to the parameter here

            neweventAggregator .GetEvent<SomeEvent>().Publish(parameter);
        }
    }
...