I try to create an application that has a window behaviour as MS Office, for example Word/Excel. The user opens the application and when clicking new, a completely new window shall appear with the look of the application.
The closest that I found so far is this: Link
However, here the shells are shown at application start. How to do this by command, or maybe there is a completely different way to achieve this?
Edit: I have also found the following now: Link, but where and how to call this code?
Creating multiple shells is the correct idea. You just need to take care of the details appropriately.
The Prism way is, of course, to have a DelegateCommand
handle the creation of a new shell. Considering that this command does not strictly belong to any particular ViewModel (I 'd say it has an application-wide scope), it feels better to me to have a public static class ApplicationWideCommands
with a CreateNewShellCommand
static property. You can then either bind to it from XAML with {x:Static}
or execute it from code-behind as needed.
This command would need to take care of two things:
Window
(actually, a Shell
)IRegionManager
for the new shell, so that there is no conflict in region names between the regions in the existing shell and those in the new shellIRegionManager
I 'll tackle this last-to-first, because it's easier to explain.
When declaring a region in Prism you can declare the region manager to use in addition to the region name. Normally you don't need to do this, but here we need to choose which RegionManager
to use because region names must be unique in the scope of a single region manager. Since the region names are hardcoded inside the XAML of the Views, and it would be a major pain to assign them another way, we need to change the other half of the equation: the region manager instance used by each shell. So inside Shell.xaml
there might be something like this:
<ContentControl
regions:RegionManager.RegionManager="{Binding RegionManager}"
regions:RegionManager.RegionName="ExampleRegion"
/>
This will instruct the "WorkspaceRegion" in each shell that it belongs to the IRegionManager
provided by the binding. Since the shell usually has no DataContext
, we can declare the RegionManager
property in the shell class itself:
public partial class Shell : Window
{
public Shell(IRegionManager regionManager)
{
this.RegionManager = regionManager;
InitializeComponent();
}
public IRegionManager RegionManager { get; private set; }
}
So now we just need to make sure that each Shell
instance gets its own RegionManager
. For the "first" shell, this will be done by the BootStrapper
. (The code below uses the DI container to resolve objects, and the examples use the UnityContainer
. If you use MEF for dependency injection just mentally translate to the equivalent code.)
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);
}
For the other shells, it will be done by the 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();
}
There is an important caveat here: The RegionManager
is registered into the container as a singleton. This means that whenever you resolve IRegionManager
you will be getting back the same instance. For this reason, we create a new instance by calling the IRegionManager.CreateRegionManager
method (this applies Prism v4; I 'm not sure about v2).
At this point, you know how to create any number of new Shell
instances and wire up the regions accordingly.
The final detail you need to take care of is that all regions hosted in each shell, no matter how deep in its visual tree, have to bind to the same RegionManager
.
This means that you have to explicitly set the region manager to use like we did in the ContentControl
example above for all regions in all Views in your application. Fortunately, this is done quite easily because:
Shell
in the visual treeShell
already exposes the correct RegionManager
as a property, so we can bind to thatYou would do so like this:
<ItemsControl
regions:RegionManager.RegionManager="{Binding RegionManager, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Shell}}}"
regions:RegionManager.RegionName="AnotherRegion"
/>
You now should be ready to go.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With