ListBox的滚动方式 分为像素滚动和列表项滚动html
经过ListBox的附加属性ScrollViewer.CanContentScroll来设置。所以ListBox的默认模板中,含有ScrollViewer,ScrollViewer下存放列表内容缓存
<ScrollViewer FocusVisualStyle="{x:Null}"> <ItemsPresenter SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}"/> </ScrollViewer>
而CanContentScroll,true支持逻辑单元(Item),false支持物理单元(像素)。源码以下:post
/// <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); } }
一、像素滚动(物理单元) ScrollViewer.CanContentScroll=false性能
经过查看源码,咱们能够得知CanContentScroll的默认值为false。因此列表ListBox/ListView/DataGrid默认像素滚动this
/// <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; } }
像素滚动的优势:平滑--由于按照像素滚动,肉眼分辨较低。url
像素滚动的缺点:耗性能-列表中每一个项,都要计算出宽高具体数值,且滚动时时计算。若是列表中数量过多,就至关卡了。spa
二、列表项滚动(逻辑单元) ScrollViewer.CanContentScroll="True"code
按照Item高宽为滚动单位。htm
列表项滚动时,列表只会滚动到一个完整的Item,不会有一个Item只显示一半的状况。blog
经过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 列表开启虚拟化的方式