最佳化效能:資料繫結

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。 這個反映作業序列從效能觀點來看可能非常耗時。

第二個解析物件參考的方法涉及實作 INotifyPropertyChanged 介面的 CLR 來源物件,以及一個是 CLR 屬性的來源屬性。 在此情況下,資料繫結引擎會直接對來源類型使用反映,並取得必要的屬性。 這仍不是最佳的方法,但它的工作集需求的成本會比第一種方法低。

第三個解析物件參考的方法涉及 DependencyObject 的來源物件,以及一個是 DependencyProperty 的來源屬性。 在此情況下,資料繫結引擎不必使用反映。 相反地,屬性引擎和資料繫結引擎會一起獨立解析屬性參考。 這是解析用於資料繫結之物件參考的最佳方法。

下表會使用這三種方法,比較資料繫結 Text 屬性 TextBlock 元素的速度。

繫結 TextBlock 的 Text 屬性 繫結時間 (毫秒) 轉譯時間 -- 包括繫結 (毫秒)
對 CLR 物件的屬性 115 314
對實作 INotifyPropertyChanged 之 CLR 物件的屬性 115 305
DependencyObjectDependencyProperty 90 263

繫結至大型 CLR 物件

當您將資料繫結至單一 CLR 物件而它具有數以千計的屬性時,會有顯著的效能影響。 您可以藉由將單一物件分割成多個較少屬性的 CLR 物件來降低這種影響。 下表顯示單一大型 CLR 物件與多個較小物件之資料繫結的繫結和轉譯時間。

資料繫結 1000 個 TextBlock 物件 繫結時間 (毫秒) 轉譯時間 -- 包括繫結 (毫秒)
對具有 1000 個屬性的 CLR 物件 950 1200
對 1000 個具有 1 個屬性的 CLR 物件 115 314

繫結至 ItemsSource

假設您有 CLR List<T> 物件,其保存您想要在 ListBox 中顯示的員工清單。 若要建立這兩個物件之間的對應,您可以將員工清單繫結至 ListBoxItemsSource 屬性。 不過,假設您有新的員工加入您的群組。 您可能會認為,若要將這個新人插入已繫結的 ListBox 值,您只要將這個人加入員工清單,這項變更便預期會被資料繫結引擎自動辨識。 這個假設證明是錯的。實際上,變更不會自動反映在 ListBox。 這是因為 List<T> CLR 物件不會自動引發集合變更事件。 若要取得 ListBox 以挑選變更,您必須重新建立員工清單,並將其重新附加至 ListBoxItemsSource 屬性。 雖然這個解決方案有效,但它造成了嚴重的效能影響。 每次您將 ListBoxItemsSource 重新指派給新物件時, ListBox 會先拋棄其先前的項目,並重新產生其整個清單。 如果您的 ListBox 對應到複雜 DataTemplate,對效能影響就會放大。

對此問題有一個非常有效率的解決方案,就是讓您的員工清單成為 ObservableCollection<T>ObservableCollection<T> 物件會觸發資料繫結引擎可接收的變更通知。 事件會新增或移除 ItemsControl 的項目,而不需要重新產生整個清單。

下表顯示新增一個項目時,更新 ListBox (關閉 UI 虛擬化) 所花的時間。 第一列中的數值代表 CLR List<T> 物件繫結至 ListBox 元素的 ItemsSource 時所經過的時間。 第二列中的數值代表 ObservableCollection<T> 物件繫結至 ListBox 元素的 ItemsSource 時所經過的時間。 請注意到使用 ObservableCollection<T> 資料繫結策略的顯著節省時間效果。

資料繫結 ItemsSource 1 個項目的更新時間 (毫秒)
對 CLR List<T> 物件。 16:56
ObservableCollection<T> 20

將 IList 繫結至 ItemsControl 而非 IEnumerable

如果您選擇將 IList<T>IEnumerable 繫結至 ItemsControl 物件,請選擇 IList<T> 物件。 繫結 IEnumerableItemsControl 會強制 WPF 建立包裝函式 IList<T> 物件,這表示您的效能會受到第二個物件不必要的額外負荷影響。

不要只為了資料繫結而將 CLR 物件轉換成 XML。

WPF 可讓您資料繫結至 XML 內容。不過,資料繫結至 XML 內容會比資料繫結至 CLR 物件來得慢。 如果唯一的目的是資料繫結,請不要將 CLR 物件轉換成 XML。

另請參閱