UWP 网格行高在内容缩放变换后不更新
UWP grid rowheight not updating after scale transform of content
我有一个包含 2 行(header 和内容)的控件。
当指针进入或存在时,内容上有一个 ScaleY 变换,但是当内容缩放到 0 时,高度为 Auto 的行似乎不会折叠高度。
<Page
x:Class="ScaleTest.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ScaleTest"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<local:TestControl VerticalAlignment="Bottom"/>
</Grid>
<Style TargetType="local:TestControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:TestControl">
<Grid VerticalAlignment="{TemplateBinding VerticalAlignment}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="DisplayStates">
<VisualState x:Name="Minimized">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="contentTransForm"
Storyboard.TargetProperty="ScaleY"
Duration="0:0:0.2"
To="0"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Maximized">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="contentTransForm"
Storyboard.TargetProperty="ScaleY"
Duration="0:0:0.2"
To="1"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid Background="Red">
<TextBlock Text="Header"/>
</Grid>
<Grid Grid.Row="1" Background="Orange">
<Grid.RenderTransform>
<CompositeTransform x:Name="contentTransForm" ScaleY="0" ScaleX="1"/>
</Grid.RenderTransform>
<TextBlock Text="Content"/>
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
我制作了一个小示例应用程序来说明问题。
sample
为什么网格没有更新。
确实如此。 RenderTransform
其实只是为了渲染,不影响页面的布局。要使动画以您想要的方式显示,您需要为橙色的高度设置动画 Grid
而不是 ScaleY
。问题在于这样的动画 属性 会导致布局更改,因此您必须将 EnableDependentAnimation="True"
添加到 DoubleAnimations
。但是请注意,依赖动画的性能要差得多,因为它们需要对每一帧进行布局循环。如果您找到更好的解决方案,最好避免使用它们。
一种解决方案是在缩小内容时为 header 位置 (TranslateTransform
) 设置动画,然后设置可见性以实际更新布局并确保一切就位。这将再次没有布局更新并且可以正常工作。
您需要在控件模板中为视觉状态添加设置器。
<ControlTemplate TargetType="local:TestControl">
<Grid VerticalAlignment="{TemplateBinding VerticalAlignment}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid Background="Red">
<TextBlock Text="Header"/>
</Grid>
<Grid x:Name="ContentGrid" Grid.Row="1" Background="Orange" MinHeight="0" Height="auto">
<Grid.RenderTransform>
<CompositeTransform x:Name="contentTransForm" ScaleY="0" ScaleX="1"/>
</Grid.RenderTransform>
<TextBlock Text="Content">
<!--<TextBlock.RenderTransform>
<CompositeTransform x:Name="contentTransForm" ScaleY="0" ScaleX="1"/>
</TextBlock.RenderTransform>-->
</TextBlock>
</Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="DisplayStates">
<VisualState x:Name="Minimized">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="contentTransForm"
Storyboard.TargetProperty="ScaleY"
Duration="0:0:0.2"
To="0"/>
</Storyboard>
<VisualState.Setters>
<Setter Target="ContentGrid.Visibility" Value="Collapsed" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Maximized">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="contentTransForm"
Storyboard.TargetProperty="ScaleY"
Duration="0:0:0.2"
To="1"/>
</Storyboard>
<VisualState.Setters>
<Setter Target="ContentGrid.Visibility" Value="Visible" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
Here 是来自您的应用程序的修改源
谢谢大家,
我实际上是通过从 wpf 工具包中实现 LayoutTansformer 并使用调解器和转换器来解决它的。
它提供了一个很好的流畅动画,没有熬夜然后突然崩溃。
LayoutTransformer:
[TemplatePart(Name = "Presenter", Type = typeof(ContentPresenter))]
[TemplatePart(Name = "TransformRoot", Type = typeof(Grid))]
public sealed class LayoutTransformer : ContentControl
{
public static readonly DependencyProperty LayoutTransformProperty = DependencyProperty.Register("LayoutTransform", typeof(Transform), typeof(LayoutTransformer), new PropertyMetadata(new PropertyChangedCallback(LayoutTransformer.LayoutTransformChanged)));
private Size _childActualSize = Size.Empty;
private const string TransformRootName = "TransformRoot";
private const string PresenterName = "Presenter";
private const double AcceptableDelta = 0.0001;
private const int DecimalsAfterRound = 4;
private Panel _transformRoot;
private ContentPresenter _contentPresenter;
private MatrixTransform _matrixTransform;
private Matrix _transformation;
public Transform LayoutTransform
{
get => (Transform)GetValue(LayoutTransformer.LayoutTransformProperty);
set => SetValue(LayoutTransformer.LayoutTransformProperty, (object)value);
}
private FrameworkElement Child => _contentPresenter?.Content as FrameworkElement ?? _contentPresenter;
public LayoutTransformer()
{
DefaultStyleKey = (object)typeof(LayoutTransformer);
IsTabStop = false;
UseLayoutRounding = false;
}
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
_transformRoot = (Panel)(GetTemplateChild(TransformRootName) as Grid);
_contentPresenter = GetTemplateChild(PresenterName) as ContentPresenter;
_matrixTransform = new MatrixTransform();
if (null != _transformRoot)
_transformRoot.RenderTransform = (Transform)_matrixTransform;
ApplyLayoutTransform();
}
private static void LayoutTransformChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
((LayoutTransformer)o).ProcessTransform((Transform)e.NewValue);
}
public void ApplyLayoutTransform()
{
ProcessTransform(LayoutTransform);
}
private void ProcessTransform(Transform transform)
{
_transformation = LayoutTransformer.RoundMatrix(GetTransformMatrix(transform), DecimalsAfterRound);
if (null != _matrixTransform)
_matrixTransform.Matrix = _transformation;
InvalidateMeasure();
}
private Matrix GetTransformMatrix(Transform transform)
{
if (null == transform) return Matrix.Identity;
if (transform is TransformGroup transformGroup)
{
var matrix1 = Matrix.Identity;
foreach (var transform1 in (TransformCollection)transformGroup.Children)
matrix1 = LayoutTransformer.MatrixMultiply(matrix1, GetTransformMatrix(transform1));
return matrix1;
}
if (transform is RotateTransform rotateTransform)
{
var num1 = 2.0 * Math.PI * rotateTransform.Angle / 360.0;
var m12 = Math.Sin(num1);
var num2 = Math.Cos(num1);
return new Matrix(num2, m12, -m12, num2, 0.0, 0.0);
}
if (transform is ScaleTransform scaleTransform)
return new Matrix(scaleTransform.ScaleX, 0.0, 0.0, scaleTransform.ScaleY, 0.0, 0.0);
if (transform is SkewTransform skewTransform)
{
var angleX = skewTransform.AngleX;
return new Matrix(1.0, 2.0 * Math.PI * skewTransform.AngleY / 360.0, 2.0 * Math.PI * angleX / 360.0, 1.0, 0.0, 0.0);
}
if (transform is MatrixTransform matrixTransform)
return matrixTransform.Matrix;
return Matrix.Identity;
}
protected override Size MeasureOverride(Size availableSize)
{
if (_transformRoot == null || null == Child)
return Size.Empty;
_transformRoot.Measure(!(_childActualSize == Size.Empty) ? _childActualSize : ComputeLargestTransformedSize(availableSize));
var x = 0.0;
var y = 0.0;
var desiredSize = _transformRoot.DesiredSize;
var width = desiredSize.Width;
desiredSize = _transformRoot.DesiredSize;
var height = desiredSize.Height;
var rect = LayoutTransformer.RectTransform(new Rect(x, y, width, height), _transformation);
return new Size(rect.Width, rect.Height);
}
protected override Size ArrangeOverride(Size finalSize)
{
var child = Child;
if (_transformRoot == null || null == child)
return finalSize;
var a = ComputeLargestTransformedSize(finalSize);
if (LayoutTransformer.IsSizeSmaller(a, _transformRoot.DesiredSize))
a = _transformRoot.DesiredSize;
var rect = LayoutTransformer.RectTransform(new Rect(0.0, 0.0, a.Width, a.Height), _transformation);
_transformRoot.Arrange(new Rect(-rect.Left + (finalSize.Width - rect.Width) / 2.0, -rect.Top + (finalSize.Height - rect.Height) / 2.0, a.Width, a.Height));
if (LayoutTransformer.IsSizeSmaller(a, child.RenderSize) && Size.Empty == _childActualSize)
{
_childActualSize = new Size(child.ActualWidth, child.ActualHeight);
InvalidateMeasure();
}
else
_childActualSize = Size.Empty;
return finalSize;
}
private Size ComputeLargestTransformedSize(Size arrangeBounds)
{
var size = Size.Empty;
var flag1 = double.IsInfinity(arrangeBounds.Width);
if (flag1)
arrangeBounds.Width = arrangeBounds.Height;
var flag2 = double.IsInfinity(arrangeBounds.Height);
if (flag2)
arrangeBounds.Height = arrangeBounds.Width;
var m11 = _transformation.M11;
var m12 = _transformation.M12;
var m21 = _transformation.M21;
var m22 = _transformation.M22;
var num1 = Math.Abs(arrangeBounds.Width / m11);
var num2 = Math.Abs(arrangeBounds.Width / m21);
var num3 = Math.Abs(arrangeBounds.Height / m12);
var num4 = Math.Abs(arrangeBounds.Height / m22);
var num5 = num1 / 2.0;
var num6 = num2 / 2.0;
var num7 = num3 / 2.0;
var num8 = num4 / 2.0;
var num9 = -(num2 / num1);
var num10 = -(num4 / num3);
if (0.0 == arrangeBounds.Width || 0.0 == arrangeBounds.Height)
size = new Size(arrangeBounds.Width, arrangeBounds.Height);
else if (flag1 && flag2)
size = new Size(double.PositiveInfinity, double.PositiveInfinity);
else if (!LayoutTransformer.MatrixHasInverse(_transformation))
size = new Size(0.0, 0.0);
else if (0.0 == m12 || 0.0 == m21)
{
var num11 = flag2 ? double.PositiveInfinity : num4;
var num12 = flag1 ? double.PositiveInfinity : num1;
if (0.0 == m12 && 0.0 == m21)
size = new Size(num12, num11);
else if (0.0 == m12)
{
var height = Math.Min(num6, num11);
size = new Size(num12 - Math.Abs(m21 * height / m11), height);
}
else if (0.0 == m21)
{
var width = Math.Min(num7, num12);
size = new Size(width, num11 - Math.Abs(m12 * width / m22));
}
}
else if (0.0 == m11 || 0.0 == m22)
{
var num11 = flag2 ? double.PositiveInfinity : num3;
var num12 = flag1 ? double.PositiveInfinity : num2;
if (0.0 == m11 && 0.0 == m22)
size = new Size(num11, num12);
else if (0.0 == m11)
{
var height = Math.Min(num8, num12);
size = new Size(num11 - Math.Abs(m22 * height / m12), height);
}
else if (0.0 == m22)
{
var width = Math.Min(num5, num11);
size = new Size(width, num12 - Math.Abs(m11 * width / m21));
}
}
else if (num6 <= num10 * num5 + num4)
size = new Size(num5, num6);
else if (num8 <= num9 * num7 + num2)
{
size = new Size(num7, num8);
}
else
{
var width = (num4 - num2) / (num9 - num10);
size = new Size(width, num9 * width + num2);
}
return size;
}
private static bool IsSizeSmaller(Size a, Size b)
{
return a.Width + AcceptableDelta < b.Width || a.Height + AcceptableDelta < b.Height;
}
private static Matrix RoundMatrix(Matrix matrix, int decimals)
{
return new Matrix(Math.Round(matrix.M11, decimals), Math.Round(matrix.M12, decimals), Math.Round(matrix.M21, decimals), Math.Round(matrix.M22, decimals), matrix.OffsetX, matrix.OffsetY);
}
private static Rect RectTransform(Rect rect, Matrix matrix)
{
var point1 = matrix.Transform(new Point(rect.Left, rect.Top));
var point2 = matrix.Transform(new Point(rect.Right, rect.Top));
var point3 = matrix.Transform(new Point(rect.Left, rect.Bottom));
var point4 = matrix.Transform(new Point(rect.Right, rect.Bottom));
var x = Math.Min(Math.Min(point1.X, point2.X), Math.Min(point3.X, point4.X));
var y = Math.Min(Math.Min(point1.Y, point2.Y), Math.Min(point3.Y, point4.Y));
var num1 = Math.Max(Math.Max(point1.X, point2.X), Math.Max(point3.X, point4.X));
var num2 = Math.Max(Math.Max(point1.Y, point2.Y), Math.Max(point3.Y, point4.Y));
return new Rect(x, y, num1 - x, num2 - y);
}
private static Matrix MatrixMultiply(Matrix matrix1, Matrix matrix2)
{
return new Matrix(matrix1.M11 * matrix2.M11 + matrix1.M12 * matrix2.M21, matrix1.M11 * matrix2.M12 + matrix1.M12 * matrix2.M22, matrix1.M21 * matrix2.M11 + matrix1.M22 * matrix2.M21, matrix1.M21 * matrix2.M12 + matrix1.M22 * matrix2.M22, matrix1.OffsetX * matrix2.M11 + matrix1.OffsetY * matrix2.M21 + matrix2.OffsetX, matrix1.OffsetX * matrix2.M12 + matrix1.OffsetY * matrix2.M22 + matrix2.OffsetY);
}
private static bool MatrixHasInverse(Matrix matrix)
{
return 0.0 != matrix.M11 * matrix.M22 - matrix.M12 * matrix.M21;
}
}
<Style TargetType="local:LayoutTransformer">
<Setter Property="Foreground" Value="#FF000000" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:LayoutTransformer">
<Grid x:Name="TransformRoot" Background="{TemplateBinding Background}">
<ContentPresenter x:Name="Presenter" Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
调解员:
public class DoubleAnimationMediator : FrameworkElement
{
private string _layoutTransformerName;
public LayoutTransformer LayoutTransformer { get; set; }
public string LayoutTransformerName
{
get => _layoutTransformerName;
set
{
_layoutTransformerName = value;
LayoutTransformer = null;
}
}
public static readonly DependencyProperty AnimationValueProperty =
DependencyProperty.Register(
"AnimationValue",
typeof(double),
typeof(DoubleAnimationMediator),
new PropertyMetadata(0, AnimationValuePropertyChanged));
private static void AnimationValuePropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
((DoubleAnimationMediator)o).AnimationValuePropertyChanged();
}
public double AnimationValue
{
get => (double)GetValue(AnimationValueProperty);
set => SetValue(AnimationValueProperty, value);
}
private void AnimationValuePropertyChanged()
{
if (null == LayoutTransformer)
{
LayoutTransformer = FindName(LayoutTransformerName) as LayoutTransformer;
if (null == LayoutTransformer)
{
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
"AnimationMediator was unable to find a LayoutTransformer named \"{0}\".",
LayoutTransformerName));
}
}
CoreApplication.MainView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => LayoutTransformer.ApplyLayoutTransform()).AsTask().ConfigureAwait(true);
}
}
测试控件:
public class TestControl : Control
{
public TestControl()
{
DefaultStyleKey = typeof(TestControl);
PointerEntered += OnPointerEntered;
PointerExited += OnPointerExited;
}
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
VisualStateManager.GoToState(this, "Minimized", false);
}
private void OnPointerExited(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e)
{
VisualStateManager.GoToState(this, "Minimized", false);
}
private void OnPointerEntered(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e)
{
VisualStateManager.GoToState(this, "Maximized", false);
}
}
<Style TargetType="local:TestControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:TestControl">
<Grid VerticalAlignment="{TemplateBinding VerticalAlignment}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="DisplayStates">
<VisualState x:Name="Minimized">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="scaleYMediator"
Storyboard.TargetProperty="AnimationValue"
EnableDependentAnimation="True"
Duration="0:0:0.2"
To="0"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Maximized">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="scaleYMediator"
Storyboard.TargetProperty="AnimationValue"
EnableDependentAnimation="True"
Duration="0:0:0.2"
To="1"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<local:DoubleAnimationMediator x:Name="scaleYMediator"
LayoutTransformerName="layoutTransform"
AnimationValue="{Binding ScaleY, ElementName=scaleTransform, Mode=TwoWay}"/>
<Grid Background="Red"
Grid.Row="0">
<TextBlock Text="Header"/>
</Grid>
<local:LayoutTransformer x:Name="layoutTransform" Grid.Row="1"
Background="Orange">
<local:LayoutTransformer.LayoutTransform>
<TransformGroup>
<ScaleTransform x:Name="scaleTransform" ScaleY="0"/>
</TransformGroup>
</local:LayoutTransformer.LayoutTransform>
<local:LayoutTransformer.Content>
<Grid>
<TextBlock Text="Content"/>
</Grid>
</local:LayoutTransformer.Content>
</local:LayoutTransformer>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
我有一个包含 2 行(header 和内容)的控件。 当指针进入或存在时,内容上有一个 ScaleY 变换,但是当内容缩放到 0 时,高度为 Auto 的行似乎不会折叠高度。
<Page
x:Class="ScaleTest.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ScaleTest"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<local:TestControl VerticalAlignment="Bottom"/>
</Grid>
<Style TargetType="local:TestControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:TestControl">
<Grid VerticalAlignment="{TemplateBinding VerticalAlignment}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="DisplayStates">
<VisualState x:Name="Minimized">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="contentTransForm"
Storyboard.TargetProperty="ScaleY"
Duration="0:0:0.2"
To="0"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Maximized">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="contentTransForm"
Storyboard.TargetProperty="ScaleY"
Duration="0:0:0.2"
To="1"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid Background="Red">
<TextBlock Text="Header"/>
</Grid>
<Grid Grid.Row="1" Background="Orange">
<Grid.RenderTransform>
<CompositeTransform x:Name="contentTransForm" ScaleY="0" ScaleX="1"/>
</Grid.RenderTransform>
<TextBlock Text="Content"/>
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
我制作了一个小示例应用程序来说明问题。 sample
为什么网格没有更新。
确实如此。 RenderTransform
其实只是为了渲染,不影响页面的布局。要使动画以您想要的方式显示,您需要为橙色的高度设置动画 Grid
而不是 ScaleY
。问题在于这样的动画 属性 会导致布局更改,因此您必须将 EnableDependentAnimation="True"
添加到 DoubleAnimations
。但是请注意,依赖动画的性能要差得多,因为它们需要对每一帧进行布局循环。如果您找到更好的解决方案,最好避免使用它们。
一种解决方案是在缩小内容时为 header 位置 (TranslateTransform
) 设置动画,然后设置可见性以实际更新布局并确保一切就位。这将再次没有布局更新并且可以正常工作。
您需要在控件模板中为视觉状态添加设置器。
<ControlTemplate TargetType="local:TestControl">
<Grid VerticalAlignment="{TemplateBinding VerticalAlignment}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid Background="Red">
<TextBlock Text="Header"/>
</Grid>
<Grid x:Name="ContentGrid" Grid.Row="1" Background="Orange" MinHeight="0" Height="auto">
<Grid.RenderTransform>
<CompositeTransform x:Name="contentTransForm" ScaleY="0" ScaleX="1"/>
</Grid.RenderTransform>
<TextBlock Text="Content">
<!--<TextBlock.RenderTransform>
<CompositeTransform x:Name="contentTransForm" ScaleY="0" ScaleX="1"/>
</TextBlock.RenderTransform>-->
</TextBlock>
</Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="DisplayStates">
<VisualState x:Name="Minimized">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="contentTransForm"
Storyboard.TargetProperty="ScaleY"
Duration="0:0:0.2"
To="0"/>
</Storyboard>
<VisualState.Setters>
<Setter Target="ContentGrid.Visibility" Value="Collapsed" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Maximized">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="contentTransForm"
Storyboard.TargetProperty="ScaleY"
Duration="0:0:0.2"
To="1"/>
</Storyboard>
<VisualState.Setters>
<Setter Target="ContentGrid.Visibility" Value="Visible" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
Here 是来自您的应用程序的修改源
谢谢大家,
我实际上是通过从 wpf 工具包中实现 LayoutTansformer 并使用调解器和转换器来解决它的。 它提供了一个很好的流畅动画,没有熬夜然后突然崩溃。
LayoutTransformer:
[TemplatePart(Name = "Presenter", Type = typeof(ContentPresenter))]
[TemplatePart(Name = "TransformRoot", Type = typeof(Grid))]
public sealed class LayoutTransformer : ContentControl
{
public static readonly DependencyProperty LayoutTransformProperty = DependencyProperty.Register("LayoutTransform", typeof(Transform), typeof(LayoutTransformer), new PropertyMetadata(new PropertyChangedCallback(LayoutTransformer.LayoutTransformChanged)));
private Size _childActualSize = Size.Empty;
private const string TransformRootName = "TransformRoot";
private const string PresenterName = "Presenter";
private const double AcceptableDelta = 0.0001;
private const int DecimalsAfterRound = 4;
private Panel _transformRoot;
private ContentPresenter _contentPresenter;
private MatrixTransform _matrixTransform;
private Matrix _transformation;
public Transform LayoutTransform
{
get => (Transform)GetValue(LayoutTransformer.LayoutTransformProperty);
set => SetValue(LayoutTransformer.LayoutTransformProperty, (object)value);
}
private FrameworkElement Child => _contentPresenter?.Content as FrameworkElement ?? _contentPresenter;
public LayoutTransformer()
{
DefaultStyleKey = (object)typeof(LayoutTransformer);
IsTabStop = false;
UseLayoutRounding = false;
}
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
_transformRoot = (Panel)(GetTemplateChild(TransformRootName) as Grid);
_contentPresenter = GetTemplateChild(PresenterName) as ContentPresenter;
_matrixTransform = new MatrixTransform();
if (null != _transformRoot)
_transformRoot.RenderTransform = (Transform)_matrixTransform;
ApplyLayoutTransform();
}
private static void LayoutTransformChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
((LayoutTransformer)o).ProcessTransform((Transform)e.NewValue);
}
public void ApplyLayoutTransform()
{
ProcessTransform(LayoutTransform);
}
private void ProcessTransform(Transform transform)
{
_transformation = LayoutTransformer.RoundMatrix(GetTransformMatrix(transform), DecimalsAfterRound);
if (null != _matrixTransform)
_matrixTransform.Matrix = _transformation;
InvalidateMeasure();
}
private Matrix GetTransformMatrix(Transform transform)
{
if (null == transform) return Matrix.Identity;
if (transform is TransformGroup transformGroup)
{
var matrix1 = Matrix.Identity;
foreach (var transform1 in (TransformCollection)transformGroup.Children)
matrix1 = LayoutTransformer.MatrixMultiply(matrix1, GetTransformMatrix(transform1));
return matrix1;
}
if (transform is RotateTransform rotateTransform)
{
var num1 = 2.0 * Math.PI * rotateTransform.Angle / 360.0;
var m12 = Math.Sin(num1);
var num2 = Math.Cos(num1);
return new Matrix(num2, m12, -m12, num2, 0.0, 0.0);
}
if (transform is ScaleTransform scaleTransform)
return new Matrix(scaleTransform.ScaleX, 0.0, 0.0, scaleTransform.ScaleY, 0.0, 0.0);
if (transform is SkewTransform skewTransform)
{
var angleX = skewTransform.AngleX;
return new Matrix(1.0, 2.0 * Math.PI * skewTransform.AngleY / 360.0, 2.0 * Math.PI * angleX / 360.0, 1.0, 0.0, 0.0);
}
if (transform is MatrixTransform matrixTransform)
return matrixTransform.Matrix;
return Matrix.Identity;
}
protected override Size MeasureOverride(Size availableSize)
{
if (_transformRoot == null || null == Child)
return Size.Empty;
_transformRoot.Measure(!(_childActualSize == Size.Empty) ? _childActualSize : ComputeLargestTransformedSize(availableSize));
var x = 0.0;
var y = 0.0;
var desiredSize = _transformRoot.DesiredSize;
var width = desiredSize.Width;
desiredSize = _transformRoot.DesiredSize;
var height = desiredSize.Height;
var rect = LayoutTransformer.RectTransform(new Rect(x, y, width, height), _transformation);
return new Size(rect.Width, rect.Height);
}
protected override Size ArrangeOverride(Size finalSize)
{
var child = Child;
if (_transformRoot == null || null == child)
return finalSize;
var a = ComputeLargestTransformedSize(finalSize);
if (LayoutTransformer.IsSizeSmaller(a, _transformRoot.DesiredSize))
a = _transformRoot.DesiredSize;
var rect = LayoutTransformer.RectTransform(new Rect(0.0, 0.0, a.Width, a.Height), _transformation);
_transformRoot.Arrange(new Rect(-rect.Left + (finalSize.Width - rect.Width) / 2.0, -rect.Top + (finalSize.Height - rect.Height) / 2.0, a.Width, a.Height));
if (LayoutTransformer.IsSizeSmaller(a, child.RenderSize) && Size.Empty == _childActualSize)
{
_childActualSize = new Size(child.ActualWidth, child.ActualHeight);
InvalidateMeasure();
}
else
_childActualSize = Size.Empty;
return finalSize;
}
private Size ComputeLargestTransformedSize(Size arrangeBounds)
{
var size = Size.Empty;
var flag1 = double.IsInfinity(arrangeBounds.Width);
if (flag1)
arrangeBounds.Width = arrangeBounds.Height;
var flag2 = double.IsInfinity(arrangeBounds.Height);
if (flag2)
arrangeBounds.Height = arrangeBounds.Width;
var m11 = _transformation.M11;
var m12 = _transformation.M12;
var m21 = _transformation.M21;
var m22 = _transformation.M22;
var num1 = Math.Abs(arrangeBounds.Width / m11);
var num2 = Math.Abs(arrangeBounds.Width / m21);
var num3 = Math.Abs(arrangeBounds.Height / m12);
var num4 = Math.Abs(arrangeBounds.Height / m22);
var num5 = num1 / 2.0;
var num6 = num2 / 2.0;
var num7 = num3 / 2.0;
var num8 = num4 / 2.0;
var num9 = -(num2 / num1);
var num10 = -(num4 / num3);
if (0.0 == arrangeBounds.Width || 0.0 == arrangeBounds.Height)
size = new Size(arrangeBounds.Width, arrangeBounds.Height);
else if (flag1 && flag2)
size = new Size(double.PositiveInfinity, double.PositiveInfinity);
else if (!LayoutTransformer.MatrixHasInverse(_transformation))
size = new Size(0.0, 0.0);
else if (0.0 == m12 || 0.0 == m21)
{
var num11 = flag2 ? double.PositiveInfinity : num4;
var num12 = flag1 ? double.PositiveInfinity : num1;
if (0.0 == m12 && 0.0 == m21)
size = new Size(num12, num11);
else if (0.0 == m12)
{
var height = Math.Min(num6, num11);
size = new Size(num12 - Math.Abs(m21 * height / m11), height);
}
else if (0.0 == m21)
{
var width = Math.Min(num7, num12);
size = new Size(width, num11 - Math.Abs(m12 * width / m22));
}
}
else if (0.0 == m11 || 0.0 == m22)
{
var num11 = flag2 ? double.PositiveInfinity : num3;
var num12 = flag1 ? double.PositiveInfinity : num2;
if (0.0 == m11 && 0.0 == m22)
size = new Size(num11, num12);
else if (0.0 == m11)
{
var height = Math.Min(num8, num12);
size = new Size(num11 - Math.Abs(m22 * height / m12), height);
}
else if (0.0 == m22)
{
var width = Math.Min(num5, num11);
size = new Size(width, num12 - Math.Abs(m11 * width / m21));
}
}
else if (num6 <= num10 * num5 + num4)
size = new Size(num5, num6);
else if (num8 <= num9 * num7 + num2)
{
size = new Size(num7, num8);
}
else
{
var width = (num4 - num2) / (num9 - num10);
size = new Size(width, num9 * width + num2);
}
return size;
}
private static bool IsSizeSmaller(Size a, Size b)
{
return a.Width + AcceptableDelta < b.Width || a.Height + AcceptableDelta < b.Height;
}
private static Matrix RoundMatrix(Matrix matrix, int decimals)
{
return new Matrix(Math.Round(matrix.M11, decimals), Math.Round(matrix.M12, decimals), Math.Round(matrix.M21, decimals), Math.Round(matrix.M22, decimals), matrix.OffsetX, matrix.OffsetY);
}
private static Rect RectTransform(Rect rect, Matrix matrix)
{
var point1 = matrix.Transform(new Point(rect.Left, rect.Top));
var point2 = matrix.Transform(new Point(rect.Right, rect.Top));
var point3 = matrix.Transform(new Point(rect.Left, rect.Bottom));
var point4 = matrix.Transform(new Point(rect.Right, rect.Bottom));
var x = Math.Min(Math.Min(point1.X, point2.X), Math.Min(point3.X, point4.X));
var y = Math.Min(Math.Min(point1.Y, point2.Y), Math.Min(point3.Y, point4.Y));
var num1 = Math.Max(Math.Max(point1.X, point2.X), Math.Max(point3.X, point4.X));
var num2 = Math.Max(Math.Max(point1.Y, point2.Y), Math.Max(point3.Y, point4.Y));
return new Rect(x, y, num1 - x, num2 - y);
}
private static Matrix MatrixMultiply(Matrix matrix1, Matrix matrix2)
{
return new Matrix(matrix1.M11 * matrix2.M11 + matrix1.M12 * matrix2.M21, matrix1.M11 * matrix2.M12 + matrix1.M12 * matrix2.M22, matrix1.M21 * matrix2.M11 + matrix1.M22 * matrix2.M21, matrix1.M21 * matrix2.M12 + matrix1.M22 * matrix2.M22, matrix1.OffsetX * matrix2.M11 + matrix1.OffsetY * matrix2.M21 + matrix2.OffsetX, matrix1.OffsetX * matrix2.M12 + matrix1.OffsetY * matrix2.M22 + matrix2.OffsetY);
}
private static bool MatrixHasInverse(Matrix matrix)
{
return 0.0 != matrix.M11 * matrix.M22 - matrix.M12 * matrix.M21;
}
}
<Style TargetType="local:LayoutTransformer">
<Setter Property="Foreground" Value="#FF000000" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:LayoutTransformer">
<Grid x:Name="TransformRoot" Background="{TemplateBinding Background}">
<ContentPresenter x:Name="Presenter" Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
调解员:
public class DoubleAnimationMediator : FrameworkElement
{
private string _layoutTransformerName;
public LayoutTransformer LayoutTransformer { get; set; }
public string LayoutTransformerName
{
get => _layoutTransformerName;
set
{
_layoutTransformerName = value;
LayoutTransformer = null;
}
}
public static readonly DependencyProperty AnimationValueProperty =
DependencyProperty.Register(
"AnimationValue",
typeof(double),
typeof(DoubleAnimationMediator),
new PropertyMetadata(0, AnimationValuePropertyChanged));
private static void AnimationValuePropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
((DoubleAnimationMediator)o).AnimationValuePropertyChanged();
}
public double AnimationValue
{
get => (double)GetValue(AnimationValueProperty);
set => SetValue(AnimationValueProperty, value);
}
private void AnimationValuePropertyChanged()
{
if (null == LayoutTransformer)
{
LayoutTransformer = FindName(LayoutTransformerName) as LayoutTransformer;
if (null == LayoutTransformer)
{
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
"AnimationMediator was unable to find a LayoutTransformer named \"{0}\".",
LayoutTransformerName));
}
}
CoreApplication.MainView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => LayoutTransformer.ApplyLayoutTransform()).AsTask().ConfigureAwait(true);
}
}
测试控件:
public class TestControl : Control
{
public TestControl()
{
DefaultStyleKey = typeof(TestControl);
PointerEntered += OnPointerEntered;
PointerExited += OnPointerExited;
}
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
VisualStateManager.GoToState(this, "Minimized", false);
}
private void OnPointerExited(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e)
{
VisualStateManager.GoToState(this, "Minimized", false);
}
private void OnPointerEntered(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e)
{
VisualStateManager.GoToState(this, "Maximized", false);
}
}
<Style TargetType="local:TestControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:TestControl">
<Grid VerticalAlignment="{TemplateBinding VerticalAlignment}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="DisplayStates">
<VisualState x:Name="Minimized">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="scaleYMediator"
Storyboard.TargetProperty="AnimationValue"
EnableDependentAnimation="True"
Duration="0:0:0.2"
To="0"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Maximized">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="scaleYMediator"
Storyboard.TargetProperty="AnimationValue"
EnableDependentAnimation="True"
Duration="0:0:0.2"
To="1"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<local:DoubleAnimationMediator x:Name="scaleYMediator"
LayoutTransformerName="layoutTransform"
AnimationValue="{Binding ScaleY, ElementName=scaleTransform, Mode=TwoWay}"/>
<Grid Background="Red"
Grid.Row="0">
<TextBlock Text="Header"/>
</Grid>
<local:LayoutTransformer x:Name="layoutTransform" Grid.Row="1"
Background="Orange">
<local:LayoutTransformer.LayoutTransform>
<TransformGroup>
<ScaleTransform x:Name="scaleTransform" ScaleY="0"/>
</TransformGroup>
</local:LayoutTransformer.LayoutTransform>
<local:LayoutTransformer.Content>
<Grid>
<TextBlock Text="Content"/>
</Grid>
</local:LayoutTransformer.Content>
</local:LayoutTransformer>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>