您好,登錄后才能下訂單哦!
本篇內容介紹了“在WPF中怎么實現平滑滾動”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
WPF實現滾動條還是比較方便的,只要在控件外圍加上ScrollViewer即可,但美中不足的是:滾動的時候沒有動畫效果。在滾動的時候添加過渡動畫能給我們的軟件增色不少,例如Office 2013的滾動的時候支持動畫看起來就舒服多了。 之前倒是研究過如何實現這個平滑滾動,不過網上的方案大部分大多數如下:
通過VisualTree找到ScrollViewer
在ScrollChanged事件中添加動畫
系統默認的滾動方案,控件本身啥都不用干,完全由ScrollViewer來實現滾動。這種方式的好處是簡單,但也正由于簡單,控件本身完全感知不到ScorllViewer的存在,也就無法加以控制了。
將這種方式需要設置ScrollViewer的CanContentScroll為"True"才能生效,同時需要控件實現IScrollInfo接口。此時ScrollViewer只是將滾動事件通過IScrollInfo接口傳遞給控件,由控件本身自己去實現滾動。同時從IScrollInfo接口中讀取相關的屬性更新滾動條界面。
也就是說,邏輯滾動才是我們所需要的方案。由于它要求控件實現IScrollInfo接口,自行控制滾動。也就是說我們要實現自己的Panel,并且實現IScrollInfo接口。關于這個接口,MSDN上有一系列文章介紹過如何實現它:
IScrollInfo in Avalon part I
IScrollInfo in Avalon part II
IScrollInfo in Avalon part III
IScrollInfo in Avalon part IV
這個接口實現也不算麻煩,我倒沒有細看這幾篇文章,自己照著最后的一個例子嘗試著弄了一陣子也弄出來了。實際上麻煩的地方不在于實現這個接口,而是實現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; }
“在WPF中怎么實現平滑滾動”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。