1

尝试在 Expanded 和 Collapsed 事件期间使用 Behavior 为 Expander 设置动画,它在展开时有效,但在折叠时无效。在花了相当长的时间尝试找出原因(可见性 == 已折叠)后,我无法在折叠时使其动画化。

获取初始内容大小有一种技巧,如果内容发生更改,动画肯定会不正确,但是没有 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);
            }
        }
    }
4

1 回答 1

3

你的问题是Expander.ControlTemplate持有一个被设置为ContentPresenterVisibilityCollapsedIsExpandedfalse

因此,即使您的动画实际运行,您也永远看不到它,因为它的父级是不可见的。这ContentPresenter被称为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;在展开动画之前也设置了,是因为我们在设置Visibilityfrom ExpandSiteBehavior的时候,优先,忽略Trigger.Setter了默认的from 。Style因此,我们必须Visibility在这两种情况下都进行管理。

您确实有整个过程的替代方案。不要使用 a Behavior<...>,而只是Style为 the提供一个自定义并相应地在to和 your的动画中Expander指定。Trigger.Enter/ExitActionsControlTemplateVisibilityExpandSiteContent

更新:

样本下载:链接

重新调整大小的问题也存在于您的原始代码中。这与我发布的答案无关,因为我们添加的只是切换Visibility. ExpandSite该问题是由于动画冻结了 的Height属性,Content因此除非通过后续动画,否则不允许出现任何未来的更改。

这个 ^^ 示例也应该对此进行修复。

于 2013-06-21T19:28:57.373 回答