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

以下是部分代码:

 <Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Expander}">
<ControlTemplate.Resources>
<Storyboard x:Key="STHide">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.LayoutTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)"
Storyboard.TargetName="ExpandSite">
<EasingDoubleKeyFrame KeyTime="0:0:0.2"
Value="0" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)"
Storyboard.TargetName="ExpandSite">
<EasingDoubleKeyFrame KeyTime="0:0:0.2"
Value="1" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="STShow">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.LayoutTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)"
Storyboard.TargetName="ExpandSite">
<EasingDoubleKeyFrame KeyTime="0"
Value="0" />
<EasingDoubleKeyFrame KeyTime="0:0:0.2"
Value="1" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)"
Storyboard.TargetName="ExpandSite">
<EasingDoubleKeyFrame KeyTime="0"
Value="0" />
<EasingDoubleKeyFrame KeyTime="0:0:0.2"
Value="1" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</ControlTemplate.Resources>
<Border BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
CornerRadius="3"
SnapsToDevicePixels="true">
<DockPanel>
<ToggleButton x:Name="HeaderSite"
ContentTemplate="{TemplateBinding HeaderTemplate}"
ContentTemplateSelector="{TemplateBinding HeaderTemplateSelector}"
Content="{TemplateBinding Header}"
DockPanel.Dock="Top"
Foreground="{TemplateBinding Foreground}"
FontWeight="{TemplateBinding FontWeight}"
FocusVisualStyle="{StaticResource ExpanderHeaderFocusVisual}"
FontStyle="{TemplateBinding FontStyle}"
FontStretch="{TemplateBinding FontStretch}"
FontSize="{TemplateBinding FontSize}"
FontFamily="{TemplateBinding FontFamily}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
IsChecked="{Binding IsExpanded, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
Margin="1"
MinWidth="0"
MinHeight="0"
Padding="{TemplateBinding Padding}"
Style="{StaticResource ExpanderDownHeaderStyle}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" />
<ContentPresenter x:Name="ExpandSite"
DockPanel.Dock="Bottom"
Focusable="false"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Margin="{TemplateBinding Padding}"
Visibility="Visible"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
<ContentPresenter.LayoutTransform>
<TransformGroup>
<ScaleTransform />
<SkewTransform />
<RotateTransform />
<TranslateTransform />
</TransformGroup>
</ContentPresenter.LayoutTransform>
</ContentPresenter>
</DockPanel>
</Border>
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard Storyboard="{StaticResource STHide}" />
</EventTrigger>
<EventTrigger RoutedEvent="Expander.Expanded">
<BeginStoryboard x:Name="STShow_BeginStoryboard"
Storyboard="{StaticResource STShow}" />
</EventTrigger>
<EventTrigger RoutedEvent="Expander.Collapsed">
<BeginStoryboard Storyboard="{StaticResource STHide}" />
</EventTrigger>
<Trigger Property="ExpandDirection"
Value="Right">
<Setter Property="DockPanel.Dock"
TargetName="ExpandSite"
Value="Right" />
<Setter Property="DockPanel.Dock"
TargetName="HeaderSite"
Value="Left" />
<Setter Property="Style"
TargetName="HeaderSite"
Value="{StaticResource ExpanderRightHeaderStyle}" />
</Trigger>
<Trigger Property="ExpandDirection"
Value="Up">
<Setter Property="DockPanel.Dock"
TargetName="ExpandSite"
Value="Top" />
<Setter Property="DockPanel.Dock"
TargetName="HeaderSite"
Value="Bottom" />
<Setter Property="Style"
TargetName="HeaderSite"
Value="{StaticResource ExpanderUpHeaderStyle}" />
</Trigger>
<Trigger Property="ExpandDirection"
Value="Left">
<Setter Property="DockPanel.Dock"
TargetName="ExpandSite"
Value="Left" />
<Setter Property="DockPanel.Dock"
TargetName="HeaderSite"
Value="Right" />
<Setter Property="Style"
TargetName="HeaderSite"
Value="{StaticResource ExpanderLeftHeaderStyle}" />
</Trigger>
<Trigger Property="IsEnabled"
Value="false">
<Setter Property="Foreground"
Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>

  这个样式主要是用过LayoutTransform下的ScaleTransForm做了伸缩动画,然后关联Expanded和Collapsed事件执行动画。

   <EventTrigger RoutedEvent="Expander.Expanded">
<BeginStoryboard x:Name="STShow_BeginStoryboard"
Storyboard="{StaticResource STShow}" />
</EventTrigger>
<EventTrigger RoutedEvent="Expander.Collapsed">
<BeginStoryboard Storyboard="{StaticResource STHide}" />
</EventTrigger>

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

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

