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.pswp.cn/news/397189.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/397189.shtml
英文地址,請注明出處:http://en.pswp.cn/news/397189.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

去除inline-block元素間間距

根本原因&#xff1a;inline-block元素之間之所以有空白間距是因為空格有字體大小原因。 第一種&#xff1a; 把代碼之間的換行空白都去掉。 例如&#xff1a; <div>第一個inline-block元素</div><div>第二個inline-block元素</div> 第二種&#xff1a…

python - 定時清理ES 索引

只保留三天 #!/usr/bin/env python3 # -*- coding:utf-8 -*- import os import datetime# 時間轉化為字符串now_time datetime.datetime.now().strptime(datetime.datetime.now().strftime("%Y.%m.%d"),"%Y.%m.%d") os.system("curl -XGET http://12…

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…

使用flot.js 發現x軸y軸無法顯示軸名稱

添加此插件解決問題 flot-axislabels https://github.com/markrcote/flot-axislabels 轉載于:https://www.cnblogs.com/feehuang/p/4993920.html

快速冪、矩陣快速冪、快速乘法

快速冪 快速冪是我們經常用到的一種算法&#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…

python篩選含變量的特定行_Python SQL從特定變量字段中選擇行

我想從基于隨機變量的行中獲取一個特定的值。下面是一個示例表&#xff0c;PID列是一個“自動遞增主鍵整數”&#xff0c;其他兩列是文本示例表PID NAME PHONE--- ---- -----1 bill 999-99992 joe 888-8888我想把一個隨機變量扔到桌子上randomVariable raw_input(Enter someth…

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;如果有針對的解決。…

ElasticSearch聚合分析

聚合用于分析查詢結果集的統計指標&#xff0c;我們以觀看日志分析為例&#xff0c;介紹各種常用的ElasticSearch聚合操作。 目錄&#xff1a; 查詢用戶觀看視頻數和觀看時長聚合分頁器查詢視頻uv 單個視頻uv批量查詢視頻uvHaving查詢 根據 count 進行過濾根據其它指標進行過濾…