天天看點

WPF實作QQ群檔案清單動畫(一)

原文: WPF實作QQ群檔案清單動畫(一)   QQ群大家都用過,先看下目前QQ的群檔案清單容器的效果:

WPF實作QQ群檔案清單動畫(一)
  細心點大家就會發現,這玩意收縮和展開是帶動畫的,并不是很僵硬地直接收縮或者直接展開,毫無疑問,如果用WPF實作這樣的效果,這裡的最佳控件是Expander,WPF的Expander控件自帶Collapse和Expand功能,但是用過Expander的人都知道,這玩意的Collapse或者Expand是瞬間完成的,找遍Expander所有的屬性,沒有發現能設定為動畫伸縮的,于是想到它裡面一探究竟。用Blend編輯樣式如下圖:
WPF實作QQ群檔案清單動畫(一)

  通過Blend可以清楚地看到Expand的時候發生了什麼,當它Expand的時候,把ExpandSite的Visibility改成了Visible,ExpandSite是什麼呢,它就是Expander的ContentPresenter,即内容載體,難怪呢,Visibility是瞬間的,沒有什麼動畫可言。

  看到這也許你會很失望,因為沒法對Visibility這個屬性做動畫。是不是真的沒有辦法了呢,當然不是,沒有條件,可以創造條件。想想能做動畫的是什麼,最直接的,高度,或者——Transform,那麼,即使我能對單個Expander做動畫伸縮,怎麼保證其他的Expander能動畫上下位移呢?現在有3個問題需要解決:

  1.如何去掉Expander本身的Expander和Collapse效果,因為自帶的效果是單純的設定Visibility,這個屬性是沒法做動畫的

  2.如何對單個Expander做動畫伸縮,也就是使用它的哪個屬性做動畫

  3.對某個Expander伸縮的時候如何讓其他的Expander自動位移

這三個問題解決了,那麼這種效果就實作了。

問題1的解決思路

  似乎這三個問題都涉及到了控件内部的一些邏輯,最直接的想法是寫個類繼承Expander,然後去override相關函數,我試過,沒什麼作用,即使不用調base的函數,該出現的還是會出現。如果我有源碼,或者我會在Measure裡做些什麼,也許可以改動一些邏輯,可惜我不會,我隻會改改樣式什麼的。于是我想到了繼承和樣式相結合——事實上這種辦法很大程度上簡化了控件的開發(相對于遊離在VisualTree和LogicTree之間的程式員來說),因為Style能快速增減控件,但是實作的邏輯有限,而繼承控件能輕易實作邏輯,但對于一些人來說,繼承後再加個控件,在哪裡加,位置、背景、Margin、BorderThickness如何這些都太TM難了,調試難度也不低,是以繼承和樣式結合,各取所優,利益最大化。這樣的話,我可以寫個樣式,把IsExpanded觸發的邏輯去掉,然後寫個類繼承Expander,在構造函數裡找到這個樣式并設定為自己的Style,那麼第一個問題就解決了。

問題2的解決思路

  我想選高度來做動畫吧,收縮好辦,變為0就可以了,展開呢,高度該變為多少呢,Expander的高度是Auto,也就是根據内容來的,内容有多高展開就有多高,DoubleAnimation的To隻是一個Double,沒法綁定,這條路似乎有點難度。那麼我用變形效果做動畫呢,收縮的時候Y軸縮放為0,展開的時候Y軸縮放為1,這樣我根本不用關心Expander的高度具體是多少,這樣一來,問題2也得到了解決。

問題3的解決思路

  單個的Expander行為怎麼去影響别人的行為呢,這似乎有點為難。其實也不難,選好容器就可以,你把它們放在Grid裡肯定是不行的,Gird隻提供行列和Margin,如果要我關聯Expander的伸縮事件然後挨個去設行列或者margin,那是會死人的。很顯然,StackPanel最适合不過了,StackPanel提供Children了自動占用空間的特性,當一個child的高度變小,其他控件是會跟着移動的。但是還有個問題,我是選Expander的變形來做動畫,印象中控件的變形效果其實不是發生了真正的布局改變,是以還有一點要注意,就是使用LayoutTransform,這個變形效果是會影響到布局的,而這正是我想要的結果,這樣一來,問題3也解決了。以下是效果圖:

