在建立漂亮 UI 的同时,我们还需要关注应用程序的性能, WPF 尤其如此。下面从 MS 的文档中总结出了一些有用的性能优化点。在实际编写的过程中,可以参考。这个 Post 非完全原创,是根据一些文档总结出来的。
1 、建立逻辑树的时候,尽量考虑从父结点到子结点的顺序构建。因为当逻辑树的一个结点发生变化时(比如添加或删除),它的父结点和所有的子结点都会激发 Invalidation 。我们应该避免不必要的 Invalidation 。
2 、当我们在列表(比如 ListBox )显示了一个 CLR 对象列表(比如 List )时,如果想在修改 List 对象后, ListBox 也动态的反映这种变 化。此时,我们应该使用动态的 ObservableCollection 对象绑定。而不是直接的更新 ItemSource 。两者的区别在于直接更新 ItemSource 会使 WPF 抛弃 ListBox 已有的所有数据,然后全部重新从 List 加载。而使用 ObservableCollection 可以避 免这种先全部删除再重载的过程,效率更高。
3 、在使用数据绑定的过程中,如果绑定的数据源是一个 CLR 对象,属性也是一个 CLR 属性,那么在绑定的时候对象 CLR 对象所实现的机制不同,绑定的效率也不同。
A 、数据源是一个 CLR 对象,属性也是一个 CLR 属性。对象通过 TypeDescriptor/PropertyChanged 模式实现通知功能。此时绑定引擎用 TypeDescriptor 来反射源对象。效率最低。
B 、数据源是一个 CLR 对象,属性也是一个 CLR 属性。对象通过 INotifyPropertyChanged 实现通知功能。此时绑定引擎直接反射源对象。效率稍微提高。
C 、数据源是一个 DependencyObject ,而且属性是一个 DependencyProperty 。此时不需要反射,直接绑定。效率最高。
4 、访问 CLR 对象和 CLR 属性的效率会比访问 DependencyObject/DependencyProperty 高。注意这里指的是访问,不要和 前面的绑定混淆了。但是,把属性注册为 DependencyProperty 会有很多的优点:比如继承、数据绑定和 Style 。所以有时候我们可以在实现 DependencyProperty 的时候,利用缓存机制来加速访问速度:看下面的缓存例子:
另外,因为注册的 DependencyProperty 在默认是不可继承的,如果需要继承特性,也会降低 DependencyProperty 值刷新的效 率。注册 DependencyProperty 属性时,应该把 DefaultValue 传递给 Register 方法的参数来实现默认值的设置,而不是在构 造函数中设置。
5 、使用元素 TextFlow 和 TextBlock 时,如果不需要 TextFlow 的某些特性,就应该考虑使用 TextBlock ,因为它的效率更高。
6 、在 TextBlock 中显式的使用 Run 命令比不使用 Run 命名的代码要高。
7 、在 TextFlow 中使用 UIElement (比如 TextBlock )所需的代价要比使用 TextElement (比如 Run )的代价高。
8 、把 Label (标签)元素的 ContentProperty 和一个字符串( String )绑定的效率要比把字符串和 TextBlock 的 Text 属性 绑定的效率低。因为 Label 在更新字符串是会丢弃原来的字符串,全部重新显示内容。
9 、在 TextBlock 块使用 HyperLinks 时,把多个 HyperLinks 组合在一起效率会更高。看下面的两种写法,后一种效率高。
A 、
B 、
10 、任与上面 TextDecorations 有关,显示超链接的时候,尽量只在 IsMouseOver 为 True 的时候显示下划线,一直显示下划线的代码高很多。
11 、在自定义控件,尽量不要在控件的 ResourceDictionary 定义资源,而应该放在 Window 或者 Application 级。因为放在控件中会使每个实例都保留一份资源的拷贝。
12 、如果多个元素使用相同的 Brush 时,应该考虑在资源定义 Brush ,让他们共享一个 Brush 实例。
13 、如果需要修改元素的 Opacity 属性,最后修改一个 Brush 的属性,然后用这个 Brush 来填充元素。因为直接修改元素的 Opacity 会迫使系统创建一个临时的 Surface 。
14 、在系统中使用大型的 3D Surface 时,如果不需要 Surface 的 HitTest 功能,请关闭它。因为默认的 HitTest 会占用大量的 CPU 时间进行计算。 UIElement 有应该 IsHitTestVisible 属性可以用来关闭 HitTest 功能。
依然是浮云。。。