Code:
/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Framework / System / Windows / VisualStateManager.cs / 1651665 / VisualStateManager.cs
// -------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All Rights Reserved. // ------------------------------------------------------------------- using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Media.Animation; using MS.Internal; // ObservableCollectionDefaultValueFactory using System.Security; namespace System.Windows { ////// Manages visual states and their transitions on a control. /// public class VisualStateManager : DependencyObject { ////// Transitions a control's state. /// /// The control who's state is changing. /// The element to get the VSG & customer VSM from. /// The new state that the control is in. /// Whether to use transition animations. ///true if the state changed successfully, false otherwise. private static bool GoToStateCommon(FrameworkElement control, FrameworkElement stateGroupsRoot, string stateName, bool useTransitions) { if (stateName == null) { throw new ArgumentNullException("stateName"); } if (stateGroupsRoot == null) { return false; // Ignore state changes if a stateGroupsRoot doesn't exist yet } IListgroups = VisualStateManager.GetVisualStateGroupsInternal(stateGroupsRoot); if (groups == null) { return false; } VisualState state; VisualStateGroup group; VisualStateManager.TryGetState(groups, stateName, out group, out state); // Look for a custom VSM, and call it if it was found, regardless of whether the state was found or not. // This is because we don't know what the custom VSM will want to do. But for our default implementation, // we know that if we haven't found the state, we don't actually want to do anything. VisualStateManager customVsm = GetCustomVisualStateManager(stateGroupsRoot); if (customVsm != null) { return customVsm.GoToStateCore(control, stateGroupsRoot, stateName, group, state, useTransitions); } else if (state != null) { return GoToStateInternal(control, stateGroupsRoot, group, state, useTransitions); } return false; } /// /// Transitions a control's state. /// /// The control who's state is changing. /// The new state that the control is in. /// Whether to use transition animations. ///true if the state changed successfully, false otherwise. public static bool GoToState(FrameworkElement control, string stateName, bool useTransitions) { if (control == null) { throw new ArgumentNullException("control"); } FrameworkElement stateGroupsRoot = control.StateGroupsRoot; return GoToStateCommon(control, stateGroupsRoot, stateName, useTransitions); } ////// Transitions a control's state. /// /// The control who's state is changing. /// The new state that the control is in. /// Whether to use transition animations. ///true if the state changed successfully, false otherwise. public static bool GoToElementState(FrameworkElement stateGroupsRoot, string stateName, bool useTransitions) { if (stateGroupsRoot == null) { throw new ArgumentNullException("stateGroupsRoot"); } return GoToStateCommon(null, stateGroupsRoot, stateName, useTransitions); } ////// Allows subclasses to override the GoToState logic. /// protected virtual bool GoToStateCore(FrameworkElement control, FrameworkElement stateGroupsRoot, string stateName, VisualStateGroup group, VisualState state, bool useTransitions) { return GoToStateInternal(control, stateGroupsRoot, group, state, useTransitions); } #region CustomVisualStateManager public static readonly DependencyProperty CustomVisualStateManagerProperty = DependencyProperty.RegisterAttached( "CustomVisualStateManager", typeof(VisualStateManager), typeof(VisualStateManager), null); public static VisualStateManager GetCustomVisualStateManager(FrameworkElement obj) { if (obj == null) { throw new ArgumentNullException("obj"); } return obj.GetValue(VisualStateManager.CustomVisualStateManagerProperty) as VisualStateManager; } public static void SetCustomVisualStateManager(FrameworkElement obj, VisualStateManager value) { if (obj == null) { throw new ArgumentNullException("obj"); } obj.SetValue(VisualStateManager.CustomVisualStateManagerProperty, value); } #endregion #region VisualStateGroups private static readonly DependencyPropertyKey VisualStateGroupsPropertyKey = DependencyProperty.RegisterAttachedReadOnly( "VisualStateGroups", typeof(IList), typeof(VisualStateManager), new FrameworkPropertyMetadata(new ObservableCollectionDefaultValueFactory())); /// /// Read only VisualStateGroups property /// public static readonly DependencyProperty VisualStateGroupsProperty = VisualStateGroupsPropertyKey.DependencyProperty; internal static CollectionGetVisualStateGroupsInternal(FrameworkElement obj) { if (obj == null) { throw new ArgumentNullException("obj"); } // We don't want to get the default value because it will create/return an empty colleciton. bool hasModifiers; BaseValueSourceInternal source = obj.GetValueSource(VisualStateGroupsProperty, null, out hasModifiers); if (source != BaseValueSourceInternal.Default) { return obj.GetValue(VisualStateManager.VisualStateGroupsProperty) as Collection ; } return null; } [System.ComponentModel.DesignerSerializationVisibility(System.ComponentModel.DesignerSerializationVisibility.Content)] public static IList GetVisualStateGroups(FrameworkElement obj) { if (obj == null) { throw new ArgumentNullException("obj"); } return obj.GetValue(VisualStateManager.VisualStateGroupsProperty) as IList; } #endregion #region State Change internal static bool TryGetState(IList 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; } private static bool GoToStateInternal(FrameworkElement control, FrameworkElement stateGroupsRoot, VisualStateGroup group, VisualState state, bool useTransitions) { if (stateGroupsRoot == null) { throw new ArgumentNullException("stateGroupsRoot"); } 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(stateGroupsRoot, group, lastState, state) : null; // Generate dynamicTransition Storyboard Storyboard dynamicTransition = GenerateDynamicTransitionAnimations(stateGroupsRoot, 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(stateGroupsRoot, transition.Storyboard, state.Storyboard); } else { group.StartNewThenStopOld(stateGroupsRoot, state.Storyboard); } // Fire both CurrentStateChanging and CurrentStateChanged events group.RaiseCurrentStateChanging(stateGroupsRoot, lastState, state, control); group.RaiseCurrentStateChanged(stateGroupsRoot, 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, stateGroupsRoot, state, group)) { group.StartNewThenStopOld(stateGroupsRoot, state.Storyboard); } group.RaiseCurrentStateChanged(stateGroupsRoot, 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, stateGroupsRoot, state, group)) { group.StartNewThenStopOld(stateGroupsRoot, state.Storyboard); } group.RaiseCurrentStateChanged(stateGroupsRoot, 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(stateGroupsRoot, transition.Storyboard, dynamicTransition); group.RaiseCurrentStateChanging(stateGroupsRoot, lastState, state, control); } group.CurrentState = state; return true; } /// /// 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. Also if the group has already changed state, then /// don't start the new storyboards. /// ////// Critical - Accesses the PresentationSource /// TreatAsSafe - Does not expose any part of the PresentationSource to user input. /// [SecurityCritical, SecurityTreatAsSafe] 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.CriticalFromVisual(control) != null); } } if (stateGroupsRoot != null) { if (!stateGroupsRoot.IsVisible) { stateGroupsRootInTree = (PresentationSource.CriticalFromVisual(stateGroupsRoot) != null); } } return (controlInTree && stateGroupsRootInTree && (state == group.CurrentState)); } protected void RaiseCurrentStateChanging(VisualStateGroup stateGroup, VisualState oldState, VisualState newState, FrameworkElement control, FrameworkElement stateGroupsRoot) { if (stateGroup == null) { throw new ArgumentNullException("stateGroup"); } if (newState == null) { throw new ArgumentNullException("newState"); } if (stateGroupsRoot == null) { return; // Ignore if a ControlTemplate hasn't been applied } stateGroup.RaiseCurrentStateChanging(stateGroupsRoot, oldState, newState, control); } protected void RaiseCurrentStateChanged(VisualStateGroup stateGroup, VisualState oldState, VisualState newState, FrameworkElement control, FrameworkElement stateGroupsRoot) { if (stateGroup == null) { throw new ArgumentNullException("stateGroup"); } if (newState == null) { throw new ArgumentNullException("newState"); } if (stateGroupsRoot == null) { return; // Ignore if a ControlTemplate hasn't been applied } stateGroup.RaiseCurrentStateChanged(stateGroupsRoot, oldState, newState, control); } #endregion #region Transitions private static Storyboard GenerateDynamicTransitionAnimations(FrameworkElement root, VisualStateGroup group, VisualState newState, VisualTransition transition) { IEasingFunction easingFunction = null; Storyboard dynamic = new Storyboard(); if (transition != null) { if (transition.GeneratedDuration != null) { dynamic.Duration = transition.GeneratedDuration; } easingFunction = transition.GeneratedEasingFunction; } else { dynamic.Duration = new Duration(TimeSpan.Zero); } DictionarycurrentAnimations = FlattenTimelines(group.CurrentStoryboards); Dictionary transitionAnimations = FlattenTimelines(transition != null ? transition.Storyboard : null); Dictionary 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 pair in transitionAnimations) { currentAnimations.Remove(pair.Key); newStateAnimations.Remove(pair.Key); } // Generate the "to" animations foreach (KeyValuePair pair in newStateAnimations) { // The new "To" Animation -- the root is passed as a reference point for name // lookup. Timeline toAnimation = GenerateToAnimation(root, pair.Value, easingFunction, 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 pair in currentAnimations) { Timeline fromAnimation = GenerateFromAnimation(root, pair.Value, easingFunction); if (fromAnimation != null) { fromAnimation.Duration = dynamic.Duration; dynamic.Children.Add(fromAnimation); } } return dynamic; } private static Timeline GenerateFromAnimation(FrameworkElement root, Timeline timeline, IEasingFunction easingFunction) { Timeline result = null; if (timeline is ColorAnimation || timeline is ColorAnimationUsingKeyFrames) { result = new ColorAnimation() { EasingFunction = easingFunction }; } else if (timeline is DoubleAnimation || timeline is DoubleAnimationUsingKeyFrames) { result = new DoubleAnimation() { EasingFunction = easingFunction }; } else if (timeline is PointAnimation || timeline is PointAnimationUsingKeyFrames) { result = new PointAnimation() { EasingFunction = easingFunction }; } if (result != null) { CopyStoryboardTargetProperties(root, timeline, result); } // All other animation types are ignored. We will not build transitions for them, // but they will end up being executed. return result; } private static Timeline GenerateToAnimation(FrameworkElement root, Timeline timeline, IEasingFunction easingFunction, bool isEntering) { Timeline result = null; Color? targetColor = GetTargetColor(timeline, isEntering); if (targetColor.HasValue) { ColorAnimation ca = new ColorAnimation() { To = targetColor, EasingFunction = easingFunction }; result = ca; } if (result == null) { double? targetDouble = GetTargetDouble(timeline, isEntering); if (targetDouble.HasValue) { DoubleAnimation da = new DoubleAnimation() { To = targetDouble, EasingFunction = easingFunction }; result = da; } } if (result == null) { Point? targetPoint = GetTargetPoint(timeline, isEntering); if (targetPoint.HasValue) { PointAnimation pa = new PointAnimation() { To = targetPoint, EasingFunction = easingFunction }; result = pa; } } if (result != null) { CopyStoryboardTargetProperties(root, timeline, result); } return result; } private static void CopyStoryboardTargetProperties(FrameworkElement root, Timeline source, Timeline destination) { if (source != null || destination != null) { // Target takes priority over TargetName string targetName = Storyboard.GetTargetName(source); DependencyObject target = Storyboard.GetTarget(source); PropertyPath path = Storyboard.GetTargetProperty(source); if (target == null && !string.IsNullOrEmpty(targetName)) { target = root.FindName(targetName) as DependencyObject; } if (targetName != null) { Storyboard.SetTargetName(destination, targetName); } if (target != null) { Storyboard.SetTarget(destination, target); } if (path != null) { Storyboard.SetTargetProperty(destination, path); } } } /// /// Get the most appropriate transition between two states. /// /// Element being transitioned. /// Group being transitioned. /// VisualState being transitioned from. /// VisualState being transitioned to. ////// The most appropriate transition between the desired states. /// 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; IListtransitions = (IList )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; } #endregion #region GetTarget Methods // These methods are used when generating a transition animation between states. // The timeline is the "to" state, and we need to find the To value for the // animation we're generating. private static Color? GetTargetColor(Timeline timeline, bool isEntering) { ColorAnimation ca = timeline as ColorAnimation; if (ca != null) { return ca.From.HasValue ? ca.From : ca.To; } ColorAnimationUsingKeyFrames cak = timeline as ColorAnimationUsingKeyFrames; if (cak != null) { if (cak.KeyFrames.Count == 0) { return null; } ColorKeyFrame keyFrame = cak.KeyFrames[isEntering ? 0 : cak.KeyFrames.Count - 1]; return keyFrame.Value; } return null; } private static double? GetTargetDouble(Timeline timeline, bool isEntering) { DoubleAnimation da = timeline as DoubleAnimation; if (da != null) { return da.From.HasValue ? da.From : da.To; } DoubleAnimationUsingKeyFrames dak = timeline as DoubleAnimationUsingKeyFrames; if (dak != null) { if (dak.KeyFrames.Count == 0) { return null; } DoubleKeyFrame keyFrame = dak.KeyFrames[isEntering ? 0 : dak.KeyFrames.Count - 1]; return keyFrame.Value; } return null; } private static Point? GetTargetPoint(Timeline timeline, bool isEntering) { PointAnimation pa = timeline as PointAnimation; if (pa != null) { return pa.From.HasValue ? pa.From : pa.To; } PointAnimationUsingKeyFrames pak = timeline as PointAnimationUsingKeyFrames; if (pak != null) { if (pak.KeyFrames.Count == 0) { return null; } PointKeyFrame keyFrame = pak.KeyFrames[isEntering ? 0 : pak.KeyFrames.Count - 1]; return keyFrame.Value; } return null; } #endregion #region FlattenTimelines // These methods exist to put extract all animations from a Storyboard, and store them in // a Dictionary keyed on what element:property is being animated. Storyboards can contain // Storyboards, hence the "Flatten". private static Dictionary FlattenTimelines(Storyboard storyboard) { Dictionary result = new Dictionary (); FlattenTimelines(storyboard, result); return result; } private static Dictionary FlattenTimelines(Collection storyboards) { Dictionary result = new Dictionary (); for (int index = 0; index < storyboards.Count; ++index) { FlattenTimelines(storyboards[index], result); } return result; } private static void FlattenTimelines(Storyboard storyboard, Dictionary result) { if (storyboard == null) { return; } for (int index = 0; index < storyboard.Children.Count; ++index) { Timeline child = storyboard.Children[index]; Storyboard childStoryboard = child as Storyboard; if (childStoryboard != null) { FlattenTimelines(childStoryboard, result); } else { result[new TimelineDataToken(child)] = child; } } } // specifies a token to uniquely identify a Timeline object private struct TimelineDataToken : IEquatable { public TimelineDataToken(Timeline timeline) { _target = Storyboard.GetTarget(timeline); _targetName = Storyboard.GetTargetName(timeline); _targetProperty = Storyboard.GetTargetProperty(timeline); } public bool Equals(TimelineDataToken other) { bool targetsEqual = false; if (_targetName != null) { targetsEqual = other._targetName == _targetName; } else if (_target != null) { targetsEqual = other._target == _target; } else { targetsEqual = (other._target == null && other._targetName == null); } if (targetsEqual && (other._targetProperty.Path == _targetProperty.Path) && (other._targetProperty.PathParameters.Count == _targetProperty.PathParameters.Count)) { bool paramsEqual = true; for (int i = 0, count = _targetProperty.PathParameters.Count; i < count; i++) { if (other._targetProperty.PathParameters[i] != _targetProperty.PathParameters[i]) { paramsEqual = false; break; } } return paramsEqual; } return false; } public override int GetHashCode() { // // The below code has some limitations. We don't handle canonicalizing property paths, so // having two paths that target the same object/property can easily get different hash codes. // // For example the Opacity can be specified either from a string "Opacity" or via the string "(0)" // and a parameter Visual.OpacityPropety. These wont match as far as VSM is concerned. // int targetHash = _target != null ? _target.GetHashCode() : 0; int targetNameHash = _targetName != null ? _targetName.GetHashCode() : 0; int targetPropertyHash = (_targetProperty != null && _targetProperty.Path != null) ? _targetProperty.Path.GetHashCode() : 0; return ((_targetName != null) ? targetNameHash : targetHash) ^ targetPropertyHash; } private DependencyObject _target; private string _targetName; private PropertyPath _targetProperty; } #endregion #region Data private static readonly Duration DurationZero = new Duration(TimeSpan.Zero); #endregion } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- CellRelation.cs
- OwnerDrawPropertyBag.cs
- MatrixIndependentAnimationStorage.cs
- StoragePropertyMapping.cs
- BinaryFormatterWriter.cs
- OdbcError.cs
- ToolBarOverflowPanel.cs
- LicFileLicenseProvider.cs
- RelatedCurrencyManager.cs
- NetDataContractSerializer.cs
- HtmlControlPersistable.cs
- Buffer.cs
- CodeExpressionStatement.cs
- TreeWalkHelper.cs
- SettingsPropertyNotFoundException.cs
- AnnouncementInnerClient11.cs
- newitemfactory.cs
- NativeMethods.cs
- BamlCollectionHolder.cs
- SoapTransportImporter.cs
- EpmAttributeNameBuilder.cs
- DocumentEventArgs.cs
- AppDomainCompilerProxy.cs
- Token.cs
- ResourceSet.cs
- PropertyGridEditorPart.cs
- RouteItem.cs
- MSAANativeProvider.cs
- DataGridViewColumnEventArgs.cs
- SpAudioStreamWrapper.cs
- FixedNode.cs
- UpdateEventArgs.cs
- FragmentNavigationEventArgs.cs
- ContentType.cs
- EntityDataSourceViewSchema.cs
- TreeNodeConverter.cs
- Pens.cs
- TrackingAnnotationCollection.cs
- AppDomain.cs
- DefaultWorkflowLoaderService.cs
- ApplicationSecurityManager.cs
- HtmlImage.cs
- SqlDataSourceFilteringEventArgs.cs
- CardSpaceSelector.cs
- FormattedTextSymbols.cs
- securitycriticaldataformultiplegetandset.cs
- Rect3DConverter.cs
- VectorAnimation.cs
- ImpersonationContext.cs
- UpDownEvent.cs
- DataObjectSettingDataEventArgs.cs
- SessionSwitchEventArgs.cs
- Error.cs
- RelationshipManager.cs
- ServiceOperation.cs
- BitmapMetadataBlob.cs
- TransactionsSectionGroup.cs
- MemoryRecordBuffer.cs
- DbConnectionFactory.cs
- SecurityContext.cs
- BindingMAnagerBase.cs
- SlotInfo.cs
- BrushMappingModeValidation.cs
- EnvelopedSignatureTransform.cs
- OpCopier.cs
- QueueAccessMode.cs
- XmlArrayAttribute.cs
- DispatcherHookEventArgs.cs
- DataControlCommands.cs
- XpsSerializationException.cs
- HttpApplication.cs
- XmlILIndex.cs
- DocumentApplication.cs
- PhonemeConverter.cs
- WebPartMenu.cs
- EventDescriptor.cs
- ProfileEventArgs.cs
- ElementNotAvailableException.cs
- _Rfc2616CacheValidators.cs
- TableItemPatternIdentifiers.cs
- IProvider.cs
- SvcMapFile.cs
- SqlBulkCopy.cs
- SqlLiftWhereClauses.cs
- AtomMaterializer.cs
- PropertyTabChangedEvent.cs
- PointConverter.cs
- QueryOperationResponseOfT.cs
- TextSimpleMarkerProperties.cs
- MessageBox.cs
- RightsManagementLicense.cs
- FilterQuery.cs
- EventLog.cs
- StoreContentChangedEventArgs.cs
- SingleObjectCollection.cs
- HttpModuleAction.cs
- TextServicesCompartment.cs
- HtmlForm.cs
- SmiMetaDataProperty.cs
- FormatterServicesNoSerializableCheck.cs