Prism IConfirmNavigationRequest

Topics: Prism v4 - WPF 4
Oct 28, 2013 at 11:25 AM
Hi.

I'm trying to implement interface IConfirmNavigationRequest in my WPF app, but can't make Prism call its functions.

In my app there are two regions, Region_1 and Region_3.

In Region_1 there's a FirstView, within which there's a button that's connected to a command in FirstViewModel, that does the actual navigation:
IRegionManager irmRegionManager = Microsoft.Practices.ServiceLocation.ServiceLocator.Current.GetInstance<Microsoft.Practices.Prism.Regions.IRegionManager>();
IRegion irRegion3 = irmRegionManager.Regions["Region_3"];
var viewToActivate = irRegion3.GetView(this.NavigatedView);
irRegion3.Activate(viewToActivate);
...
The navigation exchanges between two views in Region_3, and it works just fine.

FirstViewModel implements interface IConfirmNavigationRequest, but its functions aren't called along navigation, which is simply performed.
public class FirstViewModel :
                NotificationObject,
                IConfirmNavigationRequest
{
    public void OnNavigatedTo(NavigationContext navigationContext)
    {
    }
...
I then also implemented interface IConfirmNavigationRequest for ThirdView and FourthView, that are the views between which the navigation occurs in Region_3, but for no avail.

Nevertheless, I implemented interface IRegionMemberLifetime for both ThirdView and FourthView, and its KeepAlive function gets called as expected.
public partial class ThirdView :
                        UserControl,
                        IConfirmNavigationRequest,
                        IRegionMemberLifetime
{
    public ThirdView()
    {
        InitializeComponent();
    }

    public bool KeepAlive
    {
        get { return true; }
    }

    public void OnNavigatedTo(NavigationContext navigationContext)
    {
    }
...
At this point I was told that you should not navigate in Region_3 from Region_1, and that if you do so, IConfirmNavigationRequest functions won't be called. I therefore moved the navigation code (Activate) to ThirdView, where it is executed in result of an event that's fired by FirstViewModel whenever above mentioned button is clicked, and made ThirdView support interface IConfirmNavigationRequest, but for no avail: the functions of interface IConfirmNavigationRequest still aren't called.

Can anybody explain how to force Prism to call these functions?

Thanks!
Oct 28, 2013 at 2:16 PM
Hello OferElboher,

Based on my understanding, the problem you are facing remains on how you would "navigate" through Views.
Activate() method would not be exactly a Navigation call. I would think that Region_3 is a SingleActiveRegion so when you call to Activate() a specified View, the previous View that was already shown gets deactivated. This behavior of SingleActiveRegion would make you experience some kind of Navigation. However, any Confirmation or NavigatedTo/From methods would not be called.

In order to properly navigate between Views, you would need to use Region_3.RequestNavigate(..) method.
Then, changing the Activate() call with RegionManager.Regions["Region_3"].RequestNavigate(new Uri(this.NavigatedView.Name, UriKind.Relative)); would solve the issue. IConfirmNavigation would also be needed to be implemented on the current source Active Region_3's View.

You can verify on the Prism library implementation how the ConfirmNavigation method is called:
 private void RequestCanNavigateFromOnCurrentlyActiveView(
       NavigationContext navigationContext,
       Action<NavigationResult> navigationCallback,
       object[] activeViews,
       int currentViewIndex)
 {
       if (currentViewIndex < activeViews.Length)
       {
           var vetoingView = activeViews[currentViewIndex] as IConfirmNavigationRequest;
           if (vetoingView != null)
           {
               // the current active view implements IConfirmNavigationRequest, request confirmation
               // providing a callback to resume the navigation request
               vetoingView.ConfirmNavigationRequest(
                   navigationContext,
                   canNavigate =>
                   {
                       if (this.currentNavigationContext == navigationContext && canNavigate)
                       {
                           RequestCanNavigateFromOnCurrentlyActiveViewModel(
                               navigationContext,
                               navigationCallback,
                               activeViews,
                               currentViewIndex);
                      }
                      else
                      {
                           this.NotifyNavigationFailed(navigationContext, navigationCallback, null);
                      }
                });
           }
     ...
}
Regarding Activate() call, you could see that it doesn't perform any Navigation confirmation:
public override void Activate(object view)
{
      object currentActiveView = ActiveViews.FirstOrDefault();

      if (currentActiveView != null && currentActiveView != view && this.Views.Contains(currentActiveView))
      {
           base.Deactivate(currentActiveView);
      }
      base.Activate(view);
}
Notice that calling RequestNavigate() method from a different Region than the one where navigation takes place would not be a problem.
Nevertheless, IConfirmNavigation should be implemented on the ViewModel that belongs to the View on Region_3 that is Active before Navigation would be performed.

You can find more information about Navigation on the following MSDN Prism Guide chapter:

I hope this helped you,

Gabriel Ostrowsky.
http://blogs.southworks.net/gostrowsky
Marked as answer by OferElboher on 10/28/2013 at 10:07 AM
Oct 28, 2013 at 5:08 PM
Thanks you VERY-VERY much, Gabriel.
That's a neat and clean explanation that immediately worked!