WPF自定义控件之列表滑动特效 PowerListBox

列表控件是应用程序中常见的控件之一,对其做一些绚丽的视觉特效,可以让软件增色不少。

本人网上看过一个视频,是windows phone 7系统上的一个App的列表滚动效果,效果非常炫

现在在WPF上用ListBox重现此效果

首先我们来分析一下,这种实时滚动的效果是如何实现的,有哪些步骤

1.获取ListBox模板内部的ScrollViewer和ItemsPanel

2.监听ScrollViewer的滚动事件ScrollChange, 获取ItemsPanel的布局方向

3.在滚动事件发生时计算当前可视化区域中的第一项和最后一项,这是此滑动效果的核心算法所在,算法的效率决定了滑动效果的流畅性

4.根据滚动的方向和布局的方向依次对指定的Item做动画效果。

 

重写ListBoxItem

public class PowerListBoxItem : ListBoxItem

声明构造函数并赋初始值

        static PowerListBoxItem(){DefaultStyleKeyProperty.OverrideMetadata(typeof(PowerListBoxItem), new FrameworkPropertyMetadata(typeof(PowerListBoxItem)));}public PowerListBoxItem(){ItemStatus = ItemStatusEnum.Out;  //默认Item状态为"退出"duration = new TimeSpan(0, 0, 0, 0, 300);//easingFunction = new PowerEase() { EasingMode = EasingMode.EaseIn, Power = 4 };easingFunction = new CircleEase() { EasingMode = EasingMode.EaseInOut };}

定义PowerListBoxItem的成员属性

      /// <summary>/// PowerListBoxItem模板中的内容控件/// </summary>private FrameworkElement contentControl;/// <summary>/// 动画间隔时间/// </summary>private TimeSpan duration;private IEasingFunction easingFunction;   //动画缓动函数private IList<AnimationModel> DownInAnimationList;  //定义Item从下往上运动的动画内容集合private IList<AnimationModel> UpInAnimationList;    //定义Item从上往下运动的动画内容集合/// <summary>/// 项枚举状态,指明Item运动的方向/// </summary>internal enum ItemStatusEnum{UpIn, DownIn, RightIn, LeftIn, Out}private ItemStatusEnum _itemStatus;internal ItemStatusEnum ItemStatus{get { return _itemStatus; }set{if (_itemStatus == value)   //状态相同时不再刷新状态return;_itemStatus = value;PlayAnimation(); //执行动画}}

重写ListBox 

 [StyleTypedProperty(Property = "ItemContainerStyle", StyleTargetType = typeof(PowerListBoxItem))]public class PowerListBox : ListBox{static PowerListBox(){DefaultStyleKeyProperty.OverrideMetadata(typeof(PowerListBox), new FrameworkPropertyMetadata(typeof(PowerListBox)));}public PowerListBox(){DefaultStyleKey = typeof(PowerListBox);}}protected override DependencyObject GetContainerForItemOverride(){return new PowerListBoxItem();  //指定PowerListBox的项为PowerListBoxItem}protected override bool IsItemItsOwnContainerOverride(object item){return item is PowerListBoxItem;}

定义PowerList的成员属性

      /// <summary>/// ListBox内部的滚动试图/// </summary>private ScrollViewer _scrollView;/// <summary>/// 容器的布局方向/// </summary>private Orientation _panelOrientation;/// <summary>/// 当前可视化视图的第一项/// </summary>private int firstVisibleIndex;/// <summary>/// 当前可视化视图的最后一项/// </summary>private int lastVisibleIndex;/// <summary>/// 上次滚动时可视化视图的第一项/// </summary>private int oldFirstVisibleIndex;/// <summary>/// 上次滚动时可视化视图的最后一项/// </summary>private int oldLastVisibleIndex;/// <summary>/// 标识,是否已找到第一项/// </summary>private bool isFindFirst;/// <summary>/// 当前累计已遍历过的Item高度或宽度的值,用于寻找第一项和最后一项/// </summary>private double cumulativeNum;

 获取PowerListBox内部的ScrollViewer和ItemsPanel,并监听滚动事件

        public override void OnApplyTemplate(){_scrollView = VisualHelper.FindFirstVisualChild<ScrollViewer>(this);if (_scrollView == null)return;_scrollView.CanContentScroll = false;  //不按Item为步长滚动_scrollView.PanningMode = PanningMode.Both;_scrollView.ScrollChanged += _scrollView_ScrollChanged;  //监听滚动事件var panel = this.ItemsPanel.LoadContent();  //读取布局容器if (panel is StackPanel)_panelOrientation = (panel as StackPanel).Orientation;else if (panel is VirtualizingPanel)_panelOrientation = (panel as VirtualizingStackPanel).Orientation;base.OnApplyTemplate();}private void _scrollView_ScrollChanged(object sender, ScrollChangedEventArgs e){//Console.WriteLine("itemCount:{0}  VerticalOffset:{1}  ViewportHeight:{2}   ContentVerticalOffset:{3}",//Items.Count, _scrollView.VerticalOffset, _scrollView.ViewportHeight, _scrollView.ContentVerticalOffset);
//每次滚动时都计算当前可视化区域的首尾项calculationIndex(); refreshItemStatus(); //刷新Item状态}

计算可视化区域的第一项和最后一项

     private void calculationIndex(){oldFirstVisibleIndex = firstVisibleIndex;oldLastVisibleIndex = lastVisibleIndex;isFindFirst = false;if (_panelOrientation == Orientation.Vertical){cumulativeNum = 0.0;for (int i = 0; i < Items.Count; i++){var _item = this.ItemContainerGenerator.ContainerFromIndex(i) as PowerListBoxItem;cumulativeNum += _item.ActualHeight + _item.Margin.Top + _item.Margin.Bottom;//遍历Items, 累计Item高度,第一个超过滚动条垂直偏移量的Item就是当前可视化区域中的第一项if (!isFindFirst && cumulativeNum >= _scrollView.VerticalOffset){firstVisibleIndex = i;isFindFirst = true;}//累计Item高度超过滚动条垂直偏移量和滚动区显示高度的和,就是当前可视化区域的最后一项if (cumulativeNum >= (_scrollView.VerticalOffset + _scrollView.ViewportHeight)){lastVisibleIndex = i;break;}}}}

确定当前可视化区域的首尾项之后,刷新Item的状态

     private void refreshItemStatus(){Console.WriteLine("firstIndex: {0}  lastIndex: {1}  oldFirstIndex: {2}  oldLastIndex: {3}  {4}",firstVisibleIndex, lastVisibleIndex, oldFirstVisibleIndex, oldLastVisibleIndex, firstVisibleIndex > oldFirstVisibleIndex ? "Down In" : firstVisibleIndex < oldFirstVisibleIndex ? "UpIn" : "normal");if ((firstVisibleIndex == oldFirstVisibleIndex && lastVisibleIndex == oldLastVisibleIndex) || oldFirstVisibleIndex == 0 && oldLastVisibleIndex == 0)return;//Console.WriteLine("firstVisibleIndex:{0} oldFirstVisibleIndex:{1}", firstVisibleIndex, oldFirstVisibleIndex);//判断滚动方向if (firstVisibleIndex > oldFirstVisibleIndex){//垂直  滚动条往下,内容网上//水平  滚动条往右,内容往左for (var i = oldLastVisibleIndex; i <= lastVisibleIndex; i++){var _item = this.ItemContainerGenerator.ContainerFromIndex(i) as PowerListBoxItem;_item.ItemStatus = _panelOrientation == Orientation.Vertical ? PowerListBoxItem.ItemStatusEnum.DownIn : PowerListBoxItem.ItemStatusEnum.RightIn;//Console.WriteLine("DownIn {0}", i);}}else if (lastVisibleIndex < oldLastVisibleIndex){//垂直  滚动条往上,内容网下//水平  滚动条往左,内容往右for (var i = oldFirstVisibleIndex; i >= firstVisibleIndex; i--){var _item = this.ItemContainerGenerator.ContainerFromIndex(i) as PowerListBoxItem;_item.ItemStatus = _panelOrientation == Orientation.Vertical ? PowerListBoxItem.ItemStatusEnum.UpIn : PowerListBoxItem.ItemStatusEnum.LeftIn;//Console.WriteLine("UpIn {0}", i);}}}

定义PowerListBox的默认外观

 <Style TargetType="{x:Type local:PowerListBox}"><Setter Property="Background" Value="Transparent"/><Setter Property="BorderThickness" Value="0"/><Setter Property="BorderBrush" Value="Transparent"/><Setter Property="Padding" Value="0"/><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="{x:Type local:PowerListBox}"><ScrollViewer x:Name="ScrollViewer" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Foreground="{TemplateBinding Foreground}" Padding="{TemplateBinding Padding}"><ItemsPresenter/></ScrollViewer></ControlTemplate></Setter.Value></Setter></Style><Style TargetType="{x:Type local:PowerListBoxItem}"><Setter Property="Background" Value="Transparent"/><Setter Property="BorderThickness" Value="0"/><Setter Property="BorderBrush" Value="Transparent"/><Setter Property="Padding" Value="0"/><Setter Property="HorizontalContentAlignment" Value="Stretch"/><Setter Property="VerticalContentAlignment" Value="Stretch"/><Setter Property="Margin" Value="0,8"/><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="{x:Type local:PowerListBoxItem}"><Border x:Name="LayoutRoot" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" HorizontalAlignment="{TemplateBinding HorizontalAlignment}" VerticalAlignment="{TemplateBinding VerticalAlignment}"><ContentControl x:Name="ContentContainer" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" Foreground="{TemplateBinding Foreground}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" RenderTransformOrigin="0.5,0.5"><ContentControl.RenderTransform><TransformGroup><TranslateTransform/></TransformGroup></ContentControl.RenderTransform></ContentControl></Border></ControlTemplate></Setter.Value></Setter></Style>

调用 PowerListBox

 <local:PowerListBox ItemsSource="{Binding TestModelList}" ><local:PowerListBox.ItemTemplate><DataTemplate><Grid><Grid.ColumnDefinitions><ColumnDefinition Width="150"/><ColumnDefinition/></Grid.ColumnDefinitions><TextBlock Text="{Binding Name}" VerticalAlignment="Center" FontSize="20"/><Border Width="100" Height="120" Background="#FF4949D3" Grid.Column="1" HorizontalAlignment="Left"><TextBlock Text="{Binding Id}" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="40" Foreground="Black"/></Border></Grid></DataTemplate></local:PowerListBox.ItemTemplate>
</local:PowerListBox>

效果图  

 

 由于gif录制帧数的原因,效果图不是很流畅,但实际运行情况动画效果是非常流畅的

转载于:https://www.cnblogs.com/ShenNan/p/4993374.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/397189.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CnosDB如何确保多步操作的最终一致性?

背景 在时序数据库中&#xff0c;资源的操作是一个复杂且关键的任务。这些操作通常涉及到多个步骤&#xff0c;每个步骤都可能会失败&#xff0c;导致资源处于不一致的状态。例如&#xff0c;一个用户可能想要在CnosDB集群中删除一个租户&#xff0c;这个操作可能需要删除租户…

颈椎前路caspar撑开器_“骨质增生”导致的颈椎病怎么破?

来源&#xff1a;《脊柱外科微创手术精要》作者&#xff1a;中日友好医院 邹海波此文是区别于颈椎间盘软性突出诊治一文&#xff0c;主要针对“骨质增生”导致的颈椎病(Spondylosis)进行介绍。传统的颈椎前路手术主要为颈椎病而设计。一度认为对颈椎病采用前路手术的主要好处在…

Struts2整合Freemarker生成静态页面

2019独角兽企业重金招聘Python工程师标准>>> 这是生成静态页面的预览&#xff1a; 其对应的模板文件&#xff1a; <table style"text-align:center;FONT-SIZE: 11pt; WIDTH: 600px; FONT-FAMILY: 宋体; BORDER-COLLAPSE: collapse" borderColor#3399ff…

快速幂、矩阵快速幂、快速乘法

快速幂 快速幂是我们经常用到的一种算法&#xff0c;快速幂顾名思义就是快速的幂运算。我们在很多题目中都会遇到幂运算&#xff0c;但是在指数很大的时候&#xff0c;我们如果用for或者是pow就会超时&#xff0c;这时候就用到了快速幂。 快速幂的原理就是&#xff0c;当求b^p的…

vue 前端显示图片加token_手摸手,带你用vue撸后台 系列二(登录权限篇)

完整项目地址&#xff1a;vue-element-adminhttps://github.com/PanJiaChen/vue-element-admin前言拖更有点严重&#xff0c;过了半个月才写了第二篇教程。无奈自己是一个业务猿&#xff0c;每天被我司的产品虐的死去活来&#xff0c;之前又病了一下休息了几天&#xff0c;大家…

注释工具_苹果已购丨Notability丨功能强大而简单易用的笔记及PDF注释工具

点击上方“天泽黑科技”右上角“...”点选“设为星标”点击加星★ 贴近你心 ❤今天给大家购买效率类排行第3名的 Notability &#xff01;大家在桌面 App store 登陆我的账号&#xff0c;搜索下载即可&#xff01;荣获 iPad、iPhone 和 Mac 的 Apple「编」爱新 App 殊荣&#x…

第四章 大网高级   NSSA

STUB、完全stub、NSSA、完全nssa实验要求&#xff1a;1、配置IP地址2、配置OSPF多区域3、配置 stub 末梢区域4、配置完全stub末梢区域5、配置 nssa 非纯末梢区域6、配置完全nssa非纯末梢区域7、配置两种协议相互注入重分发8、实现全网互通一、配置OSPF多区域二、配置rip v2三、…

[AlwaysOn Availability Groups] 健康模型 Part 2 ——扩展

[AlwaysOn Availability Groups] 健康模型 Part 2 ——扩展 健康模型扩展 第一部分已经介绍了AlwayOn健康模型的概述。现在是创建一个自己的PBM策略&#xff0c;然后设置为制定的归类。创建这些策略&#xff0c;创建之后修改一下配置&#xff0c;dashboard就会自动评估这些策略…

665. Non-decreasing Array - LeetCode

Question 665. Non-decreasing Array Solution 题目大意&#xff1a; 思路&#xff1a;当前判断2的时候可以将当前元素2变为4&#xff0c;也可以将上一个元素4变为2&#xff0c;再判断两变化后是否满足要求。 Java实现&#xff1a; public boolean checkPossibility(int[] nums…

如何制作印章_如何用Photoshop制作个性印章/文字图片

带印章和文字的图片&#xff0c;不仅可以作为个人的标签&#xff0c;更能直接表达照片的意境&#xff0c;让片子与众不同。那么&#xff0c;怎样才能给照片加印章和文字呢&#xff1f;或许方法有很多&#xff0c;甚至有多款App也可以直接做效果。但想要做出精细的效果&#xff…

麒麟810处理器_麒麟810性能实测:对比骁龙845骁龙730,谁更强?

随着荣耀9X、Nova5i Pro一众新机发布&#xff0c;采用7nm工艺制程的全新麒麟810进入了我们的视野。以手机处理器性能划分产品定位向来是最为直接的方法&#xff0c;在搭载麒麟810的荣耀9X将价格下探到1399元后&#xff0c;这枚网友口中“拳打845&#xff0c;脚踢730”的中端神u…

打造全键盘操作的PDF阅读器

其实我只想要一个非常简单的PDF阅读器&#xff0c;不要很花哨的功能&#xff0c;只要能够&#xff1a; 速度够快&#xff0c;不要翻一页等半天&#xff1b;全键盘操作&#xff0c;不想在鼠标和键盘之间来回倒腾&#xff1b;可以改变背景色&#xff0c;深夜的白光好刺眼&#xf…

mysql 导出dmp文件_一文带你了解MySQL主从复制(Master-Slave)

1.复制概述Mysql内建的复制功能是构建大型&#xff0c;高性能应用程序的基础。将Mysql的数据分布到多个系统上去&#xff0c;这种分布的机制&#xff0c;是通过将Mysql的某一台主机的数据复制到其它主机(slaves)上&#xff0c;并重新执行一遍来实现的。复制过程中一个服务器充当…

iOS开发学无止境 - NSFileManager文件操作的十个小功能

&#xff08;配图的小故事还记得嘛&#xff09; NSFileManager是一个单列类&#xff0c;也是一个文件管理器。可以通过NSFileManager创建文件夹、创建文件、写文件、读文件内容等等基本功能。 下面将介绍NSFileManager文件操作的十个小功能。我们在Documents里面进行举例&#…

smokeping自动检测系统

如何的使用smokeping来监控idc机房的网络质量情况&#xff0c;从监控图上的延时与丢包能分辨出你机房的网络是否稳定&#xff0c;是否为多线&#xff0c;是否为BGP机房&#xff0c;到各城市的3个运行商网络各是什么情况&#xff0c;如果出现问题&#xff0c;如果有针对的解决。…

bg感_【0328】BG推文 | 5本我在逃生游戏里养娃娃+岁月缱绻已无你+关于我比女主苏这回事+消失的白月光又回来了等...

大家多多支持原文&#xff01;以下内容多为网络搜集&#xff0c;非商业用途。版权归原作者所有&#xff0c;侵联&#xff01;BG文《我在逃生游戏里养娃娃》作者&#xff1a;鹤舫闲人《岁月缱绻已无你》作者&#xff1a;酒爷《关于我比女主苏这回事》作者&#xff1a;欢何极《消…

微信开发者工具 wxmi修改模版颜色_十款高效好用的在线网页工具,提升你的办公效率...

大家好&#xff0c; 我是阿毛&#xff0c;今天给大家推荐高效办公的10个在线网页工具&#xff0c;可以不用下载安装很多app&#xff0c;也不用在电脑上装很多软件。在线制作精彩视频操作非常简单&#xff0c;选择模板&#xff0c;上传照片然后点击制作等待完成就可以了&#xf…

[转]使用Navicat for Oracle工具连接oracle的

使用Navicat for Oracle工具连接oracle的 这是一款oracle的客户端的图形化管理和开发工具&#xff0c;对于许多的数据库都有支持。之前用过 Navicat for sqlserver,感觉很好用&#xff0c;所以下载了Oracle版的用。上网查看了一下这个工具可以用于任何版本 8i 或以上的 Oracle …

微信小程序基于第三方插件微信同声传译,以及一些问题解决办法

使用之前首先得在微信微信小程序后台添加插件&#xff0c;获取插件的appid 名称 使用时在app.json文件添加插件配置 1 plugins: { 2 WechatSI: { 3 version: 0.1.0, 4 provider: wx069ba97219f66d99 5 } 6 } 其次就是在使用的页面进行调用 在index.js外…

TF卡里删掉文件后内存没变大_内存卡损坏怎么修复?数据恢复方法教程

内存卡损坏怎么修复&#xff1f;内存卡又叫SD卡&#xff0c;是一种很轻便小巧的便携存储装置&#xff0c;往往内置于各种便携媒体设备内部。内存卡本身具有坚固、抗冲击等外部特性和读写快、空间大等内部特性&#xff0c;但是内存卡因为每天都要读写大量数据很容易从内部发生损…