【发布时间】:2011-07-13 17:10:26
【问题描述】:
我尝试创建一个具有 MS Office 窗口行为的应用程序,例如 Word/Excel。用户打开应用程序,点击新建时,将出现一个与应用程序外观相同的全新窗口。
到目前为止我发现的最接近的是:Link
但是,这里的 shell 是在应用程序启动时显示的。如何通过命令来做到这一点,或者可能有完全不同的方式来实现这一点?
编辑: 我现在还发现了以下内容:Link,但是在哪里以及如何调用此代码?
【问题讨论】:
我尝试创建一个具有 MS Office 窗口行为的应用程序,例如 Word/Excel。用户打开应用程序,点击新建时,将出现一个与应用程序外观相同的全新窗口。
到目前为止我发现的最接近的是:Link
但是,这里的 shell 是在应用程序启动时显示的。如何通过命令来做到这一点,或者可能有完全不同的方式来实现这一点?
编辑: 我现在还发现了以下内容:Link,但是在哪里以及如何调用此代码?
【问题讨论】:
创建多个 shell 是正确的想法。您只需要妥善处理细节。
当然,Prism 方法是让DelegateCommand 处理新外壳的创建。考虑到这个命令严格来说并不属于任何特定的 ViewModel(我会说它具有应用程序范围的范围),对我来说,拥有一个带有 CreateNewShellCommand 静态属性的 public static class ApplicationWideCommands 感觉更好。然后,您可以使用 {x:Static} 从 XAML 绑定到它,或者根据需要从代码隐藏中执行它。
这个命令需要处理两件事:
Window(实际上是Shell)IRegionManager,这样现有 shell 中的区域与新 shell 中的区域之间的区域名称不会冲突IRegionManager
我会先解决这个问题,因为它更容易解释。
在 Prism 中声明区域时,除了区域名称之外,您还可以声明要使用的区域管理器。通常你不需要这样做,但在这里我们需要选择使用哪个RegionManager,因为区域名称在单个区域管理器的范围内必须是唯一的。由于区域名称是在视图的 XAML 中硬编码的,并且以另一种方式分配它们会很痛苦,我们需要更改等式的另一半:每个 shell 使用的区域管理器实例。所以在Shell.xaml里面可能会有这样的东西:
<ContentControl
regions:RegionManager.RegionManager="{Binding RegionManager}"
regions:RegionManager.RegionName="ExampleRegion"
/>
这将指示每个 shell 中的“WorkspaceRegion”属于绑定提供的IRegionManager。由于shell通常没有DataContext,我们可以在shell类本身中声明RegionManager属性:
public partial class Shell : Window
{
public Shell(IRegionManager regionManager)
{
this.RegionManager = regionManager;
InitializeComponent();
}
public IRegionManager RegionManager { get; private set; }
}
所以现在我们只需要确保每个Shell 实例都有自己的RegionManager。对于“第一个”shell,这将由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);
}
对于其他shell,将由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 实例并相应地连接区域。
您需要注意的最后一个细节是托管在每个 shell 中的所有区域,无论其可视化树有多深,都必须绑定到同一个 RegionManager。
这意味着您必须像我们在上面的ContentControl 示例中那样为应用程序中所有视图中的所有区域显式设置要使用的区域管理器。幸运的是,这很容易做到,因为:
Shell 的后代Shell 已经将正确的RegionManager 公开为属性,因此我们可以绑定到它你会这样做:
<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);
}
}
【讨论】: