Wpf 如何在我的Prism应用程序(如MS Office)中执行多个shell?

Wpf 如何在我的Prism应用程序(如MS Office)中执行多个shell?,wpf,mvvm,prism,Wpf,Mvvm,Prism,我尝试创建一个具有MS Office窗口行为的应用程序,例如Word/Excel。用户打开应用程序,单击“新建”时,将出现一个全新的窗口,显示应用程序的外观 到目前为止,我发现的最接近的是: 但是,这里的shell显示在应用程序启动时。如何通过命令做到这一点,或者可能有一种完全不同的方法来实现这一点 编辑: 我现在还发现了以下内容:,但是在哪里以及如何调用此代码?创建多个shell是正确的想法。你只需要适当地注意细节 何时以及如何创建新的Shell 当然,Prism的方法是使用DelegateC

我尝试创建一个具有MS Office窗口行为的应用程序,例如Word/Excel。用户打开应用程序,单击“新建”时,将出现一个全新的窗口,显示应用程序的外观

到目前为止,我发现的最接近的是:

但是,这里的shell显示在应用程序启动时。如何通过命令做到这一点,或者可能有一种完全不同的方法来实现这一点

编辑:
我现在还发现了以下内容:,但是在哪里以及如何调用此代码?

创建多个shell是正确的想法。你只需要适当地注意细节

何时以及如何创建新的Shell 当然,Prism的方法是使用
DelegateCommand
处理新shell的创建。考虑到这个命令并不严格属于任何特定的ViewModel(我想说它有一个应用程序范围),我觉得最好有一个
public static类ApplicationWideCommands
CreateNewShellCommand
静态属性。然后,您可以使用
{x:Static}
从XAML绑定到它,或者根据需要从代码隐藏执行它

此命令需要处理两件事:

  • 创建新的
    窗口
    (实际上是一个
    外壳
  • 为新shell实例化一个新的
    IRegionManager
    ,以便现有shell中的区域与新shell中的区域之间在区域名称上没有冲突
  • 指示新shell中的区域属于新的
    IRegionManager
  • 我将从头到尾处理这个问题,因为它更容易解释

    给你的新Shell一个新的RegionManager 在Prism中声明区域时,除了区域名称外,还可以声明要使用的区域管理器。通常您不需要这样做,但这里我们需要选择使用哪个
    RegionManager
    ,因为区域名称在单个区域管理器的范围内必须是唯一的。由于区域名称是在视图的XAML中硬编码的,并且以另一种方式分配它们将是一件非常痛苦的事情,因此我们需要更改等式的另一半:每个shell使用的区域管理器实例。所以在
    Shell.xaml
    中可能有如下内容:

    <ContentControl
      regions:RegionManager.RegionManager="{Binding RegionManager}"
      regions:RegionManager.RegionName="ExampleRegion"
    />
    
    <ItemsControl
      regions:RegionManager.RegionManager="{Binding RegionManager, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Shell}}}"
      regions:RegionManager.RegionName="AnotherRegion"
    />
    
    所以现在我们只需要确保每个
    Shell
    实例都有自己的
    RegionManager
    。对于“第一个”shell,这将由
    引导程序执行。(下面的代码使用DI容器解析对象,示例使用
    UnityContainer
    。如果使用MEF进行依赖项注入,只需在心里转换为等效代码即可。)

    这里有一个重要的警告:RegionManager
    作为单例注册到容器中。这意味着,无论何时解析
    IRegionManager
    ,都将返回相同的实例。因此,我们通过调用该方法创建一个新实例(这适用于Prism v4;我不确定v2)

    此时,您知道如何创建任意数量的新
    Shell
    实例并相应地连接区域

    UI组合详细信息 您需要注意的最后一个细节是,每个shell中承载的所有区域,无论在其可视化树中有多深,都必须绑定到同一个
    RegionManager

    这意味着您必须显式地设置区域管理器,以便像我们在上面的
    ContentControl
    示例中那样,对应用程序中所有视图中的所有区域使用。幸运的是,这很容易做到,因为:

  • 所有视图最终都将成为可视化树中的
    Shell
    的后代
  • Shell
    已经将正确的
    RegionManager
    作为属性公开,因此我们可以绑定到该属性
  • 您可以这样做:

    <ContentControl
      regions:RegionManager.RegionManager="{Binding RegionManager}"
      regions:RegionManager.RegionName="ExampleRegion"
    />
    
    <ItemsControl
      regions:RegionManager.RegionManager="{Binding RegionManager, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Shell}}}"
      regions:RegionManager.RegionName="AnotherRegion"
    />
    
    
    
    准备就绪!
    您现在应该可以开始了。

    这里是我现在用来创建具有多个EventAggregator的多个Shell的实现。我在引导程序中传递容器。在我的特殊情况下,子模块侦听事件,以获取此Shell操作的路径参数。然后,子模块可以在路径位置创建节点数据文件,或根据参数详细信息从路径位置加载

    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);
            }
        }
    
    公共静态类AppCommands
    {
    公共静态容器;
    publicstaticicommand NewCommand=newdelegatecommand(CreateShell);
    私有静态void CreateShell(对象状态)
    {
    var regionManager=Container.Resolve();
    var newRegionManager=regionManager.CreateRegionManager();
    var neweventAggregator=neweventAggregator();
    容器.RegisterInstance(neweventAggregator);
    var shell=新shell(newRegionManager、neweventAggregator);
    shell.Show();
    SomeEventParameter=新的SomeEventParameter();
    //在此参数中添加某物
    neweventAggregator.GetEvent().Publish(参数);
    }
    }
    
    谢谢,这是一个很大的帮助,我的实现如下此答案中有一些事实上的错误。。。“UI组合详细信息”部分是不必要的。将区域名称指定给控件时,将自动创建区域。创建的区域将自动添加到逻辑树上距离该区域最近的区域管理器中。这就是作用域管理器的工作方式。因此,您需要做的就是确保新的shell实例具有自己的区域管理器。或者在每个shell中创建一个唯一命名的区域,并使用一个作用域管理器实例。我可以看出这将如何为壳牌公司指派一名新的区域经理,但我不太清楚的是这将如何发挥作用。我的理解是,当模块(模块)运行时,通常会向传递给它的区域管理器注册其视图
    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);
            }
        }