WPF实现QQ群文件列表动画(一)

简介: 原文:WPF实现QQ群文件列表动画(一)  QQ群大家都用过,先看下目前QQ的群文件列表容器的效果:   细心点大家就会发现,这玩意收缩和展开是带动画的,并不是很僵硬地直接收缩或者直接展开,毫无疑问,如果用WPF实现这样的效果,这里的最佳控件是Expander,WPF的Expander控件自带Collapse和Expand功能,但是用过Expander的人都知道,这玩意的Collapse或者Expand是瞬间完成的,找遍Expander所有的属性,没有发现能设置为动画伸缩的,于是想到它里面一探究竟。
原文: WPF实现QQ群文件列表动画(一)

  QQ群大家都用过,先看下目前QQ的群文件列表容器的效果:

  细心点大家就会发现,这玩意收缩和展开是带动画的,并不是很僵硬地直接收缩或者直接展开,毫无疑问,如果用WPF实现这样的效果,这里的最佳控件是Expander,WPF的Expander控件自带Collapse和Expand功能,但是用过Expander的人都知道,这玩意的Collapse或者Expand是瞬间完成的,找遍Expander所有的属性,没有发现能设置为动画伸缩的,于是想到它里面一探究竟。用Blend编辑样式如下图:

  通过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也解决了。以下是效果图:

以下是部分代码:

  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事件执行动画。

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>
View Code

  到这里似乎不需要再写个继承类来实现什么逻辑了,的确,这样的功能一个样式就搞定了,不过这里面有个缺陷,至于是什么缺陷,有什么办法弥补,将在下一篇阐述,敬请期待。

  本篇源码已在QQ群里共享,如有需要可以下载来研究。

目录
相关文章
|
缓存 C# 虚拟化
WPF列表性能提高技术
WPF数据绑定系统不仅需要绑定功能,还需要能够处理大量数据而不会降低显示速度和消耗大量内存,WPF提供了相关的控件以提高性能,所有继承自`ItemsControl`的控件都支持该技术。
|
3月前
|
C#
WPF中动画教程(DoubleAnimation的基本使用)
WPF中动画教程(DoubleAnimation的基本使用)
68 0
|
2月前
|
算法 C# Windows
不可不知的WPF动画(Animation)
【9月更文挑战第19天】在 WPF(Windows Presentation Foundation)中,动画能为应用程序增添生动性和交互性。主要类型包括线性动画和关键帧动画,可针对依赖属性和自定义属性操作。触发方式有事件触发和自动触发,支持暂停、恢复及停止控制。合理使用这些功能并注意性能优化,可创建引人入胜的用户界面。
|
3月前
|
开发框架 缓存 前端开发
循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(11) -- 下拉列表的数据绑定以及自定义系统字典列表控件
循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(11) -- 下拉列表的数据绑定以及自定义系统字典列表控件
|
3月前
|
开发框架 前端开发 JavaScript
循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(5) -- 树列表TreeView的使用
循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(5) -- 树列表TreeView的使用
|
3月前
|
C# UED 开发者
WPF与性能优化:掌握这些核心技巧,让你的应用从卡顿到丝滑,彻底告别延迟,实现响应速度质的飞跃——从布局到动画全面剖析与实例演示
【8月更文挑战第31天】本文通过对比优化前后的方法,详细探讨了提升WPF应用响应速度的策略。文章首先分析了常见的性能瓶颈,如复杂的XAML布局、耗时的事件处理、不当的数据绑定及繁重的动画效果。接着,通过具体示例展示了如何简化XAML结构、使用后台线程处理事件、调整数据绑定设置以及利用DirectX优化动画,从而有效提升应用性能。通过这些优化措施,WPF应用将更加流畅,用户体验也将得到显著改善。
184 0
|
3月前
|
C# 开发者 Windows
WPF与PDF文档:解锁创建和编辑PDF文件的新技能——从环境配置到代码实践,手把手教你如何在WPF应用中高效处理PDF,提升文档管理效率
【8月更文挑战第31天】随着数字文档的普及,PDF因跨平台兼容性和高保真度成为重要格式。WPF虽不直接支持PDF处理,但借助第三方库(如iTextSharp)可在WPF应用中实现PDF的创建与编辑。本文通过具体案例和示例代码,详细介绍了如何在WPF中集成PDF库,并展示了从设计用户界面到实现PDF创建与编辑的完整流程。不仅包括创建新文档的基本步骤,还涉及在现有PDF中添加页眉页脚等高级功能。通过这些示例,WPF开发者可以更好地掌握PDF处理技术,提升应用程序的功能性和实用性。
124 0
|
3月前
|
C# 开发者 UED
WPF开发者必备秘籍:深度解析文件对话框使用技巧,打开与保存文件原来如此简单!
【8月更文挑战第31天】在WPF应用开发中,文件操作是常见需求。本文详细介绍了如何利用`Microsoft.Win32`命名空间下的`OpenFileDialog`和`SaveFileDialog`类来正确实现文件打开与保存功能。通过示例代码展示了如何设置文件过滤器、初始目录等属性,并使用对话框进行文件读写操作。正确使用文件对话框能显著提升用户体验,使应用更友好易用。
69 0
|
3月前
|
C# UED 开发者
WPF动画大揭秘:掌握动画技巧,让你的界面动起来,告别枯燥与乏味!
【8月更文挑战第31天】在WPF应用开发中,动画能显著提升用户体验,使其更加生动有趣。本文将介绍WPF动画的基础知识和实现方法,包括平移、缩放、旋转等常见类型,并通过示例代码展示如何使用`DoubleAnimation`创建平移动画。此外,还将介绍动画触发器的使用,帮助开发者更好地控制动画效果,提升应用的吸引力。
150 0
|
3月前
|
存储 设计模式 开发框架
循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(7) -- 图标列表展示和选择处理
循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(7) -- 图标列表展示和选择处理