silverlight3:(ItemControl 的)UI Virtualization
是指包含一个项集合,如datagrid,listbox,tabControl,treeview.
那UI Virtualization又是什么呢?
最开始看到这个概念是在bea stollnitz的blog上,其实我也不知道该如何界定,但是一个控件如果支持ui virtualization(虚拟化),
那么它只会创建需要被显示在屏幕实际可见部分的的ui 元素。可能这还不是很明白,假设我有一个带滚动条的ListBox,绑定到ListBox上的数据有10000条,
而ListBox的高度只能够显示100条数据,由于在silverlight3中ListBox支持(UI Virtualization)虚拟化,所以实际上ListBox只会创建100条ListItem,
而不是实际绑定的10000条 ,如果将ListBox的UI虚拟化功能禁用掉,那么ListBox将会创建10000条ListItem,或者有100000条或更多,性能会怎样呢?因此,
某种程度上讲,UI虚拟化是可以解决大数据集合性能的,
当然要完全解决大数据集合绑定ItemControls的性能问题,仅仅依靠UI虚拟化是不够的,因为还需要运用另外的一种技术Data Virtualization(数据虚拟化).
1.ItemControl怎样实现UI 虚拟化?
wpf很早就已经支持UI虚拟化了,但是在silverlight中,直到3才开始,ItemControls才默认支持UI虚拟化,在silverlight3中所有的ItemControls都是
通过VirtualizingStackPanel来实现虚拟化.默认情况下, VirtualizingStackPanel将为每个可见项创建一个项容器(ItemContainer),并在不再
需要时(比如当项滚动到视图之外时)丢弃该容器。当 ItemsControl 包含多个项时,创建和丢弃项容器的过程可能会对性能产生负面影响。如果
VirtualizationMode 设置为 Recycling,VirtualizingStackPanel 将重用项容器,而不是每次创建新的项容器。 如果 将VirtualizingStackPanel
的VirtualizationMode 设置为Standerd,那么VirtualizingStackPanel将无法回收项容器,它将使用标准虚拟化模式,即为每个项创建和丢弃项容器。
详细情况见msdn 的介绍irtualizingStackPanel,VirtualizationMode .
还是以ListBox作为例子吧,假设我有一个带滚动条的ListBox,绑定到ListBox上
的数据有10000条,而ListBox的高度只能够显示100条数据ListBox将会使用VirtualizingStackPanel作为他们的项容器,在假如当前我们看到ListBox
现实的数据是第1-100条,如果ListBox是这样设置的<ListBox VirtualizingStackPanel.VirtualizationMode="Recycling" … />,那么当我拖动滚动条,
让其显示第101-200数据,那么此时ListBox不是回收之前创建的第1-100个项容器,再重新创建100个项容器来显示第101-200条数据,而是重复利用
已经创建的项容器,也就是刚开始为现实第1-100条数据而创建的项容器。但是如果我将<ListBox VirtualizingStackPanel.VirtualizationMode="Standerd" … />
这样设置,这时候VirtualizingStackPanel就如同普通的Panel,当需要滚动显示其他数据,那么就不会重复利用项容器了.在silverlight3中所有的ItemControl
的VirtualizingStackPanel.VirtualizationMode默认值都是Recycling.除非显式的指明VirtualizationMode为standerd,才会关闭UI虚拟化。
2.那么ItemControl实现UI虚拟化,会有什么问题吗?
既然我这么问,答案是绝对肯定的,直到周五我也才完全弄明白问题的原因就是由于UI虚拟化。我在siverlight forum看到也有人提出诸如此类的问题
Datagrid: Scrollbar size not changing correctly when data added to the grid,当然这个只是问题中的一种,在我的silverlight项目中,有2个场景出现问题:
A.当向一个带滚动条的ListBox,添加新数据后,有时在屏幕上会看不到刚添加的那条数据,而实际上数据已经成功加入,
情况类似Datagrid: Scrollbar size not changing correctly when data added to the grid,这个帖子下面一些朋友提到的办法,
比如在保存成功之后调用itemcontrol的UpdateLayout()方法.不过经过长时间的测试,
我发现一个规律就是只有当ListBox的中的数据超过ListBox高度的时候(需要拖动scrollBar来查看所有数据),这个现象才会出现。同里在删除ListBox中的数据也是,
数据库里没有了,但界面上依然存在。我曾用很极端的方式来解决问题,就是删除数据成功后,我找到被删除的对象,然后删除其对应的ListItem,而不是重新取数据绑定到ListBox。
B.这个问题则更让我头疼了,还是贴2张图,这是我项目中一个关于对象属性的画面,属性包括5种类型,文本,数字,日期,单选,多选,需要根据属性类型不同而显示不同的编辑方式。
图1上的文字和按钮出现重叠,原本button和checkBox都该是隐藏的,本来保存按钮只有当编辑过属性值,才会出现的,看到的图2是正常的画面,
当然图2的正常仅限于没有拖动滚动条,当拖动滚动条后图2中的内容,就是图1的效果了.至于图3由于其属性类型是简单的文本,日期,数字无论怎样拖动都不会出现问题。
虽然这个项目是由我一个人在做,但是我还是和其它同事有讨论过原因,都认为问题应该是处在多选项(ListBox)高度计算上,由于多选项的选项值不确定,
导致ListBox的高度计算上除了差错,从而导致在重绘UI元素是出现了偏差,而解决的办法是自己手动计算ListBox的高度,同事给出的结论几乎可以说这个问题无解
图1 图2
不过当我真正理解ListBox的UI虚拟化,似乎上面2个问题就迎刃而解了.当我尝试着将图1中显示最外层显示的Listbox显式VirtualizingStackPanel.
VirtualizationMode="Standerd",无论我如果拖动滚动条,图1的画面都不会出现。为什么出现这个问题?
我的解释是:
1. 对于问题A,就是当由于 不是每次让新添加的数据都显示在列表的最前端(业务需要),所以当滚动条出现后,新添加的那条数据不会出现在ListBox的可见范围内,
由于ui虚拟化就使得ListBox不会为该条数据创建用于显示的项容器,对于为什么觉得是有时能立即看到,我想大概是由于滚动条的位置有关,
不过禁用Listbox的虚拟化功能,问题也就随之解决了。
2. 对于问题B,当ListBox虚拟化开启当,如果所有属性均为简单的文本,数字,日期类型,
单选,其项容器的高度都是一个单位(一行),因此当拖动滚动条,无论属性类型,及内容如何改变,项容器高度都仍然是一个单位,自然不会出现重叠,
但是当属性类型为多选项的时候或都是多选项的时候,由于每个属性的多选项数目不一,导致项容器的高度不一致。如果拖动滚动条,重复利用项容器的时候,
无论当前的多选项选项数目多余或少于滚动之前的,都将出现项容器高度与当前多选项实际显示高度不一致的情况,即会出现重叠现象。所以解决的办法就是
禁用Listbox的虚拟化功能,让其每次滚动之后都回收之前的项容器,创建新的项容器,当然这样会损失写性能,所以还需要运用Data virtualization,这里就不多说了。
最后在这里请教一个问题,也是一个非常让我头疼的问题:在silverlight中上传大文件(当然是使用wcf service),在上传过程中,我将文件以256k为一块,
不停的调用service,直到文件传输完毕,当然这个过程使用到了事务,现在已经能正常使用,但是有一个问题就是如果我强制将ie窗口关闭(即强制取消上传)
那样整个wcf service就不能访问,除非重启服务,我猜想原因可能是由于事务造成的,由于强制取消,导致事务既没有完成,也没有回滚,处于一种挂起状态,
不过我奇怪的是为什么无法访问服务,更不知道该如何解决这一问题,希望有人能给点提示。在这里先谢过了!