using System; using System.Collections.Generic; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Data; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Media.Effects; using System.Windows.Media.Imaging; using HandyControl.Data; namespace HandyControl.Interactivity; public class ExtendedVisualStateManager : VisualStateManager { // Fields internal static readonly DependencyProperty CachedBackgroundProperty = DependencyProperty.RegisterAttached("CachedBackground", typeof(object), typeof(ExtendedVisualStateManager), new PropertyMetadata(null)); internal static readonly DependencyProperty CachedEffectProperty = DependencyProperty.RegisterAttached("CachedEffect", typeof(Effect), typeof(ExtendedVisualStateManager), new PropertyMetadata(null)); private static readonly List ChildAffectingLayoutProperties; internal static readonly DependencyProperty CurrentStateProperty = DependencyProperty.RegisterAttached("CurrentState", typeof(VisualState), typeof(ExtendedVisualStateManager), new PropertyMetadata(null)); internal static readonly DependencyProperty DidCacheBackgroundProperty = DependencyProperty.RegisterAttached("DidCacheBackground", typeof(bool), typeof(ExtendedVisualStateManager), new PropertyMetadata(ValueBoxes.FalseBox)); private static readonly List LayoutProperties; internal static readonly DependencyProperty LayoutStoryboardProperty = DependencyProperty.RegisterAttached("LayoutStoryboard", typeof(Storyboard), typeof(ExtendedVisualStateManager), new PropertyMetadata(null)); private static Storyboard LayoutTransitionStoryboard; private static List MovingElements; internal static readonly DependencyProperty OriginalLayoutValuesProperty = DependencyProperty.RegisterAttached("OriginalLayoutValues", typeof(List), typeof(ExtendedVisualStateManager), new PropertyMetadata(null)); public static readonly DependencyProperty RuntimeVisibilityPropertyProperty = DependencyProperty.RegisterAttached("RuntimeVisibilityProperty", typeof(DependencyProperty), typeof(ExtendedVisualStateManager), new PropertyMetadata(null)); public static readonly DependencyProperty TransitionEffectProperty = DependencyProperty.RegisterAttached("TransitionEffect", typeof(TransitionEffect), typeof(ExtendedVisualStateManager), new PropertyMetadata(null)); internal static readonly DependencyProperty TransitionEffectStoryboardProperty = DependencyProperty.RegisterAttached("TransitionEffectStoryboard", typeof(Storyboard), typeof(ExtendedVisualStateManager), new PropertyMetadata(null)); public static readonly DependencyProperty UseFluidLayoutProperty = DependencyProperty.RegisterAttached("UseFluidLayout", typeof(bool), typeof(ExtendedVisualStateManager), new PropertyMetadata(ValueBoxes.FalseBox)); private bool _changingState; // Methods static ExtendedVisualStateManager() { var list = new List { Grid.ColumnProperty, Grid.ColumnSpanProperty, Grid.RowProperty, Grid.RowSpanProperty, Canvas.LeftProperty, Canvas.TopProperty, FrameworkElement.WidthProperty, FrameworkElement.HeightProperty, FrameworkElement.MinWidthProperty, FrameworkElement.MinHeightProperty, FrameworkElement.MaxWidthProperty, FrameworkElement.MaxHeightProperty, FrameworkElement.MarginProperty, FrameworkElement.HorizontalAlignmentProperty, FrameworkElement.VerticalAlignmentProperty, UIElement.VisibilityProperty, StackPanel.OrientationProperty }; LayoutProperties = list; var list2 = new List { StackPanel.OrientationProperty }; ChildAffectingLayoutProperties = list2; } // Properties public static bool IsRunningFluidLayoutTransition => LayoutTransitionStoryboard != null; private static void AnimateTransitionEffect(FrameworkElement stateGroupsRoot, VisualTransition transition) { var element = new DoubleAnimation { Duration = transition.GeneratedDuration, EasingFunction = transition.GeneratedEasingFunction, From = 0.0, To = 1.0 }; var sb = new Storyboard { Duration = transition.GeneratedDuration, Children = { element } }; Storyboard.SetTarget(element, stateGroupsRoot); Storyboard.SetTargetProperty(element, new PropertyPath("(0).(1)", UIElement.EffectProperty, TransitionEffect.ProgressProperty)); if (stateGroupsRoot is Panel panel && panel.Background == null) { SetDidCacheBackground(panel, true); TransferLocalValue(panel, Panel.BackgroundProperty, CachedBackgroundProperty); panel.Background = Brushes.Transparent; } sb.Completed += delegate { if (Equals(GetTransitionEffectStoryboard(stateGroupsRoot), sb)) FinishTransitionEffectAnimation(stateGroupsRoot); }; SetTransitionEffectStoryboard(stateGroupsRoot, sb); sb.Begin(); } private static object CacheLocalValueHelper(DependencyObject dependencyObject, DependencyProperty property) { return dependencyObject.ReadLocalValue(property); } private static void control_LayoutUpdated(object sender, EventArgs e) { if (LayoutTransitionStoryboard != null) foreach (var element in MovingElements) { if (element.Parent is WrapperCanvas parent) { var layoutRect = GetLayoutRect(parent); var newRect = parent.NewRect; var renderTransform = parent.RenderTransform as TranslateTransform; var num = renderTransform?.X ?? 0.0; var num2 = renderTransform?.Y ?? 0.0; var num3 = newRect.Left - layoutRect.Left; var num4 = newRect.Top - layoutRect.Top; if (Math.Abs(num - num3) > 0.001 || Math.Abs(num2 - num4) > 0.001) { if (renderTransform == null) { renderTransform = new TranslateTransform(); parent.RenderTransform = renderTransform; } renderTransform.X = num3; renderTransform.Y = num4; } } } } private static void CopyLayoutProperties(FrameworkElement source, FrameworkElement target, bool restoring) { var canvas = restoring ? (WrapperCanvas) source : (WrapperCanvas) target; if (canvas.LocalValueCache == null) canvas.LocalValueCache = new Dictionary(); foreach (var property in LayoutProperties) if (!ChildAffectingLayoutProperties.Contains(property)) if (restoring) { ReplaceCachedLocalValueHelper(target, property, canvas.LocalValueCache[property]); } else { var obj2 = target.GetValue(property); var obj3 = CacheLocalValueHelper(source, property); canvas.LocalValueCache[property] = obj3; if (IsVisibilityProperty(property)) canvas.DestinationVisibilityCache = (Visibility) source.GetValue(property); else target.SetValue(property, source.GetValue(property)); source.SetValue(property, obj2); } } private static Storyboard CreateLayoutTransitionStoryboard(VisualTransition transition, List movingElements, Dictionary oldOpacities) { var duration = transition?.GeneratedDuration ?? new Duration(TimeSpan.Zero); var generatedEasingFunction = transition?.GeneratedEasingFunction; var storyboard = new Storyboard { Duration = duration }; foreach (var element in movingElements) { if (element.Parent is WrapperCanvas parent) { var animation = new DoubleAnimation { From = 1.0, To = 0.0, Duration = duration, EasingFunction = generatedEasingFunction }; Storyboard.SetTarget(animation, parent); Storyboard.SetTargetProperty(animation, new PropertyPath(WrapperCanvas.SimulationProgressProperty)); storyboard.Children.Add(animation); parent.SimulationProgress = 1.0; var newRect = parent.NewRect; if (!IsClose(parent.Width, newRect.Width)) { var animation3 = new DoubleAnimation { From = newRect.Width, To = newRect.Width, Duration = duration }; Storyboard.SetTarget(animation3, parent); Storyboard.SetTargetProperty(animation3, new PropertyPath(FrameworkElement.WidthProperty)); storyboard.Children.Add(animation3); } if (!IsClose(parent.Height, newRect.Height)) { var animation5 = new DoubleAnimation { From = newRect.Height, To = newRect.Height, Duration = duration }; Storyboard.SetTarget(animation5, parent); Storyboard.SetTargetProperty(animation5, new PropertyPath(FrameworkElement.HeightProperty)); storyboard.Children.Add(animation5); } if (parent.DestinationVisibilityCache == Visibility.Collapsed) { var margin = parent.Margin; if (!IsClose(margin.Left, 0.0) || !IsClose(margin.Top, 0.0) || !IsClose(margin.Right, 0.0) || !IsClose(margin.Bottom, 0.0)) { var frames = new ObjectAnimationUsingKeyFrames { Duration = duration }; var frame2 = new DiscreteObjectKeyFrame { KeyTime = TimeSpan.Zero }; var thickness2 = new Thickness(); frame2.Value = thickness2; var keyFrame = frame2; frames.KeyFrames.Add(keyFrame); Storyboard.SetTarget(frames, parent); Storyboard.SetTargetProperty(frames, new PropertyPath(FrameworkElement.MarginProperty)); storyboard.Children.Add(frames); } if (!IsClose(parent.MinWidth, 0.0)) { var animation7 = new DoubleAnimation { From = 0.0, To = 0.0, Duration = duration }; Storyboard.SetTarget(animation7, parent); Storyboard.SetTargetProperty(animation7, new PropertyPath(FrameworkElement.MinWidthProperty)); storyboard.Children.Add(animation7); } if (!IsClose(parent.MinHeight, 0.0)) { var animation9 = new DoubleAnimation { From = 0.0, To = 0.0, Duration = duration }; Storyboard.SetTarget(animation9, parent); Storyboard.SetTargetProperty(animation9, new PropertyPath(FrameworkElement.MinHeightProperty)); storyboard.Children.Add(animation9); } } } } foreach (var element2 in oldOpacities.Keys) { if (element2.Parent is WrapperCanvas canvas2) { var a = oldOpacities[element2]; var num2 = canvas2.DestinationVisibilityCache == Visibility.Visible ? 1.0 : 0.0; if (!IsClose(a, 1.0) || !IsClose(num2, 1.0)) { var animation11 = new DoubleAnimation { From = a, To = num2, Duration = duration, EasingFunction = generatedEasingFunction }; Storyboard.SetTarget(animation11, canvas2); Storyboard.SetTargetProperty(animation11, new PropertyPath(UIElement.OpacityProperty)); storyboard.Children.Add(animation11); } } } return storyboard; } private static Storyboard ExtractLayoutStoryboard(VisualState state) { Storyboard layoutStoryboard = null; if (state.Storyboard != null) { layoutStoryboard = GetLayoutStoryboard(state.Storyboard); if (layoutStoryboard == null) { layoutStoryboard = new Storyboard(); for (var i = state.Storyboard.Children.Count - 1; i >= 0; i--) { var timeline = state.Storyboard.Children[i]; if (LayoutPropertyFromTimeline(timeline, false) != null) { state.Storyboard.Children.RemoveAt(i); layoutStoryboard.Children.Add(timeline); } } SetLayoutStoryboard(state.Storyboard, layoutStoryboard); } } if (layoutStoryboard == null) return new Storyboard(); return layoutStoryboard; } private static List FindTargetElements(FrameworkElement control, FrameworkElement templateRoot, Storyboard layoutStoryboard, List originalValueRecords, List movingElements) { var list = new List(); if (movingElements != null) list.AddRange(movingElements); foreach (var timeline in layoutStoryboard.Children) { var item = (FrameworkElement) GetTimelineTarget(control, templateRoot, timeline); if (item != null) { if (!list.Contains(item)) list.Add(item); if (ChildAffectingLayoutProperties.Contains(LayoutPropertyFromTimeline(timeline, false))) { if (item is Panel panel) foreach (FrameworkElement element2 in panel.Children) if (!list.Contains(element2) && !(element2 is WrapperCanvas)) list.Add(element2); } } } foreach (var record in originalValueRecords) { if (!list.Contains(record.Element)) list.Add(record.Element); if (ChildAffectingLayoutProperties.Contains(record.Property)) { if (record.Element is Panel element) foreach (FrameworkElement element3 in element.Children) if (!list.Contains(element3) && !(element3 is WrapperCanvas)) list.Add(element3); } } for (var i = 0; i < list.Count; i++) { var reference = list[i]; var parent = VisualTreeHelper.GetParent(reference) as FrameworkElement; if (movingElements != null && movingElements.Contains(reference) && parent is WrapperCanvas) parent = VisualTreeHelper.GetParent(parent) as FrameworkElement; if (parent != null) { if (!list.Contains(parent)) list.Add(parent); for (var j = 0; j < VisualTreeHelper.GetChildrenCount(parent); j++) { if (VisualTreeHelper.GetChild(parent, j) is FrameworkElement child && !list.Contains(child) && !(child is WrapperCanvas)) list.Add(child); } } } return list; } private static VisualTransition FindTransition(VisualStateGroup group, VisualState previousState, VisualState state) { var str = previousState != null ? previousState.Name : string.Empty; var str2 = state != null ? state.Name : string.Empty; var num = -1; VisualTransition transition = null; foreach (VisualTransition transition2 in group.Transitions) { var num2 = 0; if (transition2.From == str) num2++; else if (!string.IsNullOrEmpty(transition2.From)) continue; if (transition2.To == str2) num2 += 2; else if (!string.IsNullOrEmpty(transition2.To)) continue; if (num2 > num) { num = num2; transition = transition2; } } return transition; } private static bool FinishesWithZeroOpacity(FrameworkElement control, FrameworkElement stateGroupsRoot, VisualState state, VisualState previousState) { if (state.Storyboard != null) foreach (var timeline in state.Storyboard.Children) if (TimelineIsAnimatingRootOpacity(timeline, control, stateGroupsRoot)) { var valueFromTimeline = GetValueFromTimeline(timeline, out var flag); return flag && valueFromTimeline is double d && Math.Abs(d) < 0.001; } if (previousState?.Storyboard == null) return Math.Abs(stateGroupsRoot.Opacity) < 0.001; foreach (var timeline2 in previousState.Storyboard.Children) TimelineIsAnimatingRootOpacity(timeline2, control, stateGroupsRoot); var animationBaseValue = (double) stateGroupsRoot.GetAnimationBaseValue(UIElement.OpacityProperty); return Math.Abs(animationBaseValue) < 0.001; } private static void FinishTransitionEffectAnimation(FrameworkElement stateGroupsRoot) { SetTransitionEffectStoryboard(stateGroupsRoot, null); TransferLocalValue(stateGroupsRoot, CachedEffectProperty, UIElement.EffectProperty); if (GetDidCacheBackground(stateGroupsRoot)) { TransferLocalValue(stateGroupsRoot, CachedBackgroundProperty, Panel.BackgroundProperty); SetDidCacheBackground(stateGroupsRoot, false); } } internal static object GetCachedBackground(DependencyObject obj) { return obj.GetValue(CachedBackgroundProperty); } internal static Effect GetCachedEffect(DependencyObject obj) { return (Effect) obj.GetValue(CachedEffectProperty); } internal static VisualState GetCurrentState(DependencyObject obj) { return (VisualState) obj.GetValue(CurrentStateProperty); } internal static bool GetDidCacheBackground(DependencyObject obj) { return (bool) obj.GetValue(DidCacheBackgroundProperty); } internal static Rect GetLayoutRect(FrameworkElement element) { var actualWidth = element.ActualWidth; var actualHeight = element.ActualHeight; if (element is Image || element is MediaElement) if (element.Parent is Canvas) { actualWidth = double.IsNaN(element.Width) ? actualWidth : element.Width; actualHeight = double.IsNaN(element.Height) ? actualHeight : element.Height; } else { actualWidth = element.RenderSize.Width; actualHeight = element.RenderSize.Height; } actualWidth = element.Visibility == Visibility.Collapsed ? 0.0 : actualWidth; actualHeight = element.Visibility == Visibility.Collapsed ? 0.0 : actualHeight; var margin = element.Margin; var layoutSlot = LayoutInformation.GetLayoutSlot(element); var x = 0.0; var y = 0.0; x = element.HorizontalAlignment switch { HorizontalAlignment.Left => layoutSlot.Left + margin.Left, HorizontalAlignment.Center => (layoutSlot.Left + margin.Left + layoutSlot.Right - margin.Right) / 2.0 - actualWidth / 2.0, HorizontalAlignment.Right => layoutSlot.Right - margin.Right - actualWidth, HorizontalAlignment.Stretch => Math.Max(layoutSlot.Left + margin.Left, (layoutSlot.Left + margin.Left + layoutSlot.Right - margin.Right) / 2.0 - actualWidth / 2.0), _ => x }; y = element.VerticalAlignment switch { VerticalAlignment.Top => layoutSlot.Top + margin.Top, VerticalAlignment.Center => (layoutSlot.Top + margin.Top + layoutSlot.Bottom - margin.Bottom) / 2.0 - actualHeight / 2.0, VerticalAlignment.Bottom => layoutSlot.Bottom - margin.Bottom - actualHeight, VerticalAlignment.Stretch => Math.Max(layoutSlot.Top + margin.Top, (layoutSlot.Top + margin.Top + layoutSlot.Bottom - margin.Bottom) / 2.0 - actualHeight / 2.0), _ => y }; return new Rect(x, y, actualWidth, actualHeight); } internal static Storyboard GetLayoutStoryboard(DependencyObject obj) { return (Storyboard) obj.GetValue(LayoutStoryboardProperty); } private static Dictionary GetOldOpacities(FrameworkElement control, FrameworkElement templateRoot, Storyboard layoutStoryboard, List originalValueRecords, List movingElements) { var dictionary = new Dictionary(); if (movingElements != null) foreach (var element in movingElements) { if (element.Parent is WrapperCanvas parent) dictionary.Add(element, parent.Opacity); } for (var i = originalValueRecords.Count - 1; i >= 0; i--) { var record = originalValueRecords[i]; if (IsVisibilityProperty(record.Property) && !dictionary.TryGetValue(record.Element, out var num2)) { num2 = (Visibility) record.Element.GetValue(record.Property) == Visibility.Visible ? 1.0 : 0.0; dictionary.Add(record.Element, num2); } } foreach (var timeline in layoutStoryboard.Children) { var key = (FrameworkElement) GetTimelineTarget(control, templateRoot, timeline); var property = LayoutPropertyFromTimeline(timeline, true); if (key != null && IsVisibilityProperty(property) && !dictionary.TryGetValue(key, out var num3)) { num3 = (Visibility) key.GetValue(property) == Visibility.Visible ? 1.0 : 0.0; dictionary.Add(key, num3); } } return dictionary; } internal static List GetOriginalLayoutValues(DependencyObject obj) { return (List) obj.GetValue(OriginalLayoutValuesProperty); } private static Dictionary GetRectsOfTargets(IEnumerable targets, ICollection movingElements) { var dictionary = new Dictionary(); foreach (var element in targets) { Rect layoutRect; if (movingElements != null && movingElements.Contains(element) && element.Parent is WrapperCanvas parent) { layoutRect = GetLayoutRect(parent); var renderTransform = parent.RenderTransform as TranslateTransform; var left = Canvas.GetLeft(element); var top = Canvas.GetTop(element); layoutRect = new Rect( layoutRect.Left + (double.IsNaN(left) ? 0.0 : left) + (renderTransform?.X ?? 0.0), layoutRect.Top + (double.IsNaN(top) ? 0.0 : top) + (renderTransform?.Y ?? 0.0), element.ActualWidth, element.ActualHeight); } else { layoutRect = GetLayoutRect(element); } dictionary.Add(element, layoutRect); } return dictionary; } public static DependencyProperty GetRuntimeVisibilityProperty(DependencyObject obj) { return (DependencyProperty) obj.GetValue(RuntimeVisibilityPropertyProperty); } private static object GetTimelineTarget(FrameworkElement control, FrameworkElement templateRoot, Timeline timeline) { var targetName = Storyboard.GetTargetName(timeline); if (string.IsNullOrEmpty(targetName)) return null; if (control is UserControl) return control.FindName(targetName); return templateRoot.FindName(targetName); } public static TransitionEffect GetTransitionEffect(DependencyObject obj) { return (TransitionEffect) obj.GetValue(TransitionEffectProperty); } internal static Storyboard GetTransitionEffectStoryboard(DependencyObject obj) { return (Storyboard) obj.GetValue(TransitionEffectStoryboardProperty); } public static bool GetUseFluidLayout(DependencyObject obj) { return (bool) obj.GetValue(UseFluidLayoutProperty); } private static object GetValueFromTimeline(Timeline timeline, out bool gotValue) { if (timeline is ObjectAnimationUsingKeyFrames frames) { gotValue = true; return frames.KeyFrames[0].Value; } if (timeline is DoubleAnimationUsingKeyFrames frames2) { gotValue = true; return frames2.KeyFrames[0].Value; } if (timeline is DoubleAnimation animation) { gotValue = true; return animation.To; } if (timeline is ThicknessAnimationUsingKeyFrames frames3) { gotValue = true; return frames3.KeyFrames[0].Value; } if (timeline is ThicknessAnimation animation2) { gotValue = true; return animation2.To; } if (timeline is Int32AnimationUsingKeyFrames frames4) { gotValue = true; return frames4.KeyFrames[0].Value; } if (timeline is Int32Animation animation3) { gotValue = true; return animation3.To; } gotValue = false; return null; } protected override bool GoToStateCore(FrameworkElement control, FrameworkElement stateGroupsRoot, string stateName, VisualStateGroup group, VisualState state, bool useTransitions) { if (_changingState) return false; var currentState = GetCurrentState(group); if (!Equals(currentState, state)) { var transition = FindTransition(group, currentState, state); var animateWithTransitionEffect = PrepareTransitionEffectImage(stateGroupsRoot, useTransitions, transition); if (!GetUseFluidLayout(group)) return TransitionEffectAwareGoToStateCore(control, stateGroupsRoot, stateName, group, state, useTransitions, transition, animateWithTransitionEffect, currentState); var layoutStoryboard = ExtractLayoutStoryboard(state); var originalLayoutValues = GetOriginalLayoutValues(group); if (originalLayoutValues == null) { originalLayoutValues = new List(); SetOriginalLayoutValues(group, originalLayoutValues); } if (!useTransitions) { if (LayoutTransitionStoryboard != null) StopAnimations(); var flag2 = TransitionEffectAwareGoToStateCore(control, stateGroupsRoot, stateName, group, state, false, transition, animateWithTransitionEffect, currentState); SetLayoutStoryboardProperties(control, stateGroupsRoot, layoutStoryboard, originalLayoutValues); return flag2; } if (layoutStoryboard.Children.Count == 0 && originalLayoutValues.Count == 0) return TransitionEffectAwareGoToStateCore(control, stateGroupsRoot, stateName, group, state, true, transition, animateWithTransitionEffect, currentState); try { _changingState = true; stateGroupsRoot.UpdateLayout(); var targets = FindTargetElements(control, stateGroupsRoot, layoutStoryboard, originalLayoutValues, MovingElements); var rectsOfTargets = GetRectsOfTargets(targets, MovingElements); var oldOpacities = GetOldOpacities(control, stateGroupsRoot, layoutStoryboard, originalLayoutValues, MovingElements); if (LayoutTransitionStoryboard != null) { stateGroupsRoot.LayoutUpdated -= control_LayoutUpdated; StopAnimations(); stateGroupsRoot.UpdateLayout(); } TransitionEffectAwareGoToStateCore(control, stateGroupsRoot, stateName, group, state, true, transition, animateWithTransitionEffect, currentState); SetLayoutStoryboardProperties(control, stateGroupsRoot, layoutStoryboard, originalLayoutValues); stateGroupsRoot.UpdateLayout(); var newRects = GetRectsOfTargets(targets, null); MovingElements = new List(); foreach (var element in targets) if (rectsOfTargets[element] != newRects[element]) MovingElements.Add(element); foreach (var element2 in oldOpacities.Keys) if (!MovingElements.Contains(element2)) MovingElements.Add(element2); WrapMovingElementsInCanvases(MovingElements, rectsOfTargets, newRects); stateGroupsRoot.LayoutUpdated += control_LayoutUpdated; LayoutTransitionStoryboard = CreateLayoutTransitionStoryboard(transition, MovingElements, oldOpacities); void Handler(object sender, EventArgs e) { stateGroupsRoot.LayoutUpdated -= control_LayoutUpdated; StopAnimations(); } LayoutTransitionStoryboard.Completed += Handler; LayoutTransitionStoryboard.Begin(); } finally { _changingState = false; } } return true; } private static bool IsClose(double a, double b) { return Math.Abs(a - b) < 1E-07; } private static bool IsVisibilityProperty(DependencyProperty property) { if (property != UIElement.VisibilityProperty) return property.Name == "RuntimeVisibility"; return true; } private static DependencyProperty LayoutPropertyFromTimeline(Timeline timeline, bool forceRuntimeProperty) { var targetProperty = Storyboard.GetTargetProperty(timeline); if (targetProperty != null && targetProperty.PathParameters.Count != 0) { if (targetProperty.PathParameters[0] is DependencyProperty item) { if (item.Name == "RuntimeVisibility" && item.OwnerType.Name.EndsWith("DesignTimeProperties", StringComparison.Ordinal)) { if (!LayoutProperties.Contains(item)) LayoutProperties.Add(item); if (!forceRuntimeProperty) return UIElement.VisibilityProperty; return item; } if (item.Name == "RuntimeWidth" && item.OwnerType.Name.EndsWith("DesignTimeProperties", StringComparison.Ordinal)) { if (!LayoutProperties.Contains(item)) LayoutProperties.Add(item); if (!forceRuntimeProperty) return FrameworkElement.WidthProperty; return item; } if (item.Name == "RuntimeHeight" && item.OwnerType.Name.EndsWith("DesignTimeProperties", StringComparison.Ordinal)) { if (!LayoutProperties.Contains(item)) LayoutProperties.Add(item); if (!forceRuntimeProperty) return FrameworkElement.HeightProperty; return item; } if (LayoutProperties.Contains(item)) return item; } } return null; } private static bool PrepareTransitionEffectImage(FrameworkElement stateGroupsRoot, bool useTransitions, VisualTransition transition) { var effect = transition == null ? null : GetTransitionEffect(transition); var flag = false; if (effect != null) { effect = effect.CloneCurrentValue(); if (useTransitions) { flag = true; var pixelWidth = (int) Math.Max(1.0, stateGroupsRoot.ActualWidth); var pixelHeight = (int) Math.Max(1.0, stateGroupsRoot.ActualHeight); var bitmap = new RenderTargetBitmap(pixelWidth, pixelHeight, 96.0, 96.0, PixelFormats.Pbgra32); bitmap.Render(stateGroupsRoot); var brush = new ImageBrush { ImageSource = bitmap }; effect.OldImage = brush; } var transitionEffectStoryboard = GetTransitionEffectStoryboard(stateGroupsRoot); if (transitionEffectStoryboard != null) { transitionEffectStoryboard.Stop(); FinishTransitionEffectAnimation(stateGroupsRoot); } if (useTransitions) { TransferLocalValue(stateGroupsRoot, UIElement.EffectProperty, CachedEffectProperty); stateGroupsRoot.Effect = effect; } } return flag; } private static void ReplaceCachedLocalValueHelper(FrameworkElement element, DependencyProperty property, object value) { if (value == DependencyProperty.UnsetValue) { element.ClearValue(property); } else { if (value is BindingExpressionBase base2) element.SetBinding(property, base2.ParentBindingBase); else element.SetValue(property, value); } } internal static void SetCachedBackground(DependencyObject obj, object value) { obj.SetValue(CachedBackgroundProperty, value); } internal static void SetCachedEffect(DependencyObject obj, Effect value) { obj.SetValue(CachedEffectProperty, value); } internal static void SetCurrentState(DependencyObject obj, VisualState value) { obj.SetValue(CurrentStateProperty, value); } internal static void SetDidCacheBackground(DependencyObject obj, bool value) { obj.SetValue(DidCacheBackgroundProperty, ValueBoxes.BooleanBox(value)); } internal static void SetLayoutStoryboard(DependencyObject obj, Storyboard value) { obj.SetValue(LayoutStoryboardProperty, value); } private static void SetLayoutStoryboardProperties(FrameworkElement control, FrameworkElement templateRoot, Storyboard layoutStoryboard, List originalValueRecords) { foreach (var record in originalValueRecords) ReplaceCachedLocalValueHelper(record.Element, record.Property, record.Value); originalValueRecords.Clear(); foreach (var timeline in layoutStoryboard.Children) { var dependencyObject = (FrameworkElement) GetTimelineTarget(control, templateRoot, timeline); var property = LayoutPropertyFromTimeline(timeline, true); if (dependencyObject != null && property != null) { var valueFromTimeline = GetValueFromTimeline(timeline, out var flag); if (flag) { var item = new OriginalLayoutValueRecord { Element = dependencyObject, Property = property, Value = CacheLocalValueHelper(dependencyObject, property) }; originalValueRecords.Add(item); dependencyObject.SetValue(property, valueFromTimeline); } } } } internal static void SetOriginalLayoutValues(DependencyObject obj, List value) { obj.SetValue(OriginalLayoutValuesProperty, value); } public static void SetRuntimeVisibilityProperty(DependencyObject obj, DependencyProperty value) { obj.SetValue(RuntimeVisibilityPropertyProperty, value); } public static void SetTransitionEffect(DependencyObject obj, TransitionEffect value) { obj.SetValue(TransitionEffectProperty, value); } internal static void SetTransitionEffectStoryboard(DependencyObject obj, Storyboard value) { obj.SetValue(TransitionEffectStoryboardProperty, value); } public static void SetUseFluidLayout(DependencyObject obj, bool value) { obj.SetValue(UseFluidLayoutProperty, ValueBoxes.BooleanBox(value)); } private static void StopAnimations() { if (LayoutTransitionStoryboard != null) { LayoutTransitionStoryboard.Stop(); LayoutTransitionStoryboard = null; } if (MovingElements != null) { UnwrapMovingElementsFromCanvases(MovingElements); MovingElements = null; } } private static bool TimelineIsAnimatingRootOpacity(Timeline timeline, FrameworkElement control, FrameworkElement stateGroupsRoot) { if (!Equals(GetTimelineTarget(control, stateGroupsRoot, timeline), stateGroupsRoot)) return false; var targetProperty = Storyboard.GetTargetProperty(timeline); return targetProperty != null && targetProperty.PathParameters.Count != 0 && targetProperty.PathParameters[0] == UIElement.OpacityProperty; } private static void TransferLocalValue(FrameworkElement element, DependencyProperty sourceProperty, DependencyProperty destProperty) { var obj2 = CacheLocalValueHelper(element, sourceProperty); ReplaceCachedLocalValueHelper(element, destProperty, obj2); } private bool TransitionEffectAwareGoToStateCore(FrameworkElement control, FrameworkElement stateGroupsRoot, string stateName, VisualStateGroup group, VisualState state, bool useTransitions, VisualTransition transition, bool animateWithTransitionEffect, VisualState previousState) { IEasingFunction generatedEasingFunction = null; if (animateWithTransitionEffect) { generatedEasingFunction = transition.GeneratedEasingFunction; var function2 = new DummyEasingFunction { DummyValue = FinishesWithZeroOpacity(control, stateGroupsRoot, state, previousState) ? 0.01 : 0.0 }; transition.GeneratedEasingFunction = function2; } var flag = base.GoToStateCore(control, stateGroupsRoot, stateName, group, state, useTransitions); if (animateWithTransitionEffect) { transition.GeneratedEasingFunction = generatedEasingFunction; if (flag) AnimateTransitionEffect(stateGroupsRoot, transition); } SetCurrentState(group, state); return flag; } private static void UnwrapMovingElementsFromCanvases(List movingElements) { foreach (var element in movingElements) { if (element.Parent is WrapperCanvas parent) { var obj2 = CacheLocalValueHelper(element, FrameworkElement.DataContextProperty); element.DataContext = element.DataContext; var element2 = VisualTreeHelper.GetParent(parent) as FrameworkElement; parent.Children.Remove(element); if (element2 is Panel panel) { var index = panel.Children.IndexOf(parent); panel.Children.RemoveAt(index); panel.Children.Insert(index, element); } else { if (element2 is Decorator decorator) decorator.Child = element; } CopyLayoutProperties(parent, element, true); ReplaceCachedLocalValueHelper(element, FrameworkElement.DataContextProperty, obj2); } } } private static void WrapMovingElementsInCanvases(List movingElements, Dictionary oldRects, Dictionary newRects) { foreach (var element in movingElements) { var parent = VisualTreeHelper.GetParent(element) as FrameworkElement; var canvas = new WrapperCanvas { OldRect = oldRects[element], NewRect = newRects[element] }; var obj2 = CacheLocalValueHelper(element, FrameworkElement.DataContextProperty); element.DataContext = element.DataContext; var flag = true; if (parent is Panel panel && !panel.IsItemsHost) { var index = panel.Children.IndexOf(element); panel.Children.RemoveAt(index); panel.Children.Insert(index, canvas); } else { if (parent is Decorator decorator) decorator.Child = canvas; else flag = false; } if (flag) { canvas.Children.Add(element); CopyLayoutProperties(element, canvas, false); ReplaceCachedLocalValueHelper(element, FrameworkElement.DataContextProperty, obj2); } } } // Nested Types private class DummyEasingFunction : EasingFunctionBase { // Fields public static readonly DependencyProperty DummyValueProperty = DependencyProperty.Register("DummyValue", typeof(double), typeof(DummyEasingFunction), new PropertyMetadata(0.0)); // Properties public double DummyValue { private get => (double) GetValue(DummyValueProperty); set => SetValue(DummyValueProperty, value); } // Methods protected override Freezable CreateInstanceCore() { return new DummyEasingFunction(); } protected override double EaseInCore(double normalizedTime) { return DummyValue; } } internal class OriginalLayoutValueRecord { // Properties public FrameworkElement Element { get; set; } public DependencyProperty Property { get; set; } public object Value { get; set; } } internal class WrapperCanvas : Canvas { // Fields internal static readonly DependencyProperty SimulationProgressProperty = DependencyProperty.Register("SimulationProgress", typeof(double), typeof(WrapperCanvas), new PropertyMetadata(0.0, SimulationProgressChanged)); // Properties public Visibility DestinationVisibilityCache { get; set; } public Dictionary LocalValueCache { get; set; } public Rect NewRect { get; set; } public Rect OldRect { get; set; } public double SimulationProgress { get => (double) GetValue(SimulationProgressProperty); set => SetValue(SimulationProgressProperty, value); } // Methods private static void SimulationProgressChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var canvas = d as WrapperCanvas; var newValue = (double) e.NewValue; if (canvas != null && canvas.Children.Count > 0) { if (canvas.Children[0] is FrameworkElement element) { element.Width = Math.Max(0.0, canvas.OldRect.Width * newValue + canvas.NewRect.Width * (1.0 - newValue)); element.Height = Math.Max(0.0, canvas.OldRect.Height * newValue + canvas.NewRect.Height * (1.0 - newValue)); SetLeft(element, newValue * (canvas.OldRect.Left - canvas.NewRect.Left)); SetTop(element, newValue * (canvas.OldRect.Top - canvas.NewRect.Top)); } } } } }