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

本人网上看过一个视频,是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

WPF自定义控件之列表滑动特效 PowerListBox相关推荐

  1. HTML+CSS+JS实现 ❤️H5图片列表滑动特效❤️

  2. WPF自定义控件——顶级控件

    作为一个WPF程序员,我最希望看到的是WPF的应用,或者更确切的说是绚丽的应用,虽然限于自身的实力还不能拿出成绩来,但看到别人的作品时,心里还是有很大的宽慰--WPF是可以做出更加动人地产品的,只要你 ...

  3. Android 编程下代码之(QQ消息列表滑动删除)

       这份代码写出来有些时候了,一直没共享,现在把它共享给大家.简单列一下代码中你可以学到的知识点: 自定义控件的实现方式: 事件的拦截分发消费机制: QQ会话列表滑动删除原理: 最后附上源码链接:Q ...

  4. WPF xaml中列表依赖属性的定义

    原文:WPF xaml中列表依赖属性的定义 列表内容属性 如上图,是一个列表标题排序控件,我们需要定义一个标题列表,从而让调用方可以自由的设置标题信息. 在自定义控件时,会遇到列表依赖属性,那么该如何 ...

  5. WPF自定义控件与样式(8)-ComboBox与自定义多选控件MultComboBox

    一.前言 申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐步发布的等,若有不明白的地方可以参考本系列前面的文章,文末附有部分文章链接. 本文主要内容: 下拉选 ...

  6. [WPF自定义控件库] 自定义控件的代码如何与ControlTemplate交互

    [WPF自定义控件库] 自定义控件的代码如何与ControlTemplate交互 原文:[WPF自定义控件库] 自定义控件的代码如何与ControlTemplate交互 1. 前言 WPF有一个灵活的 ...

  7. 像“今日头条”这样的模块滑动特效,大概咋写,求指点 ?---酷课堂iOS交流群问答整理(201805期)

    酷课堂iOS交流群 我们是一个什么样的组织: 酷课堂iOS交流群,聚集了一群热爱技术.有趣.有料,平均Q龄在10年以上的"老司机",他们遍布在全国/球各地,有知名企业iOS工程师. ...

  8. WPF自定义控件(四)の自定义控件

    原文:WPF自定义控件(四)の自定义控件 在实际工作中,WPF提供的控件并不能完全满足不同的设计需求.这时,需要我们设计自定义控件. 这里LZ总结一些自己的思路,特性如下: Coupling UITe ...

  9. WPF 自定义控件的坑(蠢的:自定义控件内容不显示)

    WPF 自定义控件的坑(蠢的:自定义控件内容不显示) 原文:WPF 自定义控件的坑(蠢的:自定义控件内容不显示) 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csd ...

最新文章

  1. 【PAT (Basic Level) 】1014 福尔摩斯的约会 (20 分)
  2. 【npm第4期】文件系统操作的跨平台兼容
  3. AndroidManifest.xml配置文件详解
  4. linux的技术点,给你的Linux系统上点stress
  5. Docker部署运行微服务
  6. 苹果雪豹操作系统正式版_Android 11 正式版发布!
  7. memcpy内存重叠的解决
  8. vs2010下libevent的使用
  9. 结构化CSS设计思维
  10. 数据库系统概论第五版(王珊) 课后习题答案
  11. android studio httpclient包导入,HttpClient不会导入Android Studio
  12. 依分布收敛的定义细节
  13. 使用Comparator.comparing根据类的属性对list进行排序
  14. 2021年中国人工智能行业全景图谱
  15. Tomb.finance每周更新(11.29-12.5)
  16. html+css轮播图
  17. 以 gensim 訓練中文詞向量
  18. SHP(shapefile)文件详细格式介绍
  19. Maven中央仓库地址大全,Maven中央仓库配置示例
  20. 解决python爬虫出现的521问题

热门文章

  1. 获取人口_「微科普」14亿人口数据是如何得到的?
  2. 金融工作用计算机吗,为什么计算机专业的人想转金融,而金融专业的想转计算机?...
  3. oracle 存储过程写文件,Oracle写本地文件
  4. 《MySQL——锁》
  5. spring的Bean属性
  6. 伪静态设置 html,Apache下伪静态html(URL Rewrite)的设置方法
  7. c语言mcisendstring函数,mciSendString用法
  8. 电磁波传播相位是否会变化_相位常数β与波数k是一回事吗?
  9. django xadmin出现的问题
  10. 二叉树题目 ----7 前序中序遍历构造二叉树