/// <summary> /// Performs navigation against current ModernFrame. /// </summary> /// <param name="oldValue">Navigate From Uri</param> /// <param name="newValue">Navigate To Uri</param> /// <param name="navigationType">Type of Navigation</param> /// <param name="passingParameter">Parameter for passing.</param> /// <remarks> /// Can be used to fire Navigated events. /// </remarks> public void Navigate <TK>(Uri oldValue, Uri newValue, NavigationType navigationType, TK passingParameter) { Debug.WriteLine("Navigating from '{0}' to '{1}'", oldValue, newValue); // set IsLoadingContent state Frame.SetValue(ModernFrame.IsLoadingContentPropertyKey, true); // cancel previous load content task (if any) // note: no need for thread synchronization, this code always executes on the UI thread if (Frame.TokenSource != null) { Frame.TokenSource.Cancel(); Frame.TokenSource = null; } // push previous source onto the history stack (only for new navigation types) if (oldValue != null && navigationType == NavigationType.New) { _history.Push(oldValue); } object newContent = null; if (newValue != null) { // content is cached on uri without fragment var newValueNoFragment = NavigationHelper.RemoveFragment(newValue); if (navigationType == NavigationType.Refresh || !this._contentCache.TryGetValue(newValueNoFragment, out newContent)) { var localTokenSource = new CancellationTokenSource(); Frame.TokenSource = localTokenSource; // load the content (asynchronous!) var scheduler = TaskScheduler.FromCurrentSynchronizationContext(); var task = Frame.ContentLoader.LoadContentAsync(newValue, Frame.TokenSource.Token); task.ContinueWith(t => { try { if (t.IsCanceled || localTokenSource.IsCancellationRequested) { Debug.WriteLine("Cancelled navigation to '{0}'", newValue); } else if (t.IsFaulted && t.Exception != null) { var failedArgs = new NavigationFailedEventArgs { Frame = Frame, Source = newValue, Error = t.Exception.InnerException, Handled = false }; OnNavigationFailed(failedArgs); // if not handled, show error as content newContent = failedArgs.Handled ? null : failedArgs.Error; SetContent(newValue, navigationType, newContent, true, passingParameter); } else { newContent = t.Result; if (ShouldKeepContentAlive(newContent)) { // keep the new content in memory _contentCache[newValueNoFragment] = newContent; } SetContent(newValue, navigationType, newContent, false, passingParameter); } } finally { // clear global tokenSource to avoid a Cancel on a disposed object if (Frame.TokenSource == localTokenSource) { Frame.TokenSource = null; } // and dispose of the local tokensource localTokenSource.Dispose(); } }, scheduler); return; } } // newValue is null or newContent was found in the cache SetContent(newValue, navigationType, newContent, false, passingParameter); }
/// <summary> /// Performs navigation to specified link. /// </summary> /// <param name="uri">The uri to navigate to.</param> /// <param name="source">The source element that triggers the navigation. Required for frame navigation.</param> /// <param name="parameter">An optional command parameter or navigation target.</param> public virtual void Navigate(Uri uri, FrameworkElement source = null, string parameter = null) { if (uri == null) { throw new ArgumentNullException(nameof(uri)); } if (uri.OriginalString.StartsWith(@"www.")) { uri = new Uri("http://" + uri.OriginalString); } var args = new NavigateEventArgs(uri); PreviewNavigate?.Invoke(this, args); if (args.Cancel) { return; } // first check if uri refers to a command if (Commands != null) { if (Commands.TryGetValue(uri, out var command)) { // note: not executed within BbCodeBlock context, Hyperlink instance has Command and CommandParameter set if (command.CanExecute(parameter)) { command.Execute(parameter); } return; } if (uri.IsAbsoluteUri) { var original = uri.AbsoluteUri; var index = original.IndexOf('?'); if (index != -1) { var subUri = new Uri(original.Substring(0, index), UriKind.Absolute); if (Commands.TryGetValue(subUri, out command)) { parameter = uri.GetQueryParam("param"); if (command.CanExecute(parameter)) { command.Execute(parameter); } return; } } } } if (uri.IsAbsoluteUri && ExternalSchemes != null && ExternalSchemes.Any(s => uri.Scheme.Equals(s, StringComparison.OrdinalIgnoreCase))) { // uri is external, load in default browser Process.Start(uri.AbsoluteUri); } else { // perform frame navigation if (source == null) // source required { throw new ArgumentException(string.Format(CultureInfo.CurrentUICulture, UiStrings.NavigationFailedSourceNotSpecified, uri)); } // use optional parameter as navigation target to identify target frame (_self, _parent, _top or named target frame) var frame = NavigationHelper.FindFrame(parameter, source); if (frame == null) { throw new ArgumentException(string.Format(CultureInfo.CurrentUICulture, UiStrings.NavigationFailedFrameNotFound, uri, parameter)); } if (!(Window.GetWindow(frame) is INavigateUriHandler window) || frame.GetParent <ModernFrame>() != null || window.NavigateTo(uri) != true) { // delegate navigation to the frame frame.Source = uri; } } }