<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
WPF實現卷軸還是比較方便的,只要在控制元件外圍加上ScrollViewer即可,但美中不足的是:捲動的時候沒有動畫效果。在捲動的時候新增過渡動畫能給我們的軟體增色不少,例如Office 2013的捲動的時候支援動畫看起來就舒服多了。 之前倒是研究過如何實現這個平滑捲動,不過網上的方案大部分大多數如下:
通過VisualTree找到ScrollViewer
在ScrollChanged事件中新增動畫
這種方案效果並不好,以為我們的捲動很多時候都是一口氣捲動好幾格滾輪的,這個時候上一個動畫還沒有結束,下一個動畫就來了,反而還出現了卡頓的感覺,並且網上的一些演演算法大部分還都會導致偏移錯位。
趁著這兩天有點時間,就研究了一下ScorllViewer,從MSDN檔案中看到,它是支援兩種捲動方式的:
系統預設的捲動方案,控制元件本身啥都不用幹,完全由ScrollViewer來實現捲動。這種方式的好處是簡單,但也正由於簡單,控制元件本身完全感知不到ScorllViewer的存在,也就無法加以控制了。
將這種方式需要設定ScrollViewer的CanContentScroll為"True"才能生效,同時需要控制元件實現IScrollInfo介面。此時ScrollViewer只是將捲動事件通過IScrollInfo介面傳遞給控制元件,由控制元件本身自己去實現捲動。同時從IScrollInfo介面中讀取相關的屬性更新卷軸介面。
也就是說,邏輯捲動才是我們所需要的方案。由於它要求控制元件實現IScrollInfo介面,自行控制捲動。也就是說我們要實現自己的Panel,並且實現IScrollInfo介面。關於這個介面,MSDN上有一系列文章介紹過如何實現它:
這個介面實現也不算麻煩,我倒沒有細看這幾篇文章,自己照著最後的一個例子嘗試著弄了一陣子也弄出來了。實際上麻煩的地方不在於實現這個介面,而是實現Panel,我這裡為了簡單,直接繼承了WrapPanel類,程式碼如下:
class MyWrapPanel : WrapPanel, IScrollInfo { TranslateTransform _transForm; public MyWrapPanel() { _transForm = new TranslateTransform(); this.RenderTransform = _transForm; } #region Layout Size _screenSize; Size _totalSize; protected override Size MeasureOverride(Size availableSize) { _screenSize = availableSize; if (Orientation == Orientation.Horizontal) availableSize = new Size(availableSize.Width, double.PositiveInfinity); else availableSize = new Size(double.PositiveInfinity, availableSize.Height); _totalSize = base.MeasureOverride(availableSize); return _totalSize; } protected override Size ArrangeOverride(Size finalSize) { var size = base.ArrangeOverride(finalSize); if (ScrollOwner != null) { _transForm.Y = -VerticalOffset; _transForm.X = -HorizontalOffset; ScrollOwner.InvalidateScrollInfo(); } return _screenSize; } #endregion #region IScrollInfo public ScrollViewer ScrollOwner { get; set; } public bool CanHorizontallyScroll { get; set; } public bool CanVerticallyScroll { get; set; } public double ExtentHeight { get { return _totalSize.Height; } } public double ExtentWidth { get { return _totalSize.Width; } } public double HorizontalOffset { get; private set; } public double VerticalOffset { get; private set; } public double ViewportHeight { get { return _screenSize.Height; } } public double ViewportWidth { get { return _screenSize.Width; } } void appendOffset(double x, double y) { var offset = new Vector(HorizontalOffset + x, VerticalOffset + y); offset.Y = range(offset.Y, 0, _totalSize.Height - _screenSize.Height); offset.X = range(offset.X, 0, _totalSize.Width - _screenSize.Width); HorizontalOffset = offset.X; VerticalOffset = offset.Y; InvalidateArrange(); } double range(double value, double value1, double value2) { var min = Math.Min(value1, value2); var max = Math.Max(value1, value2); value = Math.Max(value, min); value = Math.Min(value, max); return value; } const double _lineOffset = 30; const double _wheelOffset = 90; public void LineDown() { appendOffset(0, _lineOffset); } public void LineUp() { appendOffset(0, -_lineOffset); } public void LineLeft() { appendOffset(-_lineOffset, 0); } public void LineRight() { appendOffset(_lineOffset, 0); } public Rect MakeVisible(Visual visual, Rect rectangle) { throw new NotSupportedException(); } public void MouseWheelDown() { appendOffset(0, _wheelOffset); } public void MouseWheelUp() { appendOffset(0, -_wheelOffset); } public void MouseWheelLeft() { appendOffset(0, _wheelOffset); } public void MouseWheelRight() { appendOffset(_wheelOffset, 0); } public void PageDown() { appendOffset(0, _screenSize.Height); } public void PageUp() { appendOffset(0, -_screenSize.Height); } public void PageLeft() { appendOffset(-_screenSize.Width, 0); } public void PageRight() { appendOffset(_screenSize.Width, 0); } public void SetVerticalOffset(double offset) { this.appendOffset(HorizontalOffset, offset - VerticalOffset); } public void SetHorizontalOffset(double offset) { this.appendOffset(offset - HorizontalOffset, VerticalOffset); } #endregion }
基本上從程式碼中也能看出IScrollInfo介面的互動流程,這裡就不多介紹了。
主介面程式碼如下:
<ItemsControl ItemsSource="{Binding}" > <ItemsControl.ItemTemplate> <DataTemplate> <Border BorderThickness="1" BorderBrush="Black" Margin="8" Width="150" Height="50"> <Rectangle Fill="{Binding}" /> </Border> </DataTemplate> </ItemsControl.ItemTemplate> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <local:MyWrapPanel /> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> <ItemsControl.Template> <ControlTemplate> <ScrollViewer CanContentScroll="True"> <ItemsPresenter /> </ScrollViewer> </ControlTemplate> </ItemsControl.Template> </ItemsControl>
需要注意的是,這兒需要設定<ScrollViewer CanContentScroll="True">,否則使用的不是邏輯捲動。
資料來源程式碼如下:
var brushes = from property in typeof(Brushes).GetProperties() let value = property.GetValue(null) select value; this.DataContext = brushes.Take(100).ToArray();
由於使用了IscrollInfo介面,所有的捲動操作是自己實現的,這裡我是通過設定Panel的RenderTransFrom的X,Y偏移來實現捲動操作的。執行後看上去上和WrapPanel沒有什麼區別,但是由於是自己控制的捲動,加上動畫效果也只是分分鐘的事情了,把上面程式碼的RenderTransFrom的X,Y硬切換改成動畫切換即可:
protected override Size ArrangeOverride(Size finalSize) { var size = base.ArrangeOverride(finalSize); if (ScrollOwner != null) { var yOffsetAnimation = new DoubleAnimation() { To = -VerticalOffset, Duration = TimeSpan.FromSeconds(0.3) }; _transForm.BeginAnimation(TranslateTransform.YProperty, yOffsetAnimation); var xOffsetAnimation = new DoubleAnimation() { To = -HorizontalOffset, Duration = TimeSpan.FromSeconds(0.3) }; _transForm.BeginAnimation(TranslateTransform.XProperty, xOffsetAnimation); ScrollOwner.InvalidateScrollInfo(); } return _screenSize; }
對於其它的Panel,如Grid,DockPanel等,基本上也可以按照這種方式實現,IScrollInfo介面處基本上可以保持不變,只需要重寫MeasureOverride和ArrangeOverride兩個函數即可。一個特殊的控制元件是StackPanel,由於它本身已經實現了IScrollInfo介面,也就是說它本身就有自身的自繪製捲動的方案,並且沒有提供介面在覆蓋自身的自繪製捲動,因此我們需要自己寫一個StackPanel,好在實現StackPanel並不難,由於篇幅有限,這裡我懶得繼續寫了,讀者朋友自己實現吧。至於那些非Panel的控制元件,實現就更簡單了,也留著讀者朋友自己實現吧。
到此這篇關於WPF實現平滑捲動的文章就介紹到這了。希望對大家的學習有所幫助,也希望大家多多支援it145.com。
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45