protected override bool GoToStateCore()

in src/Microsoft.Xaml.Behaviors/Core/ExtendedVisualStateManager.cs [244:431]


        protected override bool GoToStateCore(FrameworkElement control, FrameworkElement stateGroupsRoot, string stateName, VisualStateGroup group, VisualState state, bool useTransitions)
        {
            //
            // Reminder that a layout transition may already be running; several of these functions keep track of the current value of MovingElements
            // so that they can account for the fact that these elements have unusual layout positions right now.
            //

            Storyboard layoutStoryboard;

            // On WPF 4 there's an open bug (882549) where platform controls reassert all states every measure, and at designtime this is a problem because the CommonStates
            // can't possibly be right. Fix is to inhibit a state change when we are in the middle of setting up a FluidLayout change.
            if (this.changingState)
            {
                return false;
            }

            if (group == null || state == null)
            {
                return false;
            }

            //
            // Keep our own copy of the current state
            //
            VisualState previousState = GetCurrentState(group);

            if (previousState == state)
            {
                return true;
            }

            //
            // Find the transition that should be used
            //
            VisualTransition transition = FindTransition(group, previousState, state);

            bool animateWithTransitionEffect = PrepareTransitionEffectImage(stateGroupsRoot, useTransitions, transition);

            //
            // If this group is not using Fluid Layout, then get out
            //
            if (!GetUseFluidLayout(group))
            {
                return this.TransitionEffectAwareGoToStateCore(control, stateGroupsRoot, stateName, group, state, useTransitions, transition, animateWithTransitionEffect, previousState);
            }

            //
            // Get all layout properties out of the state's storyboard. This is only performed once per state thanks to an attached property on the state.
            //
            layoutStoryboard = ExtractLayoutStoryboard(state);

            //
            // Make sure that we have a place to store the original values for anything that we might overwrite
            //
            List<OriginalLayoutValueRecord> originalValueRecords = GetOriginalLayoutValues(group);
            if (originalValueRecords == null)
            {
                originalValueRecords = new List<OriginalLayoutValueRecord>();
                SetOriginalLayoutValues(group, originalValueRecords);
            }

            //
            // Take the easy road if we don't have to animate - this is a compressed version of what's below
            //
            if (!useTransitions)
            {
                if (LayoutTransitionStoryboard != null)
                {
                    StopAnimations();
                }
                bool returnValue = this.TransitionEffectAwareGoToStateCore(control, stateGroupsRoot, stateName, group, state, useTransitions, transition, animateWithTransitionEffect, previousState);

                SetLayoutStoryboardProperties(control, stateGroupsRoot, layoutStoryboard, originalValueRecords);
                return returnValue;
            }

            if (layoutStoryboard.Children.Count == 0 && originalValueRecords.Count == 0)
            {
                return this.TransitionEffectAwareGoToStateCore(control, stateGroupsRoot, stateName, group, state, useTransitions, transition, animateWithTransitionEffect, previousState);
            }

            try
            {
                this.changingState = true;

                // Force layout to be updated first - helps with OnLoaded() animations.
                stateGroupsRoot.UpdateLayout();

                //
                // Enumerate elements in the state (and the previous state), then expand that list to contain anything that might move
                // as a result of these elements changing size:
                //   - siblings of elements in the list
                //   - parents of elements in the list
                //   - siblings of parents in the list
                //   - grandparents and their siblings
                //   - etc.
                //   - no need to travel *down* the tree, if a parent changes size then the children will move
                //
                List<FrameworkElement> targetElements = FindTargetElements(control, stateGroupsRoot, layoutStoryboard, originalValueRecords, MovingElements);

                //
                // Get the parent-relative rect of every element in the list, and the original effective opacity (= opacity * visibility, more or less)
                //  - Assume that every Visibility change is an intended animation, unlike the work we do to filter the set of elements that actually moved
                //
                Dictionary<FrameworkElement, Rect> oldRects = GetRectsOfTargets(targetElements, MovingElements);
                Dictionary<FrameworkElement, double> oldOpacities = GetOldOpacities(control, stateGroupsRoot, layoutStoryboard, originalValueRecords, MovingElements);

                //
                // Now that we've captured the current situation, stop the previous transition before going to the new state
                //
                if (LayoutTransitionStoryboard != null)
                {
                    stateGroupsRoot.LayoutUpdated -= new EventHandler(control_LayoutUpdated);
                    StopAnimations();
                    stateGroupsRoot.UpdateLayout();
                }

                //
                // Go to the new state; jump immediately to the layout changes
                //
                this.TransitionEffectAwareGoToStateCore(control, stateGroupsRoot, stateName, group, state, useTransitions, transition, animateWithTransitionEffect, previousState);
                SetLayoutStoryboardProperties(control, stateGroupsRoot, layoutStoryboard, originalValueRecords);

                //
                // UpdateLayout
                //
                stateGroupsRoot.UpdateLayout();

                //
                // Get the parent-relative rect of every element in the list
                //  - Note: Do not need the new visibility since we can just read the property
                //
                Dictionary<FrameworkElement, Rect> newRects = GetRectsOfTargets(targetElements, null);

                //
                // Compute the set of elements from the list whose rects changed
                //
                MovingElements = new List<FrameworkElement>();

                foreach (FrameworkElement target in targetElements)
                {
                    if (oldRects[target] != newRects[target])
                    {
                        MovingElements.Add(target);
                    }
                }

                //
                // Add the elements whose opacity is changing, so we can change opacity on the wrapper and not the element
                //
                foreach (FrameworkElement visibilityElement in oldOpacities.Keys)
                {
                    if (!MovingElements.Contains(visibilityElement))
                    {
                        MovingElements.Add(visibilityElement);
                    }
                }

                //
                // Freeze these elements at their current location, otherwise detach from layout
                //  - For now, wrap each of these elements in a Canvas
                //
                WrapMovingElementsInCanvases(MovingElements, oldRects, newRects);

                stateGroupsRoot.LayoutUpdated += new EventHandler(control_LayoutUpdated);

                //
                // Animate the size/location of these elements from old rect to new rect
                //  - NOT with scale transforms, though translate transforms are OK
                //  - changing the size of the element should call measure/arrange on any of its children that are not themselves detached from layout
                //
                LayoutTransitionStoryboard = CreateLayoutTransitionStoryboard(transition, MovingElements, oldOpacities);

                LayoutTransitionStoryboard.Completed += (EventHandler)delegate (object sender, EventArgs args)
                {
                    stateGroupsRoot.LayoutUpdated -= new EventHandler(control_LayoutUpdated);
                    StopAnimations();
                };

                LayoutTransitionStoryboard.Begin();
            }
            finally
            {
                this.changingState = false;
            }

            return true;
        }