public async Task Change(Route route)
        {
            SynchronizationContext.VerifyAccess();

            if (route == null)
            {
                throw new ArgumentNullException(nameof(route));
            }

            if (route == Current)
            {
                return;
            }

            if (route.Routes != null && route.Routes != this)
            {
                throw new ArgumentException(ErrorMessages.RoutesAssociatedWithOtherStack);
            }

            var completionSources = new List <TaskCompletionSource <object> >(stack.Count);
            var errors            = new List <RouteEventError>();

            using (Lock)
            {
                await ChangeRoute(route, completionSources, errors);
            }

            await OnRouteReady(route, RouteActivationMethod.Changed, errors);

            foreach (var completionSource in completionSources)
            {
                completionSource.SetCanceled();
            }
        }
        public async Task <object> Push(Route route, bool cacheCurrentView)
        {
            SynchronizationContext.VerifyAccess();

            if (route == null)
            {
                throw new ArgumentNullException(nameof(route));
            }

            if (route.Routes != null && route.Routes != this)
            {
                throw new ArgumentException(ErrorMessages.RoutesAssociatedWithOtherStack);
            }

            if (route is TransientRoute)
            {
                throw new ArgumentException("Cannot push a transient route.");
            }

            if (stack.Any(item => item.Route == route))
            {
                throw new InvalidOperationException("Cannot push same route instance multiple times.");
            }

            List <RouteEventError> errors;
            Task <object>          result;

            using (Lock)
            {
                errors = new List <RouteEventError>();
                result = (await PushRoute(route, cacheCurrentView, false, errors)).Task;
            }

            await OnRouteReady(route, RouteActivationMethod.Pushed, errors);

            return(await result);
        }
        public async Task <Route> Pop(object result)
        {
            SynchronizationContext.VerifyAccess();

            if (stack.Count <= 1)
            {
                throw new InvalidOperationException("Cannot pop base route.");
            }

            RouteItem poppedRoute;
            RouteItem nextRoute;
            List <RouteEventError> errors;

            using (Lock)
            {
                poppedRoute = stack.Pop();
                nextRoute   = stack.Peek();
                errors      = new List <RouteEventError>();

                try
                {
                    await poppedRoute.Route.OnRouteDeactivating(false);
                }
                catch (Exception ex)
                {
                    RouteErrorListener.TryOnRouteEventException(poppedRoute.Route, RouteEventType.Deactivating, ex);
                }

                try
                {
                    await nextRoute.Route.OnRouteRestoring(result);
                }
                catch (Exception ex)
                {
                    errors.Add(new RouteEventError(RouteEventType.Restoring, ex));
                    RouteErrorListener.TryOnRouteEventException(nextRoute.Route, RouteEventType.Restoring, ex);
                }

                poppedRoute.CachedView = null;
                var route = nextRoute.Route;
                if (nextRoute.CachedView == null)
                {
                    try
                    {
                        var view = route.CreateView(false);
                        nextRoute.CachedView = view;
                        var frameworkElement = view as FrameworkElement;
                        if (frameworkElement != null && frameworkElement.DataContext == null)
                        {
                            frameworkElement.DataContext = route;
                        }
                    }
                    catch (Exception ex)
                    {
                        errors.Add(new RouteEventError(RouteEventType.ViewCreation, ex));
                        RouteErrorListener.TryOnRouteEventException(route, RouteEventType.ViewCreation, ex);
                    }
                }

                RouteHead = nextRoute;

                try
                {
                    await poppedRoute.Route.OnRouteDeactivated(false);
                }
                catch (Exception ex)
                {
                    RouteErrorListener.TryOnRouteEventException(poppedRoute.Route, RouteEventType.Deactivated, ex);
                }

                try
                {
                    await nextRoute.Route.OnRouteRestored(result);
                }
                catch (Exception ex)
                {
                    errors.Add(new RouteEventError(RouteEventType.Restored, ex));
                    RouteErrorListener.TryOnRouteEventException(nextRoute.Route, RouteEventType.Restored, ex);
                }
            }

            await OnRouteReady(nextRoute.Route, RouteActivationMethod.Restored, errors);

            poppedRoute.CompletionSource.SetResult(result);
            return(nextRoute.Route);
        }