我正在尝试为StackPanel设置动画,当它的可见性从0宽度增加到自动宽度时,这就是我现在所拥有的:
<Trigger Property="Visibility" Value="Visible">
<Setter Property="Width" Value="0"></Setter>
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Width" Duration="0:0:1">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<System:Double>NaN</System:Double>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
有人可以解释我如何实现这个动画吗?这可能不是我想要的方式吗?
谢谢, 亚历克斯。
答案 0 :(得分:9)
这是一个我扔在一起的快速模型项目。
在Window的Loaded事件中,我只是将stackpanel的可见性设置为Visible,它从左到右展开以适应其容器宽度......希望这符合您的需求。
有些注意事项:
以下是代码:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="600" Loaded="Window_Loaded">
<Border HorizontalAlignment="Center" Width="300" Background="Gainsboro">
<Border.Resources>
<Style TargetType="StackPanel" x:Key="expand">
<Setter Property="RenderTransform">
<Setter.Value>
<ScaleTransform ScaleX="1"/>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="RenderTransform.ScaleX"
From="0"
Duration="0:00:01"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
</Border.Resources>
<StackPanel x:Name="stackpanel" Background="Gray" Visibility="Collapsed" Style="{StaticResource expand}"/>
</Border>
</Window>
答案 1 :(得分:0)
所以,这是一个很老的问题,但是我认为这是一个足够普遍的情况,您需要将Width
或Height
从0
动画到Auto
(或类似内容)以证明其他答案的合理性。在这里,我将不着重于Alex的确切要求,以强调我提出的解决方案的一般性质。
这是什么:编写自己的Clipper
控件,将其孩子的可见Width
和Height
裁剪到其中的一部分。然后,我们可以对那些Fraction
属性(0
-> 1
)进行动画处理以获得所需的效果。以下是Clipper
的代码,其中包括所有帮助程序。
public sealed class Clipper : Decorator
{
public static readonly DependencyProperty WidthFractionProperty = DependencyProperty.RegisterAttached("WidthFraction", typeof(double), typeof(Clipper), new PropertyMetadata(1d, OnClippingInvalidated), IsFraction);
public static readonly DependencyProperty HeightFractionProperty = DependencyProperty.RegisterAttached("HeightFraction", typeof(double), typeof(Clipper), new PropertyMetadata(1d, OnClippingInvalidated), IsFraction);
public static readonly DependencyProperty BackgroundProperty = DependencyProperty.Register("Background", typeof(Brush), typeof(Clipper), new FrameworkPropertyMetadata(Brushes.Transparent, FrameworkPropertyMetadataOptions.AffectsRender));
public static readonly DependencyProperty ConstraintProperty = DependencyProperty.Register("Constraint", typeof(ConstraintSource), typeof(Clipper), new PropertyMetadata(ConstraintSource.WidthAndHeight, OnClippingInvalidated), IsValidConstraintSource);
private Size _childSize;
private DependencyPropertySubscriber _childVerticalAlignmentSubcriber;
private DependencyPropertySubscriber _childHorizontalAlignmentSubscriber;
public Clipper()
{
ClipToBounds = true;
}
public Brush Background
{
get { return (Brush)GetValue(BackgroundProperty); }
set { SetValue(BackgroundProperty, value); }
}
public ConstraintSource Constraint
{
get { return (ConstraintSource)GetValue(ConstraintProperty); }
set { SetValue(ConstraintProperty, value); }
}
[AttachedPropertyBrowsableForChildren]
public static double GetWidthFraction(DependencyObject obj)
{
return (double)obj.GetValue(WidthFractionProperty);
}
public static void SetWidthFraction(DependencyObject obj, double value)
{
obj.SetValue(WidthFractionProperty, value);
}
[AttachedPropertyBrowsableForChildren]
public static double GetHeightFraction(DependencyObject obj)
{
return (double)obj.GetValue(HeightFractionProperty);
}
public static void SetHeightFraction(DependencyObject obj, double value)
{
obj.SetValue(HeightFractionProperty, value);
}
protected override Size MeasureOverride(Size constraint)
{
if (Child is null)
{
return Size.Empty;
}
switch (Constraint)
{
case ConstraintSource.WidthAndHeight:
Child.Measure(constraint);
break;
case ConstraintSource.Width:
Child.Measure(new Size(constraint.Width, double.PositiveInfinity));
break;
case ConstraintSource.Height:
Child.Measure(new Size(double.PositiveInfinity, constraint.Height));
break;
case ConstraintSource.Nothing:
Child.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
break;
}
var finalSize = Child.DesiredSize;
if (Child is FrameworkElement childElement)
{
if (childElement.HorizontalAlignment == HorizontalAlignment.Stretch && constraint.Width > finalSize.Width && !double.IsInfinity(constraint.Width))
{
finalSize.Width = constraint.Width;
}
if (childElement.VerticalAlignment == VerticalAlignment.Stretch && constraint.Height > finalSize.Height && !double.IsInfinity(constraint.Height))
{
finalSize.Height = constraint.Height;
}
}
_childSize = finalSize;
finalSize.Width *= GetWidthFraction(Child);
finalSize.Height *= GetHeightFraction(Child);
return finalSize;
}
protected override Size ArrangeOverride(Size arrangeSize)
{
if (Child is null)
{
return Size.Empty;
}
var childSize = _childSize;
var clipperSize = new Size(Math.Min(arrangeSize.Width, childSize.Width * GetWidthFraction(Child)),
Math.Min(arrangeSize.Height, childSize.Height * GetHeightFraction(Child)));
var offsetX = 0d;
var offsetY = 0d;
if (Child is FrameworkElement childElement)
{
if (childSize.Width > clipperSize.Width)
{
switch (childElement.HorizontalAlignment)
{
case HorizontalAlignment.Right:
offsetX = -(childSize.Width - clipperSize.Width);
break;
case HorizontalAlignment.Center:
offsetX = -(childSize.Width - clipperSize.Width) / 2;
break;
}
}
if (childSize.Height > clipperSize.Height)
{
switch (childElement.VerticalAlignment)
{
case VerticalAlignment.Bottom:
offsetY = -(childSize.Height - clipperSize.Height);
break;
case VerticalAlignment.Center:
offsetY = -(childSize.Height - clipperSize.Height) / 2;
break;
}
}
}
Child.Arrange(new Rect(new Point(offsetX, offsetY), childSize));
return clipperSize;
}
protected override void OnVisualChildrenChanged(DependencyObject visualAdded, DependencyObject visualRemoved)
{
void UpdateLayout(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue.Equals(HorizontalAlignment.Stretch) || e.NewValue.Equals(VerticalAlignment.Stretch))
{
InvalidateMeasure();
}
else
{
InvalidateArrange();
}
}
_childHorizontalAlignmentSubscriber?.Unsubscribe();
_childVerticalAlignmentSubcriber?.Unsubscribe();
if (visualAdded is FrameworkElement childElement)
{
_childHorizontalAlignmentSubscriber = new DependencyPropertySubscriber(childElement, HorizontalAlignmentProperty, UpdateLayout);
_childVerticalAlignmentSubcriber = new DependencyPropertySubscriber(childElement, VerticalAlignmentProperty, UpdateLayout);
}
}
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
drawingContext.DrawRectangle(Background, null, new Rect(RenderSize));
}
private static bool IsFraction(object value)
{
var numericValue = (double)value;
return numericValue >= 0d && numericValue <= 1d;
}
private static void OnClippingInvalidated(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is UIElement element && VisualTreeHelper.GetParent(element) is Clipper translator)
{
translator.InvalidateMeasure();
}
}
private static bool IsValidConstraintSource(object value)
{
return Enum.IsDefined(typeof(ConstraintSource), value);
}
}
public enum ConstraintSource
{
WidthAndHeight,
Width,
Height,
Nothing
}
public class DependencyPropertySubscriber : DependencyObject
{
private static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(object), typeof(DependencyPropertySubscriber), new PropertyMetadata(null, ValueChanged));
private readonly PropertyChangedCallback _handler;
public DependencyPropertySubscriber(DependencyObject dependencyObject, DependencyProperty dependencyProperty, PropertyChangedCallback handler)
{
if (dependencyObject is null)
{
throw new ArgumentNullException(nameof(dependencyObject));
}
if (dependencyProperty is null)
{
throw new ArgumentNullException(nameof(dependencyProperty));
}
_handler = handler ?? throw new ArgumentNullException(nameof(handler));
var binding = new Binding() { Path = new PropertyPath(dependencyProperty), Source = dependencyObject, Mode = BindingMode.OneWay };
BindingOperations.SetBinding(this, ValueProperty, binding);
}
public void Unsubscribe()
{
BindingOperations.ClearBinding(this, ValueProperty);
}
private static void ValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((DependencyPropertySubscriber)d)._handler(d, e);
}
}
用法如下:
<Clipper Constraint="WidthAndHeight">
<Control Clipper.HeightFraction="0.5"
Clipper.WidthFraction="0.5" />
</Clipper>
请注意Constraint
属性:它确定子控件认为“自动”尺寸的内容。例如,如果您的控件是静态的(已明确设置Height
和Width
),则应将Constraint
设置为Nothing
来裁剪整个元素的一部分。如果您的控件是WrapPanel
,并且Orientation
设置为Horizontal
,则Constraint
应该设置为Width
,依此类推。如果截取错误,请尝试一下不同的约束。还请注意,Clipper
尊重您控件的对齐方式,该对齐方式可以在动画中使用(例如,将HeightFraction
从0
动画到1
,VerticalAlignment.Bottom
表示控件“向下滑动”,VerticalAlignment.Center
-“打开”)。