private async Task <bool> NavigationOrchestratorAsync(Type page, object parameter, NavigationMode mode, Func <bool> navigate) { using (var locker = await LockAsync.Create(navigationLock)) { DebugWrite($"Page: {page}, Parameter: {parameter}, NavigationMode: {mode}"); if (page == null) { throw new ArgumentNullException(nameof(page)); } if (navigate == null) { throw new ArgumentNullException(nameof(navigate)); } // this cannot be used for duplicate navigation, except for refresh if (!Equals(mode, NavigationMode.Refresh)) { if (Equals(page, CurrentPageType)) { if (Equals(parameter, CurrentPageParam)) { return(false); } } } // fetch current (which will become old) var oldPage = FrameFacadeInternal.Content as Page; var oldParameter = CurrentPageParam; var oldViewModel = oldPage?.DataContext; var oldPageState = await FrameFacadeInternal.GetPageStateAsync(oldPage?.GetType()); var fromInfo = new NavigationInfo(oldPage?.GetType(), oldParameter, oldPageState); var toPageState = await FrameFacadeInternal.GetPageStateAsync(page?.GetType()); var toInfo = new NavigationInfo(page, parameter, toPageState); // call oldViewModel.OnNavigatingFromAsync() var viewmodelCancels = await Settings.ViewModelActionStrategy.NavigatingFromAsync((oldViewModel, false), fromInfo, toInfo, this); if (viewmodelCancels) { return(false); } // raise Navigating event RaiseNavigatingCancels(parameter, false, mode, toInfo, out var cancel); if (cancel) { return(false); } // invoke navigate (however custom) if (navigate.Invoke()) { CurrentPageParam = parameter; CurrentPageType = page; } else { return(false); } // fetch (current which is now new) var newPage = FrameFacadeInternal.Content as Page; var newViewModel = newPage?.DataContext; // note: this has no value now, but it will await Settings.ViewModelActionStrategy.NavigatingToAsync((newViewModel, mode, false), fromInfo, toInfo, this); // raise Navigated event RaiseNavigated(new NavigatedEventArgs() { Parameter = parameter, NavigationMode = mode, PageType = newPage?.GetType(), }); // call oldViewModel.OnNavigatedFrom() await Settings.ViewModelActionStrategy.NavigatedFromAsync((oldViewModel, false), fromInfo, toInfo, this); // call newViewModel.ResolveForPage() if (newViewModel == null) { newViewModel = ResolveViewModelForPage.Invoke(newPage); newPage.DataContext = newViewModel; } // call newTemplate10ViewModel.Properties if (newViewModel is ITemplate10ViewModel vm) { vm.NavigationService = this; } // call newViewModel.OnNavigatedToAsync() await Settings.ViewModelActionStrategy.NavigatedToAsync((newViewModel, mode, false), fromInfo, toInfo, this); // finally return(true); } }
private async Task <bool> NavigationOrchestratorAsync(Type page, object parameter, NavigationMode mode, Func <bool> navigate) { if (!await navigationOrchestratorAsyncSemaphore.WaitAsync(TimeSpan.FromSeconds(10))) { throw new TimeoutException("Semaphore wait ellapsed"); } // using (var locker = await LockAsync.Create(navigationLock)) try { DebugWrite($"Page: {page}, Parameter: {parameter}, NavigationMode: {mode}"); if (page == null) { throw new ArgumentNullException(nameof(page)); } if (navigate == null) { throw new ArgumentNullException(nameof(navigate)); } // this cannot be used for duplicate navigation, except for refresh if (!Equals(mode, NavigationMode.Refresh)) { if (Equals(page, CurrentPageType)) { if (Equals(parameter, CurrentPageParam)) { return(false); } } } // fetch current (which will become old) var oldPage = FrameFacadeInternal.Content as Page; var oldParameter = CurrentPageParam; var oldViewModel = oldPage?.DataContext; var oldPageState = await FrameFacadeInternal.GetPageStateAsync(oldPage?.GetType()); var from = new NavigationInfo(oldPage?.GetType(), oldParameter, oldPageState); var newPageState = await FrameFacadeInternal.GetPageStateAsync(page?.GetType()); var to = new NavigationInfo(page, parameter, newPageState); // call oldViewModel.OnNavigatingFromAsync() var cancelled = await Settings.ViewModelActionStrategy.NavigatingFromAsync((oldViewModel, mode, false), from, to, this); if (cancelled) { return(false); } // raise Navigating event RaiseNavigatingCancels(parameter, false, mode, to, out var cancel); if (cancel) { return(false); } // try to resolve the view-model before navigation var newViewModel = default(object); switch (mode) { case NavigationMode.New: case NavigationMode.Refresh: var strategy = Settings.ViewModelResolutionStrategy; newViewModel = await strategy.ResolveViewModel(page); if (newViewModel != null) { await Settings.ViewModelActionStrategy.NavigatingToAsync((newViewModel, mode, false), from, to, this); } break; } // navigate var newPage = default(Page); if (navigate.Invoke()) { if ((newPage = FrameFacadeInternal.Content as Page) == null) { return(false); } CurrentPageParam = parameter; CurrentPageType = page; } else { return(false); } // fetch current (which is now new) if (newViewModel != null) { newPage.DataContext = newViewModel; } else if ((newViewModel = newPage?.DataContext) != null) { await Settings.ViewModelActionStrategy.NavigatingToAsync((newViewModel, mode, false), from, to, this); } // raise Navigated event RaiseNavigated(new NavigatedEventArgs() { Parameter = parameter, NavigationMode = mode, PageType = newPage?.GetType(), }); // call oldViewModel.OnNavigatedFrom() await Settings.ViewModelActionStrategy.NavigatedFromAsync((oldViewModel, mode, false), from, to, this); // call newTemplate10ViewModel.Properties if (newViewModel is ITemplate10ViewModel vm) { vm.NavigationService = this; } // call newViewModel.OnNavigatedToAsync() await Settings.ViewModelActionStrategy.NavigatedToAsync((newViewModel, mode, false), from, to, this); // finally, all-good return(true); } catch (Exception ex) { throw; } finally { navigationOrchestratorAsyncSemaphore.Release(); } }