/// <summary> /// Allows subclasses to override the GoToState logic. /// </summary> protected virtual bool GoToStateCore(Control control, FrameworkElement templateRoot, string stateName, VisualStateGroup group, VisualState state, bool useTransitions) { return(GoToStateInternal(control, templateRoot, group, state, useTransitions)); }
/// <summary> /// Get the most appropriate transition between two states. /// </summary> /// <param name="element">Element being transitioned.</param> /// <param name="group">Group being transitioned.</param> /// <param name="from">VisualState being transitioned from.</param> /// <param name="to">VisualState being transitioned to.</param> /// <returns> /// The most appropriate transition between the desired states. /// </returns> internal static VisualTransition GetTransition(FrameworkElement element, VisualStateGroup group, VisualState from, VisualState to) { if (element == null) { throw new ArgumentNullException("element"); } if (group == null) { throw new ArgumentNullException("group"); } if (to == null) { throw new ArgumentNullException("to"); } VisualTransition best = null; VisualTransition defaultTransition = null; int bestScore = -1; IList <VisualTransition> transitions = (IList <VisualTransition>)group.Transitions; if (transitions != null) { foreach (VisualTransition transition in transitions) { if (defaultTransition == null && transition.IsDefault) { defaultTransition = transition; continue; } int score = -1; VisualState transitionFromState = group.GetState(transition.From); VisualState transitionToState = group.GetState(transition.To); if (from == transitionFromState) { score += 1; } else if (transitionFromState != null) { continue; } if (to == transitionToState) { score += 2; } else if (transitionToState != null) { continue; } if (score > bestScore) { bestScore = score; best = transition; } } } return(best ?? defaultTransition); }
private static Storyboard GenerateDynamicTransitionAnimations(FrameworkElement root, VisualStateGroup group, VisualState newState, VisualTransition transition) { Storyboard dynamic = new Storyboard(); if (transition != null && transition.GeneratedDuration != null) { dynamic.Duration = transition.GeneratedDuration; } else { dynamic.Duration = new Duration(TimeSpan.Zero); } Dictionary <TimelineDataToken, Timeline> currentAnimations = FlattenTimelines(group.CurrentStoryboards); Dictionary <TimelineDataToken, Timeline> transitionAnimations = FlattenTimelines(transition != null ? transition.Storyboard : null); Dictionary <TimelineDataToken, Timeline> newStateAnimations = FlattenTimelines(newState.Storyboard); // Remove any animations that the transition already animates. // There is no need to create an interstitial animation if one already exists. foreach (KeyValuePair <TimelineDataToken, Timeline> pair in transitionAnimations) { currentAnimations.Remove(pair.Key); newStateAnimations.Remove(pair.Key); } // Generate the "to" animations foreach (KeyValuePair <TimelineDataToken, Timeline> pair in newStateAnimations) { // The new "To" Animation -- the root is passed as a reference point for name // lookup. Timeline toAnimation = GenerateToAnimation(root, pair.Value, true); // If the animation is of a type that we can't generate transition animations // for, GenerateToAnimation will return null, and we should just keep going. if (toAnimation != null) { toAnimation.Duration = dynamic.Duration; dynamic.Children.Add(toAnimation); } // Remove this from the list of current state animations we have to consider next currentAnimations.Remove(pair.Key); } // Generate the "from" animations foreach (KeyValuePair <TimelineDataToken, Timeline> pair in currentAnimations) { Timeline fromAnimation = GenerateFromAnimation(root, pair.Value); if (fromAnimation != null) { fromAnimation.Duration = dynamic.Duration; dynamic.Children.Add(fromAnimation); } } return(dynamic); }
/// <summary> /// If the stateGroupsRoot or control is removed from the tree, then the new /// storyboards will not be able to resolve target names. Thus, /// if the stateGroupsRoot or control is not in the tree, don't start the new /// storyboards. /// </summary> private static bool ShouldRunStateStoryboard(FrameworkElement control, FrameworkElement stateGroupsRoot, VisualState state, VisualStateGroup group) { bool controlInTree = true; bool stateGroupsRootInTree = true; // We cannot simply check control.IsLoaded because the control may not be in the visual tree // even though IsLoaded is true. Instead we will check that it can find a PresentationSource // which would tell us it's in the visual tree. if (control != null) { // If it's visible then it's in the visual tree, so we don't even have to look for a // PresentationSource if (!control.IsVisible) { controlInTree = (PresentationSource.FromVisual(control) != null); } } if (stateGroupsRoot != null) { if (!stateGroupsRoot.IsVisible) { stateGroupsRootInTree = (PresentationSource.FromVisual(stateGroupsRoot) != null); } } return(controlInTree && stateGroupsRootInTree && (state == group.CurrentState)); }
private static bool GoToStateInternal(Control control, FrameworkElement element, VisualStateGroup group, VisualState state, bool useTransitions) { if (element == null) { throw new ArgumentNullException("element"); } if (state == null) { throw new ArgumentNullException("state"); } if (group == null) { throw new InvalidOperationException(); } VisualState lastState = group.CurrentState; if (lastState == state) { return(true); } // Get the transition Storyboard. Even if there are no transitions specified, there might // be properties that we're rolling back to their default values. VisualTransition transition = useTransitions ? VisualStateManager.GetTransition(element, group, lastState, state) : null; // Generate dynamicTransition Storyboard Storyboard dynamicTransition = GenerateDynamicTransitionAnimations(element, group, state, transition); // If the transition is null, then we want to instantly snap. The dynamicTransition will // consist of everything that is being moved back to the default state. // If the transition.Duration and explicit storyboard duration is zero, then we want both the dynamic // and state Storyboards to happen in the same tick, so we start them at the same time. if (transition == null || (transition.GeneratedDuration == DurationZero && (transition.Storyboard == null || transition.Storyboard.Duration == DurationZero))) { // Start new state Storyboard and stop any previously running Storyboards if (transition != null && transition.Storyboard != null) { group.StartNewThenStopOld(element, transition.Storyboard, state.Storyboard); } else { group.StartNewThenStopOld(element, state.Storyboard); } // Fire both CurrentStateChanging and CurrentStateChanged events group.RaiseCurrentStateChanging(element, lastState, state, control); group.RaiseCurrentStateChanged(element, lastState, state, control); } else { // In this case, we have an interstitial storyboard of duration > 0 and/or // explicit storyboard of duration >0 , so we need // to run them first, and then we'll run the state storyboard. // we have to wait for both storyboards to complete before // starting the steady state animations. transition.DynamicStoryboardCompleted = false; // Hook up generated Storyboard's Completed event handler dynamicTransition.Completed += delegate(object sender, EventArgs e) { if (transition.Storyboard == null || transition.ExplicitStoryboardCompleted) { if (ShouldRunStateStoryboard(control, element, state, group)) { group.StartNewThenStopOld(element, state.Storyboard); } group.RaiseCurrentStateChanged(element, lastState, state, control); } transition.DynamicStoryboardCompleted = true; }; if (transition.Storyboard != null && transition.ExplicitStoryboardCompleted == true) { EventHandler transitionCompleted = null; transitionCompleted = new EventHandler(delegate(object sender, EventArgs e) { if (transition.DynamicStoryboardCompleted) { if (ShouldRunStateStoryboard(control, element, state, group)) { group.StartNewThenStopOld(element, state.Storyboard); } group.RaiseCurrentStateChanged(element, lastState, state, control); } transition.Storyboard.Completed -= transitionCompleted; transition.ExplicitStoryboardCompleted = true; }); // hook up explicit storyboard's Completed event handler transition.ExplicitStoryboardCompleted = false; transition.Storyboard.Completed += transitionCompleted; } // Start transition and dynamicTransition Storyboards // Stop any previously running Storyboards group.StartNewThenStopOld(element, transition.Storyboard, dynamicTransition); group.RaiseCurrentStateChanging(element, lastState, state, control); } group.CurrentState = state; return(true); }
internal static bool TryGetState(IList <VisualStateGroup> groups, string stateName, out VisualStateGroup group, out VisualState state) { for (int groupIndex = 0; groupIndex < groups.Count; ++groupIndex) { VisualStateGroup g = groups[groupIndex]; VisualState s = g.GetState(stateName); if (s != null) { group = g; state = s; return(true); } } group = null; state = null; return(false); }