WPF實作QQ群檔案清單動畫(一)

以下是部分代碼:

WPF實作QQ群檔案清單動畫(一)
WPF實作QQ群檔案清單動畫(一)
1 <Setter Property="Template">
  2             <Setter.Value>
  3                 <ControlTemplate TargetType="{x:Type Expander}">
  4                     <ControlTemplate.Resources>
  5                         <Storyboard x:Key="STHide">
  6                             <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.LayoutTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)"
  7                                                            Storyboard.TargetName="ExpandSite">
  8                                 <EasingDoubleKeyFrame KeyTime="0:0:0.2"
  9                                                       Value="0" />
 10                             </DoubleAnimationUsingKeyFrames>
 11                             <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)"
 12                                                            Storyboard.TargetName="ExpandSite">
 13                                 <EasingDoubleKeyFrame KeyTime="0:0:0.2"
 14                                                       Value="1" />
 15                             </DoubleAnimationUsingKeyFrames>
 16                         </Storyboard>
 17                         <Storyboard x:Key="STShow">
 18                             <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.LayoutTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)"
 19                                                            Storyboard.TargetName="ExpandSite">
 20                                 <EasingDoubleKeyFrame KeyTime="0"
 21                                                       Value="0" />
 22                                 <EasingDoubleKeyFrame KeyTime="0:0:0.2"
 23                                                       Value="1" />
 24                             </DoubleAnimationUsingKeyFrames>
 25                             <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)"
 26                                                            Storyboard.TargetName="ExpandSite">
 27                                 <EasingDoubleKeyFrame KeyTime="0"
 28                                                       Value="0" />
 29                                 <EasingDoubleKeyFrame KeyTime="0:0:0.2"
 30                                                       Value="1" />
 31                             </DoubleAnimationUsingKeyFrames>
 32                         </Storyboard>
 33                     </ControlTemplate.Resources>
 34                     <Border BorderBrush="{TemplateBinding BorderBrush}"
 35                             BorderThickness="{TemplateBinding BorderThickness}"
 36                             Background="{TemplateBinding Background}"
 37                             CornerRadius="3"
 38                             SnapsToDevicePixels="true">
 39                         <DockPanel>
 40                             <ToggleButton x:Name="HeaderSite"
 41                                           ContentTemplate="{TemplateBinding HeaderTemplate}"
 42                                           ContentTemplateSelector="{TemplateBinding HeaderTemplateSelector}"
 43                                           Content="{TemplateBinding Header}"
 44                                           DockPanel.Dock="Top"
 45                                           Foreground="{TemplateBinding Foreground}"
 46                                           FontWeight="{TemplateBinding FontWeight}"
 47                                           FocusVisualStyle="{StaticResource ExpanderHeaderFocusVisual}"
 48                                           FontStyle="{TemplateBinding FontStyle}"
 49                                           FontStretch="{TemplateBinding FontStretch}"
 50                                           FontSize="{TemplateBinding FontSize}"
 51                                           FontFamily="{TemplateBinding FontFamily}"
 52                                           HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
 53                                           IsChecked="{Binding IsExpanded, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
 54                                           Margin="1"
 55                                           MinWidth="0"
 56                                           MinHeight="0"
 57                                           Padding="{TemplateBinding Padding}"
 58                                           Style="{StaticResource ExpanderDownHeaderStyle}"
 59                                           VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" />
 60                             <ContentPresenter x:Name="ExpandSite"
 61                                               DockPanel.Dock="Bottom"
 62                                               Focusable="false"
 63                                               HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
 64                                               Margin="{TemplateBinding Padding}"
 65                                               Visibility="Visible"
 66                                               VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
 67                                 <ContentPresenter.LayoutTransform>
 68                                     <TransformGroup>
 69                                         <ScaleTransform />
 70                                         <SkewTransform />
 71                                         <RotateTransform />
 72                                         <TranslateTransform />
 73                                     </TransformGroup>
 74                                 </ContentPresenter.LayoutTransform>
 75                             </ContentPresenter>
 76                         </DockPanel>
 77                     </Border>
 78                     <ControlTemplate.Triggers>
 79                         <EventTrigger RoutedEvent="FrameworkElement.Loaded">
 80                             <BeginStoryboard Storyboard="{StaticResource STHide}" />
 81                         </EventTrigger>
 82                         <EventTrigger RoutedEvent="Expander.Expanded">
 83                             <BeginStoryboard x:Name="STShow_BeginStoryboard"
 84                                              Storyboard="{StaticResource STShow}" />
 85                         </EventTrigger>
 86                         <EventTrigger RoutedEvent="Expander.Collapsed">
 87                             <BeginStoryboard Storyboard="{StaticResource STHide}" />
 88                         </EventTrigger>
 89                         <Trigger Property="ExpandDirection"
 90                                  Value="Right">
 91                             <Setter Property="DockPanel.Dock"
 92                                     TargetName="ExpandSite"
 93                                     Value="Right" />
 94                             <Setter Property="DockPanel.Dock"
 95                                     TargetName="HeaderSite"
 96                                     Value="Left" />
 97                             <Setter Property="Style"
 98                                     TargetName="HeaderSite"
 99                                     Value="{StaticResource ExpanderRightHeaderStyle}" />
