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);
                }
            }
        }
        /// <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);
        }