WPF实现QQ群文件列表动画(一)的更多相关文章

  1. WPF实现QQ群文件列表动画(二)

    上篇(WPF实现QQ群文件列表动画(一))介绍了WPF实现QQ群文件列表动画的大致思路,结合我之前讲过的WPF里ItemsControl的分组实现,实现起来问题不大,以下是效果图: 其实就是个List ...

  2. QQ群文件下载速度慢-解决办法

    QQ群文件下载速度慢-解决办法 本方法是本人亲测测试出来的,特此和大家分享 没有效果让你打我 解决方法 1.打开[手机版 QQ] 2.进入群文件,找到需要下载文件 3.[分享],先点击[发给好友],选 ...

  3. QQ群文件未通过安全检查,禁止下载该文件

    直接用手机收藏群文件,然后用电脑登上qq去收藏里面下载就ok了

  4. 【QQ技术】群文件报毒怎样下载?~ 变相绕过QQ复杂检验过程

    刚才又人问我,要是群文件被鉴定为病毒那怎么下载? 我简单说一下吧: 其实qq客户端过滤比较严的,而web段却还是老一套,很多人说出现这个情况其实是腾讯已经把他库里面的文件删了,其实不然 如果源删了,那 ...

  5. WPF编程,通过KeyFrame 类型制作控件线性动画的一种方法。

    原文:WPF编程,通过KeyFrame 类型制作控件线性动画的一种方法. 版权声明:我不生产代码,我只是代码的搬运工. https://blog.csdn.net/qq_43307934/articl ...

  6. 深入理解MVC C#+HtmlAgilityPack+Dapper走一波爬虫 StackExchange.Redis 二次封装 C# WPF 用MediaElement控件实现视频循环播放 net 异步与同步

    深入理解MVC   MVC无人不知,可很多程序员对MVC的概念的理解似乎有误,换言之他们一直在错用MVC,尽管即使如此软件也能被写出来,然而软件内部代码的组织方式却是不科学的,这会影响到软件的可维护性 ...

  7. Qt定制控件列表

    目录 炫酷进度条 提示框 小时钟 高仿excel表格 多级表头表格 多级表头树控件 多维度折线图 表格控件-蚂蚁线 日历控件 饼图 窗体靠边自动隐藏 下拉框内容定制 模仿QQ上传头像 菜单定制 属性表 ...

  8. jQuery演示10种不同的切换图片列表动画效果以及tab动画演示 2

    很常用的一款特效纯CSS完成tab实现5种不同切换对应内容效果 实例预览 下载地址 实例代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ...

  9. IOS开发-UI学习-使用UIImageView控件制作动画

    先添加40张tomcat的图片到资源列表中:名称为cat_eat0000.jpg到cat_eat0039.jpg. 1.定义所需控件 // 定义按钮,图片控件.可变数组对象 UIButton *act ...

随机推荐

  1. 《C#高效编程》读书笔记04-使用Conditional特性而不是#if条件编译

    #if/#endif语句常用来基于同一份源代码生成不同的编译结果,其中最常见的就是debug版和release版.但是这在实际应用中并不是非常友好,因为它们容易被滥用,其代码也难以理解或调试. C#为 ...

  2. JavaSE之Java基础(3)

    11.什么是值传递和引用传递? 值传递:方法调用时,实际参数把它的值传递给对应的形式参数,方法执行中形式参数值的改变不影响实际参数的值. 引用传递:也称为传地址.方法调用时,实际参数的引用被传递给方法 ...

  3. bootstrap Table的 一些小操作

    function HQCreatTable(ob) { var option = { cache: false,//是否使用缓存,默认为true,所以一般情况下需要设置一下这个属性(*) scroll ...

  4. NGSL + NAWL 单词表 以及学习网站

    https://quizlet.com/44769538/nawl-1-1-50-flash-cards/ NAWL 网站 NAWL 单词表  + NGSL 单词表 http://www.newgen ...

  5. echarts的title和legend重合解决(各种小细节)

    一:关于title与legend重叠 1.重合样子 2.解决办法: legend:{ show: true, top:"6%",//与上方的距离 可百分比% 可像素px }, 3. ...

  6. Eucalyptus简介

    1.Eucalyptus是什么? Eucalyptus  n.桉树 Eucalyptus is a Linux-based software architecture that implements ...

  7. 《Unity預計算即時GI》笔记:二、光照图

    说明 这篇文章是对<Unity預計算即時GI>这个系列文章的笔记. 光照图 什么是光照图 光照图在第三章中有如下的定义,读起来很是费解. 一個光照圖(Chart)是表示一個光照貼圖的區域, ...

  8. Win10 耳机无声音,扬声器有声音的解决办法

    注:适用于WIN10及WIN10.ubuntu双系统耳机无声音的问题. 在WIN10的引导下,安装了Ubuntu的桌面版,作成了双系统,可是发现了一个问题:进入Win10后插耳机会没有声音,扬声器有声 ...

  9. squid隐藏squid的版本号

    reply_header_access Via deny all reply_header_access Cache-Control deny all reply_header_access Serv ...

  10. python_61_装饰器4

    import time def timer(func):#timer(test1) func=test1 def deco(): start_time=time.time() func()#run t ...