Подтвердить что ты не робот

Как сделать несколько оболочек в приложении Prism (например, MS Office)?

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

Ближайшим, что я нашел до сих пор, является следующее: Ссылка

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

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

4b9b3361

Ответ 1

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

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

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

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

  • Создайте новый Window (фактически, Shell)
  • Создайте новый IRegionManager для новой оболочки, чтобы не было конфликтов между именами регионов между областями существующей оболочки и элементами новой оболочки
  • Попросите регионы в новой оболочке принадлежать новому 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 выше для всех регионов во всех представлениях в вашем приложении. К счастью, это делается довольно легко, потому что:

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

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

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

Все настройки!

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

Ответ 2

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

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);
        }
    }