100                         </Trigger>
101                         <Trigger Property="ExpandDirection"
102                                  Value="Up">
103                             <Setter Property="DockPanel.Dock"
104                                     TargetName="ExpandSite"
105                                     Value="Top" />
106                             <Setter Property="DockPanel.Dock"
107                                     TargetName="HeaderSite"
108                                     Value="Bottom" />
109                             <Setter Property="Style"
110                                     TargetName="HeaderSite"
111                                     Value="{StaticResource ExpanderUpHeaderStyle}" />
112                         </Trigger>
113                         <Trigger Property="ExpandDirection"
114                                  Value="Left">
115                             <Setter Property="DockPanel.Dock"
116                                     TargetName="ExpandSite"
117                                     Value="Left" />
118                             <Setter Property="DockPanel.Dock"
119                                     TargetName="HeaderSite"
120                                     Value="Right" />
121                             <Setter Property="Style"
122                                     TargetName="HeaderSite"
123                                     Value="{StaticResource ExpanderLeftHeaderStyle}" />
124                         </Trigger>
125                         <Trigger Property="IsEnabled"
126                                  Value="false">
127                             <Setter Property="Foreground"
128                                     Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
129                         </Trigger>
130                     </ControlTemplate.Triggers>
131                 </ControlTemplate>
132             </Setter.Value>
133         </Setter>      

View Code

  這個樣式主要是用過LayoutTransform下的ScaleTransForm做了伸縮動畫,然後關聯Expanded和Collapsed事件執行動畫。

WPF實作QQ群檔案清單動畫(一)
WPF實作QQ群檔案清單動畫(一)
1   <EventTrigger RoutedEvent="Expander.Expanded">
2                             <BeginStoryboard x:Name="STShow_BeginStoryboard"
3                                              Storyboard="{StaticResource STShow}" />
4                         </EventTrigger>
5                         <EventTrigger RoutedEvent="Expander.Collapsed">
6                             <BeginStoryboard Storyboard="{StaticResource STHide}" />
7                         </EventTrigger>      

  到這裡似乎不需要再寫個繼承類來實作什麼邏輯了,的确,這樣的功能一個樣式就搞定了,不過這裡面有個缺陷,至于是什麼缺陷,有什麼辦法彌補,将在下一篇闡述,敬請期待。

  本篇源碼已在QQ群裡共享,如有需要可以下載下傳來研究。

繼續閱讀