优化性能:数据绑定

Windows Presentation Foundation (WPF) 数据绑定为应用程序提供一种简单且一致的方法来呈现和交互数据。 元素可以绑定到各种数据源中的数据,其形式为 CLR 对象和 XML。

本主题提供数据绑定性能建议。

如何解析数据绑定引用

在讨论数据绑定性能问题之前,有必要了解 Windows Presentation Foundation (WPF) 数据绑定引擎如何解析绑定的对象引用。

Windows Presentation Foundation (WPF) 数据绑定的源可以是任何 CLR 对象。 可绑定到 CLR 对象的属性、子属性或索引器。 绑定引用是通过使用 Microsoft .NET Framework 反射或 ICustomTypeDescriptor 解析的。 下面是用于解析绑定对象引用的三种方法。

第一种方法涉及使用反射。 在这种情况下,PropertyInfo 对象用于发现属性的属性,并提供对属性元数据的访问权限。 使用 ICustomTypeDescriptor 接口时,数据绑定引擎使用此接口访问属性值。 在对象没有静态属性集的情况下,ICustomTypeDescriptor 接口特别有用。

可以通过实现 INotifyPropertyChanged 接口或使用与 TypeDescriptor 关联的更改通知来提供属性更改通知。 但是,实现属性更改通知的首选策略是使用 INotifyPropertyChanged

如果源对象是 CLR 对象,并且源属性是 CLR 属性,则 Windows Presentation Foundation (WPF) 数据绑定引擎必须首先使用源对象的反射来获取 TypeDescriptor,然后查询 PropertyDescriptor。 从性能的角度来看,这种反射操作序列可能非常耗时。

解析对象引用的第二种方法涉及实现 CLR 接口的 INotifyPropertyChanged 源对象,以及一种作为 CLR 属性的源属性。 在这种情况下,数据绑定引擎直接在源类型上使用反射并获取所需的属性。 这仍然不是最佳方法,但它在工作集要求方面的成本低于第一种方法。

解析对象引用的第三种方法涉及源对象,该对象是 DependencyObject,源属性是 DependencyProperty。 在这种情况下,数据绑定引擎不需要使用反射。 属性引擎和数据绑定引擎共同独立地解析属性引用。 这是解析用于数据绑定的对象引用的最佳方法。

下表比较了通过这三种方法,对一千个Text元素的TextBlock属性进行数据绑定的速度。

绑定 TextBlock 的文本属性 绑定时间 (ms) 呈现时间 -- 包括绑定 (ms)
绑定到 CLR 对象的属性 115 314
绑定到实现 CLR 的 INotifyPropertyChanged 对象的属性 115 305
绑定到 DependencyPropertyDependencyObject 90 263

绑定到大型 CLR 对象

将数据绑定到具有数千个属性的单个 CLR 对象时,性能有显著影响。 通过将单个对象划分为多个属性较少的 CLR 对象,可以最大程度地减少这种影响。 下表显示将数据绑定到单个大型 CLR 对象和多个较小对象的绑定时间和呈现时间。

数据绑定 1000 个 TextBlock 对象 绑定时间 (ms) 呈现时间 -- 包括绑定 (ms)
指向具有 1000 个属性的 CLR 对象 950 1200
绑定到 1000 个具有 1 个属性的 CLR 对象 115 314

绑定到 ItemsSource

假设你有一个 CLRList<T> 对象,该对象包含要在 ListBox中显示的员工列表。 若要在这两个对象之间创建对应关系,请将员工列表绑定到 ItemsSourceListBox 属性。 但是,假设你有一名加入你的组的新员工。 你可能会想,若要将这名新员工插入到绑定的 ListBox 值中,仅需将这名新员工添加到员工列表即可,数据绑定引擎应该会自动识别此更改。 这种假设将证明是错误的;实际上,更改不会自动反映在 ListBox 中。 这是因为 CLRList<T> 对象不会自动引发集合更改事件。 若要使 ListBox 接受更改,你需重新创建员工列表并将它重新附加到 ItemsSourceListBox 属性。 虽然此解决方案有效,但会产生巨大的性能影响。 每次将 ItemsSourceListBox 重新分配给新对象时,ListBox 会首先丢弃其先前的项并重新生成整个列表。 如果 ListBox 映射到复杂的 DataTemplate,则性能影响会被放大。

针对此问题的一个非常高效的解决方案是使员工列表成为 ObservableCollection<T>ObservableCollection<T> 对象会引发一个更改通知,数据绑定引擎可以接收。 此事件从 ItemsControl 添加或删除项,无需重新生成整个列表。

下表显示了添加一个新项时更新 ListBox(其中 UI 虚拟化处于关闭状态)所需的时间。 第一行中的数字表示 CLRList<T> 对象绑定到 ListBox 元素的ItemsSource时经过的时间。 第二行的数字表示当 ObservableCollection<T> 绑定到 ListBox 元素的 ItemsSource时经过的时间。 请注意,使用 ObservableCollection<T> 数据绑定策略能显著节省时间。

数据绑定 ItemsSource 1 个项目的更新时间(ms)
绑定到 CLRList<T> 对象 1656
绑定到 ObservableCollection<T> 20

将 IList 绑定到 ItemsControl 而非 IEnumerable

如果选择将 IList<T>IEnumerable 绑定到 ItemsControl 对象,请选择 IList<T> 对象。 将 IEnumerable 绑定到 ItemsControl 会强制 WPF 创建一个包装器 IList<T> 对象,这意味着性能会因第二个对象不必要的开销而受到影响。

不要将 CLR 对象转换为仅用于数据绑定的 XML。

WPF 允许将数据绑定到 XML 内容;但是,数据绑定到 XML 内容的速度比将数据绑定到 CLR 对象的速度慢。 如果唯一的用途是数据绑定,请不要将 CLR 对象数据转换为 XML。

另请参阅