ListBox的滾動方式 分為像素滾動和列表項滾動
通過ListBox的附加屬性ScrollViewer.CanContentScroll來設置。因此ListBox的默認模板中,含有ScrollViewer,ScrollViewer下存放列表內容
<ScrollViewer FocusVisualStyle="{x:Null}"> <ItemsPresenter SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}"/> </ScrollViewer>
而CanContentScroll,true支持邏輯單元(Item),false支持物理單元(像素)。源碼如下:
/// <summary> /// 獲取或設置一個值,該值指示是否支持元素 <see cref="T:System.Windows.Controls.Primitives.IScrollInfo" /> 接口允許滾動。 /// </summary> /// <returns> /// <see langword="true" /> 如果 <see cref="T:System.Windows.Controls.ScrollViewer" /> 執行滾動操作使得在邏輯單元; 方面 <see langword="false" /> 如果 <see cref="T:System.Windows.Controls.ScrollViewer" /> 執行滾動操作使得在物理單元方面。 /// 默認值為 <see langword="false" />。 /// </returns> public bool CanContentScroll { get { return (bool) this.GetValue(ScrollViewer.CanContentScrollProperty); } set { this.SetValue(ScrollViewer.CanContentScrollProperty, value); } }
滾動
1、像素滾動(物理單元) ScrollViewer.CanContentScroll=false
通過查看源碼,我們可以得知CanContentScroll的默認值為false。所以列表ListBox/ListView/DataGrid默認像素滾動
/// <summary> /// 標識 <see cref="P:System.Windows.Controls.ScrollViewer.CanContentScroll" /> 依賴屬性。 /// </summary> /// <returns> /// <see cref="P:System.Windows.Controls.ScrollViewer.CanContentScroll" /> 依賴項屬性的標識符。 /// </returns> [CommonDependencyProperty] public static readonly DependencyProperty CanContentScrollProperty = DependencyProperty.RegisterAttached(nameof (CanContentScroll), typeof (bool), typeof (ScrollViewer), (PropertyMetadata) new FrameworkPropertyMetadata(BooleanBoxes.FalseBox));
[FriendAccessAllowed] internal static class BooleanBoxes { internal static object TrueBox = (object) true; internal static object FalseBox = (object) false; internal static object Box(bool value) { if (value) return BooleanBoxes.TrueBox; return BooleanBoxes.FalseBox; } }
像素滾動的優點:平滑--因為按照像素滾動,肉眼分辨較低。
像素滾動的缺點:耗性能-列表中每個項,都要計算出寬高具體數值,且滾動時時計算。如果列表中數量過多,就相當卡了。
2、列表項滾動(邏輯單元) ScrollViewer.CanContentScroll="True"
按照Item高寬為滾動單位。
列表項滾動時,列表只會滾動到一個完整的Item,不會有一個Item只顯示一半的情況。
虛擬化
通過VirtualizingPanel,設置列表ListBox/ListView/DataGrid是否開啟虛擬化
VirtualizingPanel其它屬性有:
VirtualizingPanel.ScrollUnit="Pixel"--虛擬化滾動單位(像素/單元)
VirtualizingPanel.IsVirtualizing="True" --是否虛擬
VirtualizingPanel.VirtualizationMode="Recycling"
VirtualizingPanel.CacheLengthUnit="Item" --緩存單位
VirtualizingPanel.CacheLength="20,20"-上下緩存數量
開啟虛擬化:為何需要設置ScrollViewer.CanContentScroll="True"?
開啟虛擬化后,VirtualizingPanel.ScrollUnit會替換原有的ScrollViewer.CanContentScroll滾動方式
虛擬化也有物理單元與邏輯單元之分,滾動單元設置會轉移到VirtualizingPanel.ScrollUnit
但是ScrollViewer.CanContentScroll="False"像素滾動,並不僅僅是滾動消耗性能。當數據很多時加載列表,即使開啟了虛化化,因計算太耗性能,界面一樣卡頓。
有一個解決辦法,設置ScrollViewer.CanContentScroll="True"后,在虛擬化設置中,可以設置虛擬化滾動單元VirtualizingPanel.ScrollUnit="Pixel",此即為虛擬化時的像素滾動。
另:虛擬化時的列表項滾動,VirtualizingPanel.ScrollUnit="Item"列表項
注:
VirtualizingPanel.ScrollUnit和ScrollViewer.CanContentScroll的設置滾動單元一樣。
設置虛擬單位為邏輯單元時,滾動時會自動滾動到一個完整的項,而不是滾動到項的部分。
因此當列表可見區域,Items數量或者高寬會變化時,列表滾動時會閃現。
列表正確開啟虛擬化方式,請看我的另一博客:WPF 列表開啟虛擬化的方式