WPF - Prism 7.1 - Navigation - Mastering Tab Control - Modal/Dialog Window

Viewed 1844

I am using Prism 7.1 navigation framework (WPF) to get a dialog window to pop up using the configuration below. This is successful. However, I want this popup to have tabs that I can navigate back and forth among. When I click the button on the popup box in an attempt to display ViewA inside of it, nothing happens. By setting a breakpoint, I see that the navigation path is hit, and is displaying the correct view name. Refer to PopUpWindow.cs. However when it goes to resolve the view, the view does not display. Even worse, no error is thrown! I am confused as to why this is occurring.

Assuming my namespaces are correct, what am I doing wrong?

PrismApplication.cs

protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
    containerRegistry.RegisterForNavigation<ViewA>();
}

//Have tried register type, register type for navigation, etc etc.

MainWindowViewModel.xaml

<Window 
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        xmlns:prism="http://prismlibrary.com/"
        prism:ViewModelLocator.AutoWireViewModel="True"
        Height="350" Width="525">
    <i:Interaction.Triggers>
        <prism:InteractionRequestTrigger SourceObject="{Binding NotificationRequest}">
            <prism:PopupWindowAction IsModal="True" CenterOverAssociatedObject="True" />
        </prism:InteractionRequestTrigger>
    </i:Interaction.Triggers>
    <StackPanel>
        <Button Margin="5" Content="Raise Default Notification" Command="{Binding NotificationCommand}" />
    </StackPanel>

MainWindowViewModel.cs

public MainWindowViewModel
{
    public InteractionRequest<INotification> NotificationRequest { get; set; }
    public DelegateCommand NotificationCommand { get; set; }

    public MainWindowViewModel()
    {
        NotificationRequest = new InteractionRequest<INotification>();
        NotificationCommand = new DelegateCommand(RaiseNotification);
    }

    void RaiseNotification()
    {
        NotificationRequest.Raise(new PopupWindow());
    }
}

PopUpWindow.xaml

<UserControl 
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:prism="http://prismlibrary.com/"
        prism:ViewModelLocator.AutoWireViewModel="True"
        Height="350" Width="525">
    <DockPanel LastChildFill="True">
        <StackPanel Orientation="Horizontal" DockPanel.Dock="Top" Margin="5" >
            <Button Command="{Binding NavigateCommand}" CommandParameter="ViewA" Margin="5">Navigate to View A</Button> 
        </StackPanel>
        <ContentControl prism:RegionManager.RegionName="ContentRegion" Margin="5"  />
    </DockPanel>
</UserControl>

PopUpWindow.cs

public class PopupWindowViewModel
{
    private readonly IRegionManager _regionManager;

    public DelegateCommand<string> NavigateCommand { get; private set; }

    public PopupWindowViewModel(IRegionManager regionManager)
    {
        _regionManager = regionManager;

        NavigateCommand = new DelegateCommand<string>(Navigate);
    }

    private void Navigate(string navigatePath)
    {
        if (navigatePath != null)
            _regionManager.RequestNavigate("ContentRegion", navigatePath); 

        //During debugging, this correctly shows navigatePath as "ViewA"
    }
}

ViewA.xaml

<UserControl 
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:prism="http://prismlibrary.com/"             
             prism:ViewModelLocator.AutoWireViewModel="True">
    <Grid>
        <TextBlock Text="ViewA" FontSize="48" HorizontalAlignment="Center" VerticalAlignment="Center" />
    </Grid>
</UserControl>
3 Answers

Maybe it's just not finding your view. Isn't the second parameter supposed to be a url rather than a string? From here: https://prismlibrary.github.io/docs/wpf/Navigation.html

    IRegionManager regionManager = ...;
regionManager.RequestNavigate("MainRegion",
                                new Uri("InboxView", UriKind.Relative));

Check where your view is and what the path should be. I think you could prove that using something like:

var testinstance = System.Windows.Application.LoadComponent(testUrl);

https://docs.microsoft.com/en-us/dotnet/api/system.windows.application.loadcomponent?view=netframework-4.7.2

And if you're using MEF I think you also need to mark the View with the Export attribute.

Hopefully your problem is just you forgot about a folder or some such.

If not then it could be related to regionmanager not getting a reference to your region.

The InteractionRequest pattern is a bit quirky. You need to make sure that all views that should react on the request have the necessary InteractionRequestTrigger in the visual tree. Thus, the immediate fix to your problem is to copy your XAML from MainWindowView.xaml to ViewA.xaml:

<UserControl 
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:prism="http://prismlibrary.com/"
        prism:ViewModelLocator.AutoWireViewModel="True"
        Height="350" Width="525">
   <i:Interaction.Triggers>
        <prism:InteractionRequestTrigger SourceObject="{Binding NotificationRequest}">
            <prism:PopupWindowAction IsModal="True" CenterOverAssociatedObject="True" />
        </prism:InteractionRequestTrigger>
    </i:Interaction.Triggers> 
    <!-- ... -->
</UserControl>

Then make sure to add the NotificationRequest in the viewmodel for ViewA. Please note that you may still encounter scenarios where the interaction request doesn't work. E.g. when adding triggers inside a data template. Though, as long as you put them on the UserControl level you should be fine.

One possible improvement to this (flawed) design is to create a behavior where you programmatically add these interaction triggers.

Regions that aren't in the visual tree are ignored by the region manager. You define ContentRegion within the PopUpWindow (which is lazily created), so it is not there and the navigation request for the unknown region is just ignored.

As detailled here and there, in this case, you have to add the region manually in the constructor of the view containing it:

RegionManager.SetRegionName( theNameOfTheContentControlInsideThePopup, WellKnownRegionNames.DataFeedRegion );
RegionManager.SetRegionManager( theNameOfTheContentControlInsideThePopup, theRegionManagerInstanceFromUnity );

with a region manager from the ServiceLocator:

ServiceLocator.Current.GetInstance<IRegionManager>()
Related