1

Inspired by this answer, I created a general purpose Shell (Prism, WPF) like this:

<Window x:Class="VRMDataLogger.UI.Shell" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:prism="http://prismlibrary.com/" Title="My App" Height="450" Width="800">
    <Grid>
        <ContentControl prism:RegionManager.RegionManager="{Binding RegionManager}" prism:RegionManager.RegionName="MainShellRegion" />
    </Grid>
</Window>
public partial class Shell : Window
{
    public Shell(IRegionManager regionManager, Type contentType)
    {
        RegionManager = regionManager;
        InitializeComponent();
        RegionManager.RegisterViewWithRegion("MainShellRegion", contentType);
    }

    public IRegionManager RegionManager { get; }
}

The initial shell gets created in App.CreateShell():

protected override Window CreateShell()
{
    return new Shell(Container.Resolve<IRegionManager>(), typeof(StartScreen));
}

This works fine and the correct view is shown in that initial shell.

I then try to create a second Shell from StartScreenViewModel which shows a different view:

var shell = new Shell(RegionManager.CreateRegionManager(), typeof(MainScreen));
shell.Show();

This opens a new window, but it shows the same view like the first window (StartScreen) instead of MainScreen. What am I doing wrong here?

Robert Hegner
  • 9,014
  • 7
  • 62
  • 98
  • The two attached properties might interfer with each other, resulting in the wrong region manager effectively used... just guessing... Can you try to set everything from code-behind? Like here: https://stackoverflow.com/questions/41212881/region-manager-can-not-find-region-inside-of-the-custom-popupwindow/41228367#41228367 – Haukinger May 27 '19 at 14:24
  • @Haukinger thanks for your suggestion. Unfortunately it didn't help. I removed the bindings, and added a x:Name to the ContentControl. Then in the code behind I used SetRegionName and SetRegionManager. But it still behaves in the same way as before. Will now try out mm8's suggestion. – Robert Hegner May 27 '19 at 14:35
  • Question: if the region name does not matter, and you use only one region anyway, why use a region _at all_? You could just put a property `object ContentViewModel { get; }` in the shell view and bind that to the content control. As far as I see it, the benefit of a region is navigation and collecting content from any module providing some (through registration). If you use neither, you don't need the region in the first place. – Haukinger May 27 '19 at 14:43
  • Good question. I'm new to Prism and try to keep things as simple as possible (for now). So I was thinking about getting rid of the region stuff too. But I think what I'll lose by doing so is the nice feature of dependency injection into my view models - or am I wrong? – Robert Hegner May 27 '19 at 15:00
  • You're wrong. You also don't need all that shell stuff at all if you just have a mainwindow always. – Andy May 27 '19 at 17:31

2 Answers2

0

Try to use a unique region name for each shell:

public partial class Shell : Window
{
    public Shell(IRegionManager regionManager, Type contentType)
    {
        RegionManager = regionManager;
        InitializeComponent();
        MainRegionName = Guid.NewGuid().ToString();
        RegionManager.RegisterViewWithRegion(MainRegionName, contentType);
    }

    public string MainRegionName { get; }
    public IRegionManager RegionManager { get; }

}

XAML:

<ContentControl prism:RegionManager.RegionManager="{Binding RegionManager}"
                prism:RegionManager.RegionName="{Binding MainRegionName, RelativeSource={RelativeSource AncestorType=Window}}" />
mm8
  • 163,881
  • 10
  • 57
  • 88
  • Thanks @mm8 for your answer. This brings me one step further. But there are two things which irritate me with this: (1) If we really had a separate `IRegionManager` for the two shells, then (in theory) it shouldn't be necessary to use different names per shell. (2) When I implement `INavigationAware` on `MainScreenViewModel` (to get access to the navigation context), the `OnNavigatedTo` handler never gets fired. I'm wondering if this is because there is still something weird going on with the region manager... – Robert Hegner May 27 '19 at 16:02
0

So I was thinking about getting rid of the region stuff too. But I think what I'll lose by doing so is the nice feature of dependency injection into my view models

Not at all. In fact, you get more control over how your view models are created.

When going view-first, your view model is typically created by the ViewModelLocator upon navigation and then passed NavigationParameters, if any.

If you create your view model manually and bind the view via data templating, you have full control over the creation of the view model. The simplest option is to inject a factory for the view models (like Func<MainScreenViewModel>) and you get full dependency injection from the container.

internal class StartScreenViewModel
{
    public StartScreenViewModel( Func<MainScreenViewModel> mainScreenViewModelFactory )
    {
        GoToMainScreenCommand = new DelegateCommand( () => new Shell( mainScreenViewModelFactory() ).Show() );
    }

    public DelegateCommand GoToMainScreenCommand { get; }
}

Of course, you can use a more sophisticated, hand-crafted factory if need be (see this answer).

Haukinger
  • 10,420
  • 2
  • 15
  • 28