本文内容
- 不删除对象的事件处理程序可能会使对象保持活动状态
- 依赖属性和对象
- Freezable 对象
- 用户界面虚拟化
了解 WPF 对象的内部行为有助于在功能和性能之间做出适当的取舍。
1、不删除对象的事件处理程序可能会使对象保持活动状态
对象传递给其事件的委托是对该对象的有效引用。 因此, 事件处理程序可以使对象保持活动状态的时间超过预期时间。 当对已注册为侦听对象事件的对象执行清理时,在释放对象前删除委托是非常必要的。 将不需要的对象保持为活动状态会增加应用程序的内存使用量。 在对象为逻辑树或可视化树的根时更是如此。
WPF 为事件引入了弱事件侦听器模式,在很难跟踪源和侦听器之间的对象生存期关系时,这种模式特别有用。 某些现有 WPF 事件使用此模式。 如果要实现具有自定义事件的对象,此模式可能会有用。
有若干工具(如 CLR 探查器和工作集查看器)可以提供有关指定进程的内存使用量的信息。 CLR 探查器包括分配配置文件的许多非常有用的视图,其中包括已分配类型的直方图、分配和调用关系图、显示各代垃圾回收及上述回收之后托管堆的生成状态的时间线,以及显示每个方法分配和程序集加载的调用树。
2、依赖属性和对象
通常,访问DependencyObject的依赖属性服务器托管网的速度不会慢于访问 CLR 属性的速度。 虽然设置属性值会产生一些小的性能开销,但是获取值与从 CLR 属性获取值的速度一样快。 抵销一些小的性能开销是因为依赖属性支持可靠的功能,如数据绑定、动画、继承和样式设置。
DependencyProperty 优化
在应用程序中定义依赖属性时请务必谨慎。 如果DependencyProperty仅影响呈现类型元数据选项,而不影响其他元数据选项(如AffectsMeasure),则应通替代其元数据来对其进行同样的标记。
如果并非所有属性更改都会影响测量、排列和呈现,则通过属性更改处理程序手动使测量、排列和呈现阶段无效的做法可能会更高效。 例如,你可能决定仅在值大于设置限制时才重新呈现背景。 在这种情况下,值超过设置限制时,属性更改处理程序仅会使呈现无效。
将 DependencyProperty 设置为可继承会影响性能
默认情况下,注册的依赖属性是不可继承的。 但可以显式地将所有属性设置为可继承。 尽管这是一个有用的功能,但是将属性转换为可继承会影响性能,因为会增加属性无效的时长。
谨慎使用 RegisterClassHandler
调用RegisterClassHandler会允许保存实例状态,但请注意,每个实例上都会调用处理程序,这将导致性能问题。 应用程序要求保存实例状态时,仅使用RegisterClassHandler。
在注册过程中为 DependencyProperty 设置默认值
创建要求默认值的DependencyProperty时,请将传递的默认元数据用作DependencyProperty的Register方法的参数来设置此值。 请使用此技术,而不要在构造函数中或在元素的每个实例上设置属性值。
使用 Register 设置 PropertyMetadata 值
创建DependencyProperty时,可以选择使用Register或OverrideMetadata方法设置PropertyMetadata。 尽管对象可能有一个静态构造函数来调用OverrideMetadata,但这不是最佳方案,并且会影响性能。 为了获得最佳性能,建议在调用过程中将PropertyMetadata设置为Register。
3、Freezable 对象
Freezable是一种特殊类型的对象,具有两种状态:解冻和冻结。 尽可能冻结对象会改进应用程序性能,并缩小其工作集。
每个Freezable都有一个Changed事件,只要发生更改就会引发该事件。 不过,更改通知会降低应用程序的性能。
考虑以下示例,其中每个Rectangle使用相同的Brush对象:
rectangle_1.Fill = myBrush;
rectangle_2.Fill = myBrush;
rectangle_3.Fill = myBrush;
// ...
rectangle_10.Fill = myBrush;
默认情况下,WPF 为SolidColorBrush对象的Changed事件提供事件处理程序,以便使Rectangle对象的Fill属性无效。 在这种情况下,每当SolidColorBrush不得不引发其Changed事件时,必须为每个Rectangle调用回叫函数,这些回叫函数调用的累积将导致性能严重下降。 此外,此时添加和删除处理程序将会严重影响性能,这是因为应用程序将不得不遍历整个列表才能执行此操作服务器托管网。 如果应用程序方案从未更改SolidColorBrush,你将为不必要地维护Changed事件处理程序付出代价。
冻结Freezable会改进其性能,因为不再需要因维护更改通知而消耗资源。 下表显示简单的SolidColorBrush在其IsFrozen属性设置为true
时的大小,并列出该属性未设置为该值时的情况以供对比。 这假设将一个画笔应用于十个Rectangle对象的Fill属性。
State | 大小 |
---|---|
已冻结的SolidColorBrush | 212 字节 |
非冻结的SolidColorBrush | 972 字节 |
以下代码示例演示了此概念:
Brush frozenBrush = new SolidColorBrush(Colors.Blue);
frozenBrush.Freeze();
Brush nonFrozenBrush = new SolidColorBrush(Colors.Blue);
for (int i = 0; i
解冻的可冻结对象的已更改处理程序可以使对象保持活动状态
对象传递给Freezable对象的Changed事件的委托是对该对象的有效引用。 因此,Changed事件处理程序可以使对象保持活动状态的时间超过预期时间。 当对已注册为侦听Freezable对象的Changed事件的对象执行清理时,在释放对象前删除委托是非常必要的。
WPF 还会在内部挂钩Changed事件。 例如,所有值为Freezable的依赖属性将自动侦听Changed事件。 采用Brush的Fill属性说明了此概念。
Brush myBrush = new SolidColorBrush(Colors.Red);
Rectangle myRectangle = new Rectangle();
myRectangle.Fill = myBrush;
将myBrush
赋值给myRectangle.Fill
时,指向回Rectangle对象的委托将添加到SolidColorBrush对象的Changed事件。 这意味着以下代码实际上不会使myRect
可以进行垃圾回收:
myRectangle = null;
在这种情况下,myBrush
仍会使myRectangle
保持活动状态,并在其引发Changed事件时对其进行调用。 请注意,将myBrush
赋值给新Rectangle的Fill属性时,仅将另一个事件处理程序添加到myBrush
。
清理这些类型的对象的建议方式是从Fill属性删除Brush,这将进而删除Changed事件处理程序。
myRectangle.Fill = null;
myRectangle = null;
4用户界面虚拟化
WPF 还提供StackPanel元素的一个变体,用于自动“虚拟化”数据绑定子级内容。 在此上下文中,“虚拟化”一词指的是以下技术:通过此技术,从较多的数据项中生成一个对象子集,具体取决于屏幕上哪些项可见。 如果在指定时刻只有少量 UI 元素位于屏幕上,则此时生成大量 UI 元素需要占用大量内存和处理器。VirtualizingStackPanel(通过VirtualizingPanel提供的功能)计算可见项,并与来自ItemsControl(如ListBox或ListView)的ItemContainerGenerator配合使用,以便仅为可见项创建元素。
作为性能优化的结果,将仅生成这些项的视觉对象,或如果它们在屏幕上是可见的,则保持活动状态。 这些视觉对象不再位于控件的可视区域时,则可能已被删除。 请勿将此与数据虚拟化发生混淆,数据虚拟化中的数据对象不会全部出现在本地集合中 – 而是根据需要进行流式处理。
下表显示了将 5000 个TextBlock元素添加到StackPanel和VirtualizingStackPanel并使其呈现所需的运行时间。 在此情形中,测量值是指从将文本字符串附加到ItemsControl对象的ItemsSource属性至面板元素显示此文本字符串之间的时间。
主机面板 | 呈现时间 (ms) |
---|---|
StackPanel | 3210 |
VirtualizingStackPanel | 46 |
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
相关推荐: 10 天的开发量,老板让我 1 天完成,怎么办?
大家好,我是树哥! 昨天,我在文章《业务开发做到零 bug 有多难?》和大家聊了下影响零 bug 的一些因素。其中,我提到了开发时被压缩工时,应该怎么做。今天,我们就来聊聊这个话题。 只要工作过几年的小伙伴,必然会遇到过背压工时的情况。面对这种情况,不同的工作…