使用行为动画扩展器

时间:2013-06-21 17:22:12

标签: c# wpf xaml

尝试在展开和折叠事件期间使用行为为扩展器设置动画,它在扩展时有效,但在折叠时无效。花了很长时间才弄清楚原因(Visibility == Collapsed)我崩溃时无法使它成为动画。

在抓取初始内容大小方面存在一些黑客攻击,如果内容发生变化,动画肯定是不正确的,但是在内容发生变化的情况下,没有任何类型的ContentChanged可以挂钩并抓住新的大小。

行为:

public class AnimatedExpanderBehavior : Behavior<Expander>
{
    public Duration Duration { get; set; }
    private Size ContentSize { get; set; }

    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.Collapsed += AssociatedObject_Collapsed;
        AssociatedObject.Expanded += AssociatedObject_Expanded;
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        AssociatedObject.Collapsed -= AssociatedObject_Collapsed;
        AssociatedObject.Expanded -= AssociatedObject_Expanded;
    }

    private void AssociatedObject_Collapsed(object sender, RoutedEventArgs e)
    {
        var expander = sender as Expander;
        if (expander != null)
        {
            var name = expander.Content as FrameworkElement;
            if (name != null)
            {
                // Does not happen, collapses instantly instead
                var animation = new DoubleAnimation(name.ActualHeight, 0, Duration);
                name.BeginAnimation(FrameworkElement.HeightProperty, animation);
            }
        }
    }

    private void AssociatedObject_Expanded(object sender, RoutedEventArgs e)
    {
        var expander = sender as Expander;
        if (expander != null)
        {
            var name = expander.Content as UIElement;
            if (name != null)
            {
                // Grabbing initial content size
                if (ContentSize.Width <= 0 && ContentSize.Height <= 0)
                {
                    name.Measure(new Size(9999, 9999));
                    ContentSize = name.DesiredSize;
                }
                var animation = new DoubleAnimation(0, ContentSize.Height, Duration);
                name.BeginAnimation(FrameworkElement.HeightProperty, animation);
            }
        }
    }
}

用法:

<Expander>
    <i:Interaction.Behaviors>
        <behaviors:AnimatedExpanderBehavior Duration="0:0:0.2" />
    </i:Interaction.Behaviors>
    <Rectangle Height="100" Fill="Red" />
</Expander>

有趣的是,我一直在研究Windows UI是如何做到的,我绝对肯定它是两种方式都做到了,而实际上只是在扩展期间才这样做。

是否有任何限制会阻止在折叠时实现此类动画?

修改

新代码,但是当原始扩展程序执行内容更改时,它不会调整:

    private void AssociatedObject_Expanded(object sender, RoutedEventArgs e)
    {
        var expander = sender as Expander;
        if (expander != null)
        {
            var name = expander.Content as FrameworkElement;
            if (name != null)
            {
                _expandSite.Visibility = Visibility.Visible;
                double height;
                if (_firstExpansion)
                {
                    name.Measure(new Size(9999, 9999));
                    height = name.DesiredSize.Height;
                    _firstExpansion = false;
                }
                else
                {
                    height = name.RenderSize.Height;
                }
                var animation = new DoubleAnimation(0, height, new Duration(TimeSpan.FromSeconds(0.5d)));
                name.BeginAnimation(FrameworkElement.HeightProperty, animation);
            }
        }
    }

1 个答案:

答案 0 :(得分:3)

此处的问题是Expander.ControlTemplate只有ContentPresenter Visibility Collapsed IsExpanded false ContentPresenter p>

因此,即使您的动画实际运行,您也永远不会看到它,因为它的父级是不可见的。这个ExpandSite被称为private UIElement _expandSite; protected override void OnAttached() { base.OnAttached(); AssociatedObject.Collapsed += AssociatedObject_Collapsed; AssociatedObject.Expanded += AssociatedObject_Expanded; AssociatedObject.Loaded += (sender, args) => { _expandSite = AssociatedObject.Template.FindName("ExpandSite", AssociatedObject) as UIElement; if (_expandSite == null) throw new InvalidOperationException(); }; } ... private void AssociatedObject_Collapsed(object sender, RoutedEventArgs e) { var expander = sender as Expander; if (expander == null) return; var name = expander.Content as FrameworkElement; if (name == null) return; _expandSite.Visibility = Visibility.Visible; var animation = new DoubleAnimation(name.ActualHeight, 0, Duration); animation.Completed += (o, args) => { _expandSite.Visibility = Visibility.Collapsed; name.BeginAnimation(FrameworkElement.HeightProperty, null); }; name.BeginAnimation(FrameworkElement.HeightProperty, animation); } private void AssociatedObject_Expanded(object sender, RoutedEventArgs e) { var expander = sender as Expander; if (expander == null) return; var name = expander.Content as FrameworkElement; if (name == null) return; if (name.DesiredSize.Width <= 0 && name.DesiredSize.Height <= 0) name.Measure(new Size(9999, 9999)); _expandSite.Visibility = Visibility.Visible; var animation = new DoubleAnimation(0, name.DesiredSize.Height, Duration); animation.Completed += (o, args) => name.BeginAnimation(FrameworkElement.HeightProperty, null); name.BeginAnimation(FrameworkElement.HeightProperty, animation); } (来自默认模板),我们可以通过类似

的行为来控制它
_expandSite.Visibility = Visibility.Visible;

我们在扩展动画之前设置Visibility的原因是我们在行为中设置ExpandSite Trigger.Setter时的优先级,它优先于Style默认Visibility被忽略。因此,我们在两种情况下都要管理Behavior<...>

您确实拥有整个流程的替代方案。请勿使用Style,而只是为Expander提供自定义Trigger.Enter/ExitActions,并在ControlTemplate中相应指定Visibility以设置ExpandSite动画ContentVisibility

<强>更新

示例下载:Link

重新调整大小的问题也存在于您的原始代码中。它与我发布的答案无关,因为我们添加的是切换ExpandSite的{​​{1}}。该问题是由于动画冻结了Height的{​​{1}}属性,因此除非通过以下动画,否则不会允许任何未来的更改。

这个^^样本也应该有解决方法。