1142 lines
50 KiB
C#
1142 lines
50 KiB
C#
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<DependencyProperty> 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<DependencyProperty> LayoutProperties;
|
|
|
|
internal static readonly DependencyProperty LayoutStoryboardProperty =
|
|
DependencyProperty.RegisterAttached("LayoutStoryboard", typeof(Storyboard),
|
|
typeof(ExtendedVisualStateManager), new PropertyMetadata(null));
|
|
|
|
private static Storyboard LayoutTransitionStoryboard;
|
|
private static List<FrameworkElement> MovingElements;
|
|
|
|
internal static readonly DependencyProperty OriginalLayoutValuesProperty =
|
|
DependencyProperty.RegisterAttached("OriginalLayoutValues", typeof(List<OriginalLayoutValueRecord>),
|
|
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<DependencyProperty>
|
|
{
|
|
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<DependencyProperty>
|
|
{
|
|
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<DependencyProperty, object>();
|
|
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<FrameworkElement> movingElements, Dictionary<FrameworkElement, double> 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<FrameworkElement> FindTargetElements(FrameworkElement control,
|
|
FrameworkElement templateRoot, Storyboard layoutStoryboard,
|
|
List<OriginalLayoutValueRecord> originalValueRecords, List<FrameworkElement> movingElements)
|
|
{
|
|
var list = new List<FrameworkElement>();
|
|
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;
|
|
switch (element.HorizontalAlignment)
|
|
{
|
|
case HorizontalAlignment.Left:
|
|
x = layoutSlot.Left + margin.Left;
|
|
break;
|
|
|
|
case HorizontalAlignment.Center:
|
|
x = (layoutSlot.Left + margin.Left + layoutSlot.Right - margin.Right) / 2.0 - actualWidth / 2.0;
|
|
break;
|
|
|
|
case HorizontalAlignment.Right:
|
|
x = layoutSlot.Right - margin.Right - actualWidth;
|
|
break;
|
|
|
|
case HorizontalAlignment.Stretch:
|
|
x = Math.Max(layoutSlot.Left + margin.Left,
|
|
(layoutSlot.Left + margin.Left + layoutSlot.Right - margin.Right) / 2.0 - actualWidth / 2.0);
|
|
break;
|
|
}
|
|
switch (element.VerticalAlignment)
|
|
{
|
|
case VerticalAlignment.Top:
|
|
y = layoutSlot.Top + margin.Top;
|
|
break;
|
|
|
|
case VerticalAlignment.Center:
|
|
y = (layoutSlot.Top + margin.Top + layoutSlot.Bottom - margin.Bottom) / 2.0 - actualHeight / 2.0;
|
|
break;
|
|
|
|
case VerticalAlignment.Bottom:
|
|
y = layoutSlot.Bottom - margin.Bottom - actualHeight;
|
|
break;
|
|
|
|
case VerticalAlignment.Stretch:
|
|
y = Math.Max(layoutSlot.Top + margin.Top,
|
|
(layoutSlot.Top + margin.Top + layoutSlot.Bottom - margin.Bottom) / 2.0 - actualHeight / 2.0);
|
|
break;
|
|
}
|
|
return new Rect(x, y, actualWidth, actualHeight);
|
|
}
|
|
|
|
internal static Storyboard GetLayoutStoryboard(DependencyObject obj)
|
|
{
|
|
return (Storyboard) obj.GetValue(LayoutStoryboardProperty);
|
|
}
|
|
|
|
private static Dictionary<FrameworkElement, double> GetOldOpacities(FrameworkElement control,
|
|
FrameworkElement templateRoot, Storyboard layoutStoryboard,
|
|
List<OriginalLayoutValueRecord> originalValueRecords, List<FrameworkElement> movingElements)
|
|
{
|
|
var dictionary = new Dictionary<FrameworkElement, double>();
|
|
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<OriginalLayoutValueRecord> GetOriginalLayoutValues(DependencyObject obj)
|
|
{
|
|
return (List<OriginalLayoutValueRecord>) obj.GetValue(OriginalLayoutValuesProperty);
|
|
}
|
|
|
|
private static Dictionary<FrameworkElement, Rect> GetRectsOfTargets(IEnumerable<FrameworkElement> targets,
|
|
ICollection<FrameworkElement> movingElements)
|
|
{
|
|
var dictionary = new Dictionary<FrameworkElement, Rect>();
|
|
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<OriginalLayoutValueRecord>();
|
|
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<FrameworkElement>();
|
|
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, value);
|
|
}
|
|
|
|
internal static void SetLayoutStoryboard(DependencyObject obj, Storyboard value)
|
|
{
|
|
obj.SetValue(LayoutStoryboardProperty, value);
|
|
}
|
|
|
|
private static void SetLayoutStoryboardProperties(FrameworkElement control, FrameworkElement templateRoot,
|
|
Storyboard layoutStoryboard, List<OriginalLayoutValueRecord> 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<OriginalLayoutValueRecord> 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, 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<FrameworkElement> 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<FrameworkElement> movingElements,
|
|
Dictionary<FrameworkElement, Rect> oldRects, Dictionary<FrameworkElement, Rect> 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<DependencyProperty, object> 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));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |