WPF中动画宽度如何转换为实际宽度?

7

如何在WPF中将元素的宽度从0动画到其实际宽度?

我尝试了以下方法:

<ControlTemplate.Triggers>
    <EventTrigger RoutedEvent="Loaded">
        <BeginStoryboard>
            <Storyboard>
                <DoubleAnimation Duration="0:0:0.3" To="{Binding ElementName=MyElement, Path=ActualWidth}" From="0" Storyboard.TargetProperty="Width" Storyboard.TargetName="MyElement" />
            </Storyboard>
        </BeginStoryboard>
    </EventTrigger>
</ControlTemplate.Triggers>

如果我将绑定更改为硬编码值,例如 100,那么宽度就会正确地进行动画处理,但我想绑定到元素的实际宽度。

如果有关系,MyElement 是一个边框,我正在对选项卡项进行动画处理。

记录一下,这也行不通:

To="{Binding RelativeSource={RelativeSource AncestorType={x:Type Border}}, Path=ActualWidth}"
2个回答

10

我敢肯定这种方式错了很多方面,欢迎指出我违反了多少WPF法则,但是...我通过创建自己的BindableDoubleAnimation解决了同样的问题。

public class BindableDoubleAnimation : DoubleAnimationBase
{
    DoubleAnimation internalAnimation;

    public DoubleAnimation InternalAnimation { get { return internalAnimation; } }

    public double To
    {
        get { return (double)GetValue(ToProperty); }
        set { SetValue(ToProperty, value); }
    }

    /// <summary>
    /// Dependency backing property for the <see cref="To"/> property.
    /// </summary>
    public static readonly DependencyProperty ToProperty =
        DependencyProperty.Register("To", typeof(double), typeof(BindableDoubleAnimation), new UIPropertyMetadata(0d, new PropertyChangedCallback((s, e) =>
            {
                BindableDoubleAnimation sender = (BindableDoubleAnimation)s;
                sender.internalAnimation.To = (double)e.NewValue;
            })));


    public double From
    {
        get { return (double)GetValue(FromProperty); }
        set { SetValue(FromProperty, value); }
    }

    /// <summary>
    /// Dependency backing property for the <see cref="From"/> property.
    /// </summary>
    public static readonly DependencyProperty FromProperty =
        DependencyProperty.Register("From", typeof(double), typeof(BindableDoubleAnimation), new UIPropertyMetadata(0d, new PropertyChangedCallback((s, e) =>
        {
            BindableDoubleAnimation sender = (BindableDoubleAnimation)s;
            sender.internalAnimation.From = (double)e.NewValue;
        })));


    public BindableDoubleAnimation()
    {
        internalAnimation = new DoubleAnimation();
    }

    protected override double GetCurrentValueCore(double defaultOriginValue, double defaultDestinationValue, AnimationClock animationClock)
    {
        return internalAnimation.GetCurrentValue(defaultOriginValue, defaultDestinationValue, animationClock);
    }

    protected override Freezable CreateInstanceCore()
    {
        return internalAnimation.Clone();;
    }
}

我现在可以自由地使用To From属性的绑定。

<local:BindableDoubleAnimation Storyboard.TargetProperty="Width" From="0" To="{Binding ActualWidth, RelativeSource={RelativeSource AncestorType=Window}}"/>

有人成功地将此应用于位于ControlTemplate的VisualState中的Storyboard内部的BindableDoubleAnimation吗?对于我在VisualState中只有一个BindableDoubleAnimation并且硬编码了"To"值(例如说"100"),没有任何动画效果,GetCurrentValueCore和CreateInstanceCore也都没有被调用。对于我在"To"属性上使用数据绑定的情况,绑定总是失败,输出错误信息“无法找到绑定源...”。即使我尝试完全按照以上方法操作,它仍然失败。 - Jason Frank
我很想让它工作 - 它似乎是一个很好的解决方案! - Jason Frank

2

无法在动画中使用绑定是一个常见的问题。

一个解决方法是使用一个辅助对象,该对象具有SourceValueTargetValue(可以将其绑定到任何你想要的东西,包括ActualWidth),一个CurrentValue(返回动画值)和一个Progress,它可以从0.0到100.0进行动画处理。当进度改变时,您只需要计算出CurrentValue,然后可以将其绑定到您对象的Width上。这并不美观,但应该有效。


我对这个答案不是很清楚。你能提供一段代码示例吗?你是在描述类似于@Andy的解决方案吗? - Phillip Scott Givens
@PhillipScottGivens:Andy的答案总是通过在其上创建可绑定层来修改动画。我建议的是不同的,我建议使用一个中介对象,它始终使用相同的动画,将其“进度”属性从“0”到“100”进行动画处理(因此动画不会改变),该对象通过“CurrentValue”属性提供动画值,该属性返回从其“SourceValue”、“TargetValue”和“Progress”计算出的值。 - H.B.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接