2013-06-21 65 views
1

尝试在展开和折叠事件期间使用行为对扩展器进行动画制作,它在展开时起作用,但在折叠时不起作用。花费了相当长的一段时间试图找出原因(可见性==折叠)后,我无法在折叠时使它变成动画。使用行为动画扩展器

在抓取初始内容大小方面存在一些破绽,动画在内容更改的情况下肯定会不正确,但不会有类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用户界面是怎么做的,我是绝对肯定的是,它是做左右逢源,而事实上它只有在扩展时才会这样做。

是否有任何形式的限制,会阻止实现这种动画时崩溃?

编辑

新的代码,但它不当而原来的扩张做内容的变化调整:

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); 
      } 
     } 
    } 

回答

3

你这里的问题是Expander.ControlTemplate持有ContentPresenterVisibility设置为Collapsed只要IsExpanded变成false

因此,即使您的动画制作盟友跑你永远不会看到它,因为它的父母是不可见的。这ContentPresenterExpandSite(从默认模板),我们可以得到它保持在行为与类似

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;为cos当我们设置的VisibilityExpandSite,它会优先考虑并忽略来自默认StyleTrigger.Setter。因此我们必须在两种情况下管理Visibility

您确实有一个替代整个过程。不要使用Behavior<...>,而不是仅仅提供了Expander定制Style,并在相应ControlTemplate指定Trigger.Enter/ExitActions动画的ExpandSite和你ContentVisibility

更新:

样本下载:Link

与调整大小的问题是有没有你原来的代码。这与我发布的答案无关,因为我们添加的内容是切换的Visibility。这个问题是由于动画冻结ContentHeight属性,从而不允许任何未来的更改出现,除非通过以下动画。

这^^样品应具有的修复为好。

+0

除了在第一次扩张没有它的伟大工程发生的,我管理,使用该决定使用哪个高度从RenderSize或DesiredSize但还有一个布尔值,扩展时不其内容的变化,除非它的扩展或收缩而调整本身原始风格确实。我想现在我会忘记它,因为如果没有影响原始行为的副作用,我就无法工作。但任何想法都欢迎! – Aybe

+1

@Aybe因为也许这动画握住height属性时,它被动画,并将其锁定,由于动画优先。双方的展开/折叠动画从已完成的事件设置“名称”元素的高度,它的ActualHeight然后开始与第二个参数为null,有效去除动画优先级相同的一个新的高度动画。这应该让动画不会阻止未来的调整。 – Viv

+1

@Aybe我已经更新了我的答案有一个下载链接,应该有一个大小调整问题的修补程序,以及一个样本。我不需要像第一次展开时提到的动画那样的任何布尔值。尝试下载并查看它对你的行为是否有所不同。在示例中,您有两个按钮来修改可用于测试大小更改的'Expander.Content'大小。 – Viv