private async Task<bool> NavigateCore(
            Type sourcePageType, object parameter, NavigationMode navigationMode)
        {
            _isNavigating = true;
            this.CanNavigate = false;
            this.CanGoBack = false;
            this.CanGoForward = false;
            this.IsHitTestVisible = false;

            // Make sure we run on UI thread
            if (!Dispatcher.HasThreadAccess)
            {
                Task<bool> navigateCoreTask = null;
                await Dispatcher.RunAsync(
                    CoreDispatcherPriority.High,
                    () => navigateCoreTask = NavigateCore(sourcePageType, parameter, navigationMode));
                return await navigateCoreTask;
            }

            try
            {
                await _waitForApplyTemplateTaskSource.Task;
                await this.WaitForLoadedAsync();
                AlternativePage currentPage = null;

                #region OnNavigatingFrom part
                if (_currentPagePresenter != null)
                {
                    currentPage = (AlternativePage)_currentPagePresenter.Content;
                    var cancelArgs =
                        new AlternativeNavigatingCancelEventArgs(
                            navigationMode,
                            sourcePageType);
                    await this.OnNavigating(cancelArgs);

                    if (!cancelArgs.Cancel)
                    {
                        await currentPage.OnNavigatingFromInternal(cancelArgs);
                    }

                    if (cancelArgs.Cancel)
                    {
                        return false;
                    }
                }
                #endregion

                #region Initializing new page instance part
                var je = new JournalEntry { SourcePageType = sourcePageType, Parameter = parameter };
                AlternativePage newPage;
                ContentPresenter newPagePresenter;

                if (_preloadedPageCache.ContainsKey(je))
                {
                    newPagePresenter = _preloadedPageCache[je];
                    newPage = (AlternativePage)newPagePresenter.Content;
                    _preloadedPageCache.Remove(je);
                }
                else
                {
                    newPage = _frameCache.Get(je.SourcePageType);

                    if (newPage == null)
                    {
                        throw new InvalidOperationException(
                            "Pages used in AlternativeFrame need to be of AlternativePage type.");
                    }

                    newPage.Frame = this;
                    newPagePresenter = new ContentPresenter { Style = PagePresenterStyle };
                    newPagePresenter.Content = newPage;
                    _pagePresentersPanel.Children.Add(newPagePresenter);
                }

                newPagePresenter.Opacity = 0.005;

                await UnloadAllPreloaded();
                #endregion

                #region OnNavigatingTo part
                var args = new AlternativeNavigationEventArgs(
                            newPage.Content, navigationMode, je.Parameter, je.SourcePageType);
                await newPage.OnNavigatingToInternal(args);
                #endregion

                #region Journal Bookeeping part
                switch (navigationMode)
                {
                    case NavigationMode.New:
                        this.ForwardStack.Clear();

                        if (this.CurrentJournalEntry != null)
                        {
                            this.BackStack.Push(this.CurrentJournalEntry);
                        }

                        break;
                    case NavigationMode.Forward:
                        this.ForwardStack.Pop();

                        if (this.CurrentJournalEntry != null)
                        {
                            this.BackStack.Push(this.CurrentJournalEntry);
                        }

                        break;
                    case NavigationMode.Back:
                        this.BackStack.Pop();

                        if (this.CurrentJournalEntry != null)
                        {
                            this.ForwardStack.Push(this.CurrentJournalEntry);
                        }

                        break;
                }

                this.CurrentJournalEntry = je;
                #endregion

                #region OnNavigated~() calls
                await this.OnNavigated(args);

                if (currentPage != null)
                {
                    await currentPage.OnNavigatedFromInternal(args);
                }

                await newPage.OnNavigatedToInternal(args);
                #endregion

                #region Transition part
                await newPagePresenter.WaitForLoadedAsync();
                await newPagePresenter.WaitForNonZeroSizeAsync();

                if (this.ShouldWaitForImagesToLoad == true &&
                    newPage.ShouldWaitForImagesToLoad != false ||
                    newPage.ShouldWaitForImagesToLoad == true &&
                    this.ShouldWaitForImagesToLoad != false)
                {
                    await newPage.WaitForImagesToLoad(WaitForImagesToLoadTimeout);
                }

                newPagePresenter.Opacity = 1.0;

                if (navigationMode == NavigationMode.Back)
                {
                    await TransitionBackward(
                        currentPage,
                        newPage,
                        _currentPagePresenter,
                        newPagePresenter);
                }
                else
                {
                    await TransitionForward(
                        currentPage,
                        newPage,
                        _currentPagePresenter,
                        newPagePresenter);
                }

                if (_currentPagePresenter != null)
                {
                    _pagePresentersPanel.Children.Remove(_currentPagePresenter);
                    _frameCache.Store(currentPage);
                }

                _currentPagePresenter = newPagePresenter;
                #endregion

                return true;
            }
            finally
            {
                this.IsHitTestVisible = true;
                _isNavigating = false;
                this.CanNavigate = true;
                this.CanGoBack = this.BackStack.Count > 0;
                this.CanGoForward = this.ForwardStack.Count > 0;
                //DC.TraceLocalized(GetNavigationState());
            }
        }
        /// <summary>
        /// Unloads the preloaded page of a given type and parameter.
        /// </summary>
        /// <remarks>
        /// A page might have been preloaded in the anticipation of the user navigating to it,
        /// then a state change might invalidate or reduce the possibility of a navigation request and
        /// it might be worth unloading the preloaded page to release resources that it consumed.
        /// The preloaded page will be released or cached depending on its NavigationCacheMode property.
        /// </remarks>
        /// <param name="sourcePageType">Type of the source page.</param>
        /// <param name="parameter">The parameter.</param>
        /// <returns></returns>
        public async Task UnloadPreloaded(Type sourcePageType, object parameter)
        {
            var je = new JournalEntry { SourcePageType = sourcePageType, Parameter = parameter };

            if (!_preloadedPageCache.ContainsKey(je))
            {
                return;
            }

            var cp = _preloadedPageCache[je];
            var page = (AlternativePage)cp.Content;
            await page.UnloadPreloadedInternal();
            _frameCache.Store(page);

            _pagePresentersPanel.Children.Remove(cp);
            _preloadedPageCache.Remove(je);
        } 
        private void AppendEntryToNavigationStateString(StringBuilder sb, JournalEntry journalEntry)
        {
            var pageTypeString = journalEntry.SourcePageType.AssemblyQualifiedName;
            sb.AppendFormat(",{0},{1}", pageTypeString.Length, pageTypeString);

            if (journalEntry.Parameter == null)
            {
                sb.Append(",0");
                return;
            }

            var valueString = journalEntry.Parameter.ToString();

            if (journalEntry.Parameter is byte)
            {
                AddParameterToNavigationStateString(sb, 1, valueString);
                return;
            }
            if (journalEntry.Parameter is Int16)
            {
                AddParameterToNavigationStateString(sb, 2, valueString);
                return;
            }
            if (journalEntry.Parameter is UInt16)
            {
                AddParameterToNavigationStateString(sb, 3, valueString);
                return;
            }
            if (journalEntry.Parameter is Int32)
            {
                AddParameterToNavigationStateString(sb, 4, valueString);
                return;
            }
            if (journalEntry.Parameter is UInt32)
            {
                AddParameterToNavigationStateString(sb, 5, valueString);
                return;
            }
            if (journalEntry.Parameter is Int64)
            {
                AddParameterToNavigationStateString(sb, 6, valueString);
                return;
            }
            if (journalEntry.Parameter is UInt64)
            {
                AddParameterToNavigationStateString(sb, 7, valueString);
                return;
            }
            if (journalEntry.Parameter is Single)
            {
                AddParameterToNavigationStateString(sb, 8, valueString);
                return;
            }
            if (journalEntry.Parameter is Double)
            {
                AddParameterToNavigationStateString(sb, 9, valueString);
                return;
            }
            if (journalEntry.Parameter is Char)
            {
                AddParameterToNavigationStateString(sb, 10, valueString);
                return;
            }
            if (journalEntry.Parameter is Boolean)
            {
                AddParameterToNavigationStateString(sb, 11, valueString);
                return;
            }
            if (journalEntry.Parameter is String)
            {
                AddParameterToNavigationStateString(sb, 12, valueString);
            }
        } 
        /// <summary>
        /// Preloads a page of the specified source page type.
        /// </summary>
        /// <remarks>
        /// Allows to preload a page before user navigates to it, so if it does get navigated to - shows up quickly.
        /// If another page gets navigated to - the preloaded page will be released or cached depending on its NavigationCacheMode property.
        /// </remarks>
        /// <param name="sourcePageType">Type of the source page.</param>
        /// <param name="parameter">The parameter.</param>
        /// <returns></returns>
        public async Task<bool> Preload(Type sourcePageType, object parameter)
        {
            var je = new JournalEntry { SourcePageType = sourcePageType, Parameter = parameter };

            if (_preloadedPageCache.ContainsKey(je))
            {
                return true;
            }

            var cp = new ContentPresenter { Style = PagePresenterStyle };
            var newPage = _frameCache.Get(sourcePageType);

            Debug.Assert(newPage != null, "Pages used in AlternativeFrame need to be of AlternativePage type.");

            if (newPage == null)
            {
                return false;
            }

            newPage.Frame = this;
            cp.Content = newPage;
            cp.Opacity = 0.005;
            Canvas.SetZIndex(cp, int.MinValue);
            _pagePresentersPanel.Children.Insert(0, cp);
            _preloadedPageCache.Add(je, cp);
            await newPage.PreloadInternal(parameter);
            cp.Opacity = 0;

            return true;
        } 
        public async Task<bool> Navigate(Type sourcePageType, object parameter)
        {
            if (_isNavigating)
            {
                throw new InvalidOperationException("Navigation already in progress.");
            }

            _isNavigating = true;
            this.CanNavigate = false;
            this.CanGoBack = false;

            try
            {
                await this.WaitForLoadedAsync();
                AlternativePage currentPage = null;

                if (_currentPagePresenter != null)
                {
                    currentPage = (AlternativePage)_currentPagePresenter.Content;
                    var cancelArgs =
                        new AlternativeNavigatingCancelEventArgs(
                            NavigationMode.New,
                            sourcePageType);
                    await currentPage.OnNavigatingFromInternal(cancelArgs);

                    if (cancelArgs.Cancel)
                    {
                        return false;
                    }
                }

                var je = new JournalEntry {Type = sourcePageType, Parameter = parameter};
                AlternativePage newPage;
                ContentPresenter newPagePresenter;

                if (_preloadedPageCache.ContainsKey(je))
                {
                    newPagePresenter = _preloadedPageCache[je];
                    newPage = (AlternativePage)newPagePresenter.Content;
                    _preloadedPageCache.Remove(je);
                }
                else
                {
                    newPage = (AlternativePage)Activator.CreateInstance(je.Type);
                    newPage.Frame = this;
                    newPagePresenter = new ContentPresenter {Style = PagePresenterStyle};
                    newPagePresenter.Content = newPage;
                    _pagePresentersPanel.Children.Add(newPagePresenter);
                }

                newPagePresenter.Opacity = 0.005;

                // TODO: await?
                UnloadPreloadedPages();

                var args = new AlternativeNavigationEventArgs(
                    newPage.Content, NavigationMode.New, je.Parameter, je.Type);
                await newPage.OnNavigatingToInternal(args);

                await newPagePresenter.WaitForLoadedAsync();
                await newPagePresenter.WaitForNonZeroSizeAsync();

                if (this.ShouldWaitForImagesToLoad == true && newPage.ShouldWaitForImagesToLoad != false ||
                    newPage.ShouldWaitForImagesToLoad == true && this.ShouldWaitForImagesToLoad != false)
                {
                    await newPage.WaitForImagesToLoad();
                }

                newPagePresenter.Opacity = 1.0;

                if (this.PageTransition != null)
                {
                    await this.PageTransition.TransitionForward(_currentPagePresenter, newPagePresenter);
                }

                this.BackStack.Push(je);

                if (currentPage != null)
                {
                    await currentPage.OnNavigatedFromInternal(args);
                    _pagePresentersPanel.Children.Remove(_currentPagePresenter);
                }

                _currentPagePresenter = newPagePresenter;

                await newPage.OnNavigatedToInternal(args);

                return true;
            }
            finally
            {
                _isNavigating = false;
                this.CanNavigate = true;
                this.CanGoBack = this.BackStack.Count > 1;
            }
        }
        // Allows to preload a page before user navigates to it, so if it does get navigated to - it is quick.
        public async Task Preload(Type sourcePageType, object parameter)
        {
            var je = new JournalEntry {Type = sourcePageType, Parameter = parameter};

            if (_preloadedPageCache.ContainsKey(je))
            {
                return;
            }

            var cp = new ContentPresenter {Style = PagePresenterStyle};
            var newPage = (AlternativePage)Activator.CreateInstance(sourcePageType);
            newPage.Frame = this;
            cp.Content = newPage;
            cp.Opacity = 0.005;
            Canvas.SetZIndex(cp, int.MinValue);
            _pagePresentersPanel.Children.Insert(0, cp);
            _preloadedPageCache.Add(je, cp);
            await newPage.PreloadInternal(parameter);
        }
        private void AppendEntryToNavigationStateString(StringBuilder sb, JournalEntry journalEntry)
        {
            var pageTypeString = journalEntry.SourcePageType.AssemblyQualifiedName;

            sb.AppendFormat(",{0},{1}", pageTypeString.Length, pageTypeString);

            if (journalEntry.Parameter == null)
            {
                sb.Append(",0");
                return;
            }

            var valueString = journalEntry.Parameter.ToString();

            if (journalEntry.Parameter is byte)
            {
                AddParameterToNavigationStateString(sb, 1, valueString);
                return;
            }
            if (journalEntry.Parameter is Int16)
            {
                AddParameterToNavigationStateString(sb, 2, valueString);
                return;
            }
            if (journalEntry.Parameter is UInt16)
            {
                AddParameterToNavigationStateString(sb, 3, valueString);
                return;
            }
            if (journalEntry.Parameter is Int32)
            {
                AddParameterToNavigationStateString(sb, 4, valueString);
                return;
            }
            if (journalEntry.Parameter is UInt32)
            {
                AddParameterToNavigationStateString(sb, 5, valueString);
                return;
            }
            if (journalEntry.Parameter is Int64)
            {
                AddParameterToNavigationStateString(sb, 6, valueString);
                return;
            }
            if (journalEntry.Parameter is UInt64)
            {
                AddParameterToNavigationStateString(sb, 7, valueString);
                return;
            }
            if (journalEntry.Parameter is Single)
            {
                AddParameterToNavigationStateString(sb, 8, valueString);
                return;
            }
            if (journalEntry.Parameter is Double)
            {
                AddParameterToNavigationStateString(sb, 9, valueString);
                return;
            }
            if (journalEntry.Parameter is Char)
            {
                AddParameterToNavigationStateString(sb, 10, valueString);
                return;
            }
            if (journalEntry.Parameter is Boolean)
            {
                AddParameterToNavigationStateString(sb, 11, valueString);
                return;
            }
            if (journalEntry.Parameter is String)
            {
                AddParameterToNavigationStateString(sb, 12, valueString);
                return;
            }
            if (journalEntry.Parameter is Guid)
            {
                AddParameterToNavigationStateString(sb, 16, valueString);
            }
        }
        private async Task <bool> NavigateCoreAsync(
            Type sourcePageType, object parameter, NavigationMode navigationMode)
        {
            _isNavigating         = true;
            this.CanNavigate      = false;
            this.CanGoBack        = false;
            this.CanGoForward     = false;
            this.IsHitTestVisible = false;

            // Make sure we run on UI thread
            if (!Dispatcher.HasThreadAccess)
            {
                Task <bool> navigateCoreTask = null;
                await Dispatcher.RunAsync(
                    CoreDispatcherPriority.High,
                    () => navigateCoreTask = this.NavigateCoreAsync(sourcePageType, parameter, navigationMode));

                return(await navigateCoreTask);
            }

            try
            {
                await _waitForApplyTemplateTaskSource.Task;
                await this.WaitForLoadedAsync();

                AlternativePage currentPage = null;

                #region OnNavigatingFrom part
                if (_currentPagePresenter != null)
                {
                    currentPage = (AlternativePage)_currentPagePresenter.Content;
                    var cancelArgs =
                        new AlternativeNavigatingCancelEventArgs(
                            navigationMode,
                            sourcePageType);
                    await this.OnNavigatingAsync(cancelArgs);

                    if (!cancelArgs.Cancel)
                    {
                        await currentPage.OnNavigatingFromInternalAsync(cancelArgs);
                    }

                    if (cancelArgs.Cancel)
                    {
                        return(false);
                    }
                }
                #endregion

                #region Initializing new page instance part
                var je = new JournalEntry {
                    SourcePageType = sourcePageType, Parameter = parameter
                };
                AlternativePage  newPage;
                ContentPresenter newPagePresenter;

                if (_preloadedPageCache.ContainsKey(je))
                {
                    newPagePresenter = _preloadedPageCache[je];
                    newPage          = (AlternativePage)newPagePresenter.Content;
                    _preloadedPageCache.Remove(je);
                }
                else
                {
                    newPage = _frameCache.Get(je.SourcePageType);

                    if (newPage == null)
                    {
                        throw new InvalidOperationException(
                                  "Pages used in AlternativeFrame need to be of AlternativePage type.");
                    }

                    newPage.Frame    = this;
                    newPagePresenter = new ContentPresenter {
                        Style = PagePresenterStyle
                    };
                    newPagePresenter.Content = newPage;
                    _pagePresentersPanel.Children.Add(newPagePresenter);
                }

                newPagePresenter.Opacity = 0.005;

                await UnloadAllPreloadedAsync();

                #endregion

                #region OnNavigatingTo part
                var args = new AlternativeNavigationEventArgs(
                    newPage.Content, navigationMode, je.Parameter, je.SourcePageType);
                await newPage.OnNavigatingToInternalAsync(args);

                #endregion

                #region Journal Bookeeping part
                switch (navigationMode)
                {
                case NavigationMode.New:
                    this.ForwardStack.Clear();

                    if (this.CurrentJournalEntry != null)
                    {
                        this.BackStack.Push(this.CurrentJournalEntry);
                    }

                    break;

                case NavigationMode.Forward:
                    this.ForwardStack.Pop();

                    if (this.CurrentJournalEntry != null)
                    {
                        this.BackStack.Push(this.CurrentJournalEntry);
                    }

                    break;

                case NavigationMode.Back:
                    this.BackStack.Pop();

                    if (this.CurrentJournalEntry != null)
                    {
                        this.ForwardStack.Push(this.CurrentJournalEntry);
                    }

                    break;
                }

                this.CurrentJournalEntry = je;
                #endregion

                #region OnNavigatedAsync~() calls
                this.UpdateCans();

                if (currentPage != null)
                {
                    await currentPage.OnNavigatedFromInternalAsync(args);
                }

                await this.OnNavigatedAsync(args);

                await newPage.OnNavigatedToInternalAsync(args);

                #endregion

                #region Transition part
                await newPagePresenter.WaitForLoadedAsync();

                await newPagePresenter.WaitForNonZeroSizeAsync();

                if (this.ShouldWaitForImagesToLoad == true &&
                    newPage.ShouldWaitForImagesToLoad != false ||
                    newPage.ShouldWaitForImagesToLoad == true &&
                    this.ShouldWaitForImagesToLoad != false)
                {
                    await newPage.WaitForImagesToLoadAsync(WaitForImagesToLoadTimeout);
                }

                newPagePresenter.Opacity = 1.0;

                if (navigationMode == NavigationMode.Back)
                {
                    await this.TransitionBackwardAsync(
                        currentPage,
                        newPage,
                        _currentPagePresenter,
                        newPagePresenter);
                }
                else
                {
                    await this.TransitionForwardAsync(
                        currentPage,
                        newPage,
                        _currentPagePresenter,
                        newPagePresenter);
                }

                if (_currentPagePresenter != null)
                {
                    _pagePresentersPanel.Children.Remove(_currentPagePresenter);
                    _frameCache.Store(currentPage);
                }

                _currentPagePresenter = newPagePresenter;
                #endregion

                return(true);
            }
            finally
            {
                this.IsHitTestVisible = true;
                _isNavigating         = false;
                this.UpdateCans();
                //DC.TraceLocalized(GetNavigationState());
            }
        }