private void ContentLoader_BeginLoad_Callback(IAsyncResult result)
        {
            DependencyObject content = null;
            Uri uriBeingLoaded       = null;

            try
            {
                NavigationOperation asyncNavigationOperationCompleted = result.AsyncState as NavigationOperation;

                NavigationOperation navOp = this._currentNavigation;
                if (navOp == null || navOp.Uri != asyncNavigationOperationCompleted.Uri)
                {
                    // We already fired NavigationStopped in NavigateCore(), so just return without doing anything
                    return;
                }

                uriBeingLoaded = navOp.UriBeforeMapping;

                content = this._contentLoader.EndLoad(result) as DependencyObject;

                // If the content is anything but a UserControl, we should throw.
                // We support UserControls as they are a typical thing created in designers such as Blend,
                // but for a full experience one would use Page to get to things like NavigationContext,
                // NavigationService, Title, etc.
                if (!(content is UserControl))
                {
                    throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture,
                                                                      Resource.NavigationService_ContentIsNotAUserControl,
                                                                      content == null ? "null" : content.GetType().ToString(),
                                                                      "System.Windows.Controls.UserControl"));
                }

                // Content loader was successful, so complete navigation

                // Create a new navigation context
                JournalEntry.SetNavigationContext(content, new NavigationContext(UriParsingHelper.InternalUriParseQueryStringToDictionary(asyncNavigationOperationCompleted.Uri, true /* decodeResults */)));
                content.SetValue(NavigationServiceProperty, this);

                // Complete navigation operation
                this.CompleteNavigation(content);
            }
            catch (Exception ex)
            {
                if (this.RaiseNavigationFailed(uriBeingLoaded, ex))
                {
                    throw;
                }
            }
        }
        internal bool CheckForDeeplinks()
        {
            if (this.UseNavigationState)
            {
                string currentState = UriParsingHelper.InternalUriFromExternalValue(Application.Current.Host.NavigationState);
                if (!String.IsNullOrEmpty(currentState))
                {
                    this.AddHistoryPointIfDifferent(currentState);

                    return(true);
                }
            }

            return(false);
        }
        private void Initialize()
        {
            // Initialize stuff for the Uri template
            Regex newFromRegex = null;
            this._uriRegexIdentifierUsedTwice = this.UriTemplateContainsSameIdentifierTwice(this._uri, out newFromRegex);
            this._uriHasQueryString = !String.IsNullOrEmpty(UriParsingHelper.InternalUriGetQueryString(this._uri));
            this._uriHasFragment = !String.IsNullOrEmpty(UriParsingHelper.InternalUriGetFragment(this._uri));
            this._uriRegex = newFromRegex;
            this._mappedUriIsOnlyFragment = UriParsingHelper.InternalUriIsFragment(this._mappedUri);
            this._mappedUriIsOnlyQueryString = UriParsingHelper.QueryStringDelimiter + UriParsingHelper.InternalUriGetQueryString(this._mappedUri) == this._mappedUri.OriginalString;

            // Initialize stuff for the mapped Uri template
            this.GetIdentifiersForMappedUri(this._mappedUri);

            this._initialized = true;
        }
        /// <summary>
        /// Conditionally adds a new history point if the new state information differs from the current journal entry Uri value.
        /// </summary>
        /// <param name="newState">An updated state value to examine.</param>
        private void AddHistoryPointIfDifferent(string newState)
        {
            // Check if different from our current state
            string currentState = String.Empty;

            if (this.CurrentEntry != null && this.CurrentEntry.Source != null)
            {
                currentState = UriParsingHelper.InternalUriFromExternalValue(this.CurrentEntry.Source.OriginalString);
            }

            if (string.Equals(newState, currentState, StringComparison.Ordinal) == false)
            {
                this._suppressNavigationEvent = true;
                this.AddHistoryPoint(new JournalEntry(string.Empty, new Uri(newState, UriKind.RelativeOrAbsolute)));
                this._suppressNavigationEvent = false;
            }
        }
