2013-06-21 108 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