Beispiel #5
0
        /// <summary>
        /// Updates NavigationState to reflect the Journal state, if the Journal is using NavigationState.
        /// </summary>
        /// <param name="journalEntry">JournalEntry used to update the browser location.</param>
        private void UpdateNavigationState(JournalEntry journalEntry)
        {
            if (this.UseNavigationState)
            {
                if (this._suppressNavigationEvent == false)
                {
                    string state = journalEntry.Source == null
                                    ? string.Empty
                                    : UriParsingHelper.InternalUriToExternalValue(journalEntry.Source);

                    Application.Current.Host.NavigationState = state;
                    if (HtmlPage.IsEnabled)
                    {
                        HtmlPage.Document.SetProperty("title", journalEntry.Name);
                    }
                }
            }
        }
        private bool NavigateCore_StartNavigation(Uri uri, NavigationMode mode, bool suppressJournalAdd, Uri mergedUriAfterMapping, Uri mergedUri, bool isFragmentNavigationOnly)
        {
            this._currentNavigation = new NavigationOperation(mergedUriAfterMapping, mergedUri, uri, mode, suppressJournalAdd);

            if (isFragmentNavigationOnly)
            {
                // If we're navigating only to a fragment (e.g. "#frag2") then the Uri to journal should be that merged with the base uri
                if (UriParsingHelper.InternalUriIsFragment(uri))
                {
                    this._currentNavigation.UriForJournal = mergedUri;
                }
                this.Host.Dispatcher.BeginInvoke(() => this.CompleteNavigation(null));
                return(true);
            }

            this.UpdateNavigationCacheModeAlwaysPages();

            string uriAllButFragment = UriParsingHelper.InternalUriGetAllButFragment(uri);
            Page   reusedPage        = null;

            if (this._cacheRequiredPages.ContainsKey(uriAllButFragment))
            {
                reusedPage = this._cacheRequiredPages[uriAllButFragment];
            }
            else if (this.Cache.Contains(uriAllButFragment))
            {
                reusedPage = this.Cache[uriAllButFragment];
            }

            // If a page was found in either cache and that page hasn't yet changed its NavigationCacheMode to Disabled,
            // then navigation is done, otherwise open up new content
            if (reusedPage != null && reusedPage.NavigationCacheMode != NavigationCacheMode.Disabled)
            {
                this.Host.Dispatcher.BeginInvoke(() => this.CompleteNavigation(reusedPage));
            }
            else
            {
                this._currentNavigation.AsyncResult = this._contentLoader.BeginLoad(mergedUriAfterMapping, this._currentSourceAfterMapping, this.ContentLoader_BeginLoad_Callback, this._currentNavigation);
            }

            return(true);
        }
        /// <summary>
        /// Updates NavigationState to reflect the Journal state, if the Journal is using NavigationState.
        /// </summary>
        /// <param name="journalEntry">JournalEntry used to update the browser location.</param>
        private void UpdateNavigationState(JournalEntry journalEntry)
        {
            if (this.UseNavigationState)
            {
                if (this._suppressNavigationEvent == false)
                {
                    string state = journalEntry.Source == null
                                    ? string.Empty
                                    : UriParsingHelper.InternalUriToExternalValue(journalEntry.Source);

                    // Title updates only occur when DOM access is enabled, so check this first.
                    if (HtmlPage.IsEnabled)
                    {
                        // In older versions of IE (6, 7, and 8 in 7 compat mode) we use an
                        // iframe to cause journal updates.  But this requires that the title
                        // be set before the navigation for the dropdowns for back/forward
                        // to show the correct titles at the correct places in these lists.
                        //
                        // In newer versions of IE, and in all other supported browsers, the
                        // title should be set after the navigation for correct behavior.
                        if (UsingIFrame())
                        {
                            HtmlPage.Document.SetProperty("title", journalEntry.Name);
                            Application.Current.Host.NavigationState = state;
                        }
                        else
                        {
                            Application.Current.Host.NavigationState = state;
                            HtmlPage.Document.SetProperty("title", journalEntry.Name);
                        }
                    }
                    else
                    {
                        // We don't have DOM access, so just update NavigationState
                        // without a title update
                        Application.Current.Host.NavigationState = state;
                    }
                }
            }
        }
        private static void BeginLoad_OnUIThread(AsyncCallback userCallback, PageResourceContentLoaderAsyncResult result)
        {
            if (result.Exception != null)
            {
                result.IsCompleted = true;
                userCallback(result);
                return;
            }

            try
            {
                string pagePathAndName = UriParsingHelper.InternalUriGetBaseValue(result.Uri);

                string xaml = GetLocalXaml(pagePathAndName);

                if (String.IsNullOrEmpty(xaml))
                {
                    result.Exception = new InvalidOperationException(
                        String.Format(
                            CultureInfo.CurrentCulture,
                            Resource.PageResourceContentLoader_NoXAMLWasFound,
                            pagePathAndName));
                    return;
                }

                string classString = GetXClass(xaml);

                if (String.IsNullOrEmpty(classString))
                {
                    try
                    {
                        result.Content = XamlReader.Load(xaml);
                    }
                    catch (Exception ex)
                    {
                        result.Exception = new InvalidOperationException(
                            String.Format(
                                CultureInfo.CurrentCulture,
                                Resource.PageResourceContentLoader_XAMLWasUnloadable,
                                pagePathAndName),
                            ex);
                        return;
                    }
                }
                else
                {
                    // If it does have an x:Class attribute, then it has a
                    // code-behind, so get the CLR type of the XAML instead.
                    Type t = GetTypeFromAnyLoadedAssembly(classString);

                    if (t == null)
                    {
                        result.Exception = new InvalidOperationException(String.Format(
                                                                             CultureInfo.CurrentCulture,
                                                                             Resource.PageResourceContentLoader_TheTypeSpecifiedInTheXClassCouldNotBeFound,
                                                                             classString,
                                                                             pagePathAndName));
                        return;
                    }

                    result.Content = Activator.CreateInstance(t);
                    return;
                }
            }
            catch (Exception ex)
            {
                result.Exception = ex;
            }
            finally
            {
                result.IsCompleted = true;
                if (userCallback != null)
                {
                    userCallback(result);
                }
            }
        }
        private void CompleteNavigation(DependencyObject content)
        {
            Uri    uri                 = null;
            string pageTitle           = null;
            Page   existingContentPage = this._host.Content as Page;
            Page   newContentPage      = content as Page;

            pageTitle = JournalEntry.GetName(content ?? this._host.Content as DependencyObject);

            NavigationOperation navOp = this._currentNavigation;

            this._currentNavigation = null;
            if (navOp != null)
            {
                // Set uri
                uri = navOp.UriBeforeMapping;

                // Used to suppress navigation notifications.
                navOp.SuppressNotifications = true;

                if (this.CurrentSource == navOp.UriForJournal)
                {
                    // Do not record the navigation in the journal when moving to the same URI whether this
                    // is a redirection or not.
                    navOp.SuppressJournalAdd = true;
                }
                this.CurrentSource = navOp.UriForJournal;
                this._source       = navOp.UriBeforeMapping;
                this._currentSourceAfterMapping = navOp.Uri;
                this.Host.UpdateSourceFromNavigationService(navOp.UriForJournal);
                this.Host.CurrentSource = this.CurrentSource;

                // Check if this is a 'New' operation
                if (navOp.Mode == NavigationMode.New && navOp.Uri != null && navOp.SuppressJournalAdd == false)
                {
                    try
                    {
                        this._journalIsAddingHistoryPoint = true;
                        JournalEntry je = new JournalEntry(pageTitle ?? uri.OriginalString, navOp.UriForJournal);
                        this.Journal.AddHistoryPoint(je);
                    }
                    finally
                    {
                        this._journalIsAddingHistoryPoint = false;
                    }
                }

                this.Host.CanGoBack    = this.CanGoBack;
                this.Host.CanGoForward = this.CanGoForward;

                navOp.SuppressNotifications = false;
            }

            if (this.Journal.UseNavigationState && HtmlPage.IsEnabled)
            {
                HtmlPage.Document.SetProperty("title", pageTitle ?? (uri == null ? string.Empty : uri.OriginalString));
            }
            if (content == null)
            {
                // We're navigating to a fragment in the current page, so for WPF compatibility, fire FragmentNavigation THEN Navigated
                if (navOp != null)
                {
                    this.RaiseFragmentNavigation(UriParsingHelper.InternalUriGetFragment(navOp.Uri));
                    this.RaiseNavigated(content, uri, existingContentPage, newContentPage);
                }
            }
            else
            {
                // We're navigating to a fragment in the new content, so let the host load content, then for WPF compatibility,
                // fire Navigated THEN FragmentNavigation
                this.Host.Content = content;
                this.RaiseNavigated(content, uri, existingContentPage, newContentPage);
                string fragment = navOp == null ? null : UriParsingHelper.InternalUriGetFragment(navOp.Uri);
                if (!String.IsNullOrEmpty(fragment))
                {
                    this.RaiseFragmentNavigation(fragment);
                }
            }
        }
        private bool NavigateCore(Uri uri, NavigationMode mode, bool suppressJournalAdd, bool isRedirect)
        {
            try
            {
                if (uri == null)
                {
                    throw new ArgumentNullException("uri", Resource.NavigationService_NavigationToANullUriIsNotSupported);
                }

                // Make sure we're on the UI thread because of the DependencyProperties we use.
                if (!this.Host.Dispatcher.CheckAccess())
                {
                    // Move to UI thread
                    this.Host.Dispatcher.BeginInvoke(() => this.NavigateCore(uri, mode, suppressJournalAdd, isRedirect));
                    return(true);
                }

                Uri mappedUri = uri;
                // If the Uri is only a fragment, mapping does not take place
                if (!UriParsingHelper.InternalUriIsFragment(uri))
                {
                    UriMapperBase mapper = this.Host.UriMapper;
                    if (mapper != null)
                    {
                        Uri uriFromMapper = mapper.MapUri(uri);
                        if (uriFromMapper != null && !String.IsNullOrEmpty(uriFromMapper.OriginalString))
                        {
                            mappedUri = uriFromMapper;
                        }
                        else
                        {
                            mappedUri = uri;
                        }
                    }
                }

                Uri mergedUriAfterMapping = UriParsingHelper.InternalUriMerge(this._currentSourceAfterMapping, mappedUri) ?? mappedUri;
                Uri mergedUri             = UriParsingHelper.InternalUriMerge(this._currentSource, uri) ?? uri;

                // If we're navigating to just a fragment (i.e. "#frag1") or to a page which differs only in the fragment
                // (i.e. "Page.xaml?id=123" to "Page.xaml?id=123#frag1") then complete navigation without involving the content loader
                bool isFragmentNavigationOnly = (mode != NavigationMode.Refresh) &&
                                                (UriParsingHelper.InternalUriIsFragment(mappedUri) ||
                                                 UriParsingHelper.InternalUriGetAllButFragment(mergedUri) == UriParsingHelper.InternalUriGetAllButFragment(this._currentSource));

                // Check to see if anyone wants to cancel
                if (mode == NavigationMode.New || mode == NavigationMode.Refresh)
                {
                    if (this.RaiseNavigating(mergedUri, mode, isFragmentNavigationOnly) == true)
                    {
                        // Someone stopped us
                        this.RaiseNavigationStopped(null, mergedUri);
                        return(true);
                    }
                }

                // If the ContentLoader cannot load the new URI, throw an ArgumentException
                if (!this.ContentLoader.CanLoad(mappedUri, _currentSourceAfterMapping))
                {
                    throw new ArgumentException(Resource.NavigationService_CannotLoadUri, "uri");
                }

                if (isFragmentNavigationOnly && this.Host.Content == null)
                {
                    // It doesn't make sense to fragment navigate when there's no content, so raise NavigationFailed
                    throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture,
                                                                      Resource.NavigationService_FragmentNavigationRequiresContent,
                                                                      "Frame"));
                }

                if (isRedirect && this._currentNavigation != null &&
                    this._currentNavigation.UriForJournal == this._currentSource)
                {
                    // Do not record navigation in the journal in case of a redirection
                    // where the original target is the current URI.
                    suppressJournalAdd = true;
                }

                // Stop in-progress navigation
                this.StopLoadingCore(isRedirect);

                return(this.NavigateCore_StartNavigation(uri, mode, suppressJournalAdd, mergedUriAfterMapping, mergedUri, isFragmentNavigationOnly));
            }
            catch (Exception ex)
            {
                if (this.RaiseNavigationFailed(uri, ex))
                {
                    throw;
                }
                return(true);
            }
        }
        /// <summary>
        /// Attempts to process a Uri, if it matches the Uri template
        /// </summary>
        /// <param name="uri">The Uri to map</param>
        /// <returns>The Uri after mapping, or null if mapping did not succeed</returns>
        public Uri MapUri(Uri uri)
        {
            this.CheckPreconditions();

            if (this._uriRegex == null)
            {
                // If an empty Uri was passed in, we can map that even with an empty Uri Template.
                if (uri == null || uri.OriginalString == null || uri.OriginalString.Length == 0)
                {
                    return new Uri(this._mappedUri.OriginalString, UriKind.Relative);
                }
                // Otherwise, this does not match anything
                else
                {
                    return null;
                }
            }

            string originalUriWithoutQueryString = UriParsingHelper.InternalUriGetBaseValue(uri);

            Match m = this._uriRegex.Match(originalUriWithoutQueryString);

            if (!m.Success)
            {
                return null;
            }

            string uriAfterMappingBase = UriParsingHelper.InternalUriGetBaseValue(this._mappedUri);
            IDictionary<string, string> uriAfterMappingQueryString = UriParsingHelper.InternalUriParseQueryStringToDictionary(this._mappedUri, false /* decodeResults */);
            IDictionary<string, string> originalQueryString = UriParsingHelper.InternalUriParseQueryStringToDictionary(uri, false /* decodeResults */);
            string originalFragment = UriParsingHelper.InternalUriGetFragment(uri);
            string uriAfterMappingFragment = UriParsingHelper.InternalUriGetFragment(this._mappedUri);

            // 'uriValues' is the values of the identifiers from the 'Uri' template, as they appear in the Uri
            // being processed
            IDictionary<string, string> uriValues = new Dictionary<string, string>();

            // i begins at 1 because the group at index 0 is always equal to the parent's Match,
            // which we do not want.  We only want explicitly-named groups.
            int groupCount = m.Groups.Count;
            for (int i = 1; i < groupCount; i++)
            {
                uriValues.Add(this._uriRegex.GroupNameFromNumber(i), m.Groups[i].Value);
            }

            foreach (string identifier in this._mappedUriIdentifiers)
            {
                string identifierWithBraces = "{" + identifier + "}";
                string replacementValue = (uriValues.ContainsKey(identifier) ? uriValues[identifier] : String.Empty);

                // First check for identifiers in the base Uri, and replace them as appropriate
                uriAfterMappingBase = uriAfterMappingBase.Replace(identifierWithBraces, replacementValue);

                // Then, look through the query string (both the key and the value) and replace as appropriate
                string[] keys = new string[uriAfterMappingQueryString.Keys.Count];
                uriAfterMappingQueryString.Keys.CopyTo(keys, 0);
                foreach (string key in keys)
                {
                    // First check if the value contains it, as this is an easy replacement
                    if (uriAfterMappingQueryString[key].Contains(identifierWithBraces))
                    {
                        if (uriValues.ContainsKey(identifier))
                        {
                            uriAfterMappingQueryString[key] = uriAfterMappingQueryString[key].Replace(identifierWithBraces, replacementValue);
                        }
                    }

                    // If the key itself contains the identifier, then we need to remove the existing item with the key that
                    // contains the identifier, and re-add to the dictionary with the new key and the pre-existing value
                    if (key.Contains(identifierWithBraces))
                    {
                        string existingVal = uriAfterMappingQueryString[key];
                        uriAfterMappingQueryString.Remove(key);
                        uriAfterMappingQueryString.Add(key.Replace(identifierWithBraces, replacementValue), existingVal);
                    }
                }

                // If there's an original fragment already present, it will always win, so don't bother doing replacements
                if (String.IsNullOrEmpty(originalFragment) &&
                    !String.IsNullOrEmpty(uriAfterMappingFragment))
                {
                    if (uriAfterMappingFragment.Contains(identifierWithBraces))
                    {
                        uriAfterMappingFragment = uriAfterMappingFragment.Replace(identifierWithBraces, replacementValue);
                    }
                }
            }

            foreach (string key in originalQueryString.Keys)
            {
                if (!uriAfterMappingQueryString.ContainsKey(key))
                {
                    uriAfterMappingQueryString.Add(key, originalQueryString[key]);
                }
                else
                {
                    // If a value is present in the originally-navigated-to query string, it
                    // takes precedence over anything in the aliased query string by default.
                    uriAfterMappingQueryString[key] = originalQueryString[key];
                }
            }

            if (!String.IsNullOrEmpty(originalFragment))
            {
                uriAfterMappingFragment = originalFragment;
            }

            return UriParsingHelper.InternalUriCreateWithQueryStringValues(uriAfterMappingBase, uriAfterMappingQueryString, uriAfterMappingFragment);
        }
 /// <summary>
 /// Tells whether or not the target Uri can be loaded
 /// </summary>
 /// <param name="targetUri">A URI to load</param>
 /// <param name="currentUri">The current URI</param>
 /// <returns>True if the targetURI can be loaded</returns>
 public bool CanLoad(Uri targetUri, Uri currentUri)
 {
     return(UriParsingHelper.InternalUriIsNavigable(targetUri));
 }