最佳化效能:資料繫結
Windows Presentation Foundation (WPF) 資料繫結在資料的展示和互動上,提供應用程式簡單而一致的方式。 您可以 CLR 物件和 XML 的形式將不同資料來源的項目繫結至資料。
本主題提供資料繫結的效能建議。
這個主題包含下列章節。
- 資料繫結參考的解析方式
- 繫結至大型 CLR 物件
- 繫結至 ItemsSource
- 將 IList 繫結至 ItemsControl 而非 IEnumerable
- 請不要為了資料繫結將 CLR 物件轉換為 XML
- 相關主題
資料繫結參考的解析方式
在討論資料繫結的效能問題以前,應該花點時間了解 Windows Presentation Foundation (WPF) 資料繫結引擎是怎麼樣解析繫結的物件參考。
Windows Presentation Foundation (WPF) 資料繫結的來源可以是任何 CLR 物件。 您可以繫結至 CLR 物件的屬性、子屬性或索引子 (Indexer)。 使用 Microsoft .NET Framework 反映 (Reflection) 或 ICustomTypeDescriptor 都可以解析繫結參考。 以下是三種解析物件繫結參考的方法。
第一個方法使用到反映。 在這種情況下,會使用 PropertyInfo 物件來尋找屬性 (Property) 的屬性 (Attribute),並存取屬性 (Property) 的中繼資料 (Metadata)。 使用 ICustomTypeDescriptor 介面時,資料繫結引擎會用這個介面存取屬性 (Property) 的值。 在物件沒有靜態屬性集的情況下,ICustomTypeDescriptor 介面特別有用。
實作 INotifyPropertyChanged 介面或使用與 TypeDescriptor 相關聯的變更告知,都可以提供屬性 (Property) 變更告知。 但是,若要實作屬性 (Property) 變更告知,使用 INotifyPropertyChanged 是較好的策略。
如果來源物件是 CLR 物件,且來源屬性 (Property) 是 CLR 屬性,則 Windows Presentation Foundation (WPF) 資料繫結引擎必須先對來源物件使用反映以取得 TypeDescriptor,然後再查詢 PropertyDescriptor。 從效能的觀點來看,整個反映作業程序可能非常耗費時間。
解析物件參考的第二個方法牽涉到實作了 INotifyPropertyChanged 介面的 CLR 來源物件,以及屬於 CLR 屬性的來源屬性。 在這種情況中,資料繫結引擎直接對來源型別使用反映,並取得所需的屬性。 這依然不是最佳方法,但所花費的工作集需求比第一個方法要少。
解析物件參考的第三個方法牽涉到屬於 DependencyObject 的來源物件,以及屬於 DependencyProperty 的來源屬性。 在這種情況中,資料繫結引擎不必使用反映, 而是由屬性引擎和資料繫結引擎一起獨立解析屬性參考。 這是解析用於進行資料繫結之物件參考的最佳方法。
下表比較對有一千個 TextBlock 項目的 Text 屬性進行資料繫結時,三種方法的速度。
繫結 TextBlock 的 Text 屬性 |
繫結時間 (毫秒) |
轉譯時間 -- 包括繫結 (毫秒) |
---|---|---|
至 CLR 物件的屬性 |
115 |
314 |
至實作 INotifyPropertyChanged 之 CLR 物件的屬性 |
115 |
305 |
90 |
263 |
繫結至大型 CLR 物件
當您將資料繫結至有上千個屬性的單一 CLR 物件時,會對效能造成嚴重的影響。 您可以將這單一物件分成數個擁有較少屬性的 CLR 物件,就可以減少影響。 下表是將資料繫結至單一大型 CLR 物件和數個較小物件的繫結和轉譯時間。
1000 個 TextBlock 物件的資料繫結 |
繫結時間 (毫秒) |
轉譯時間 -- 包括繫結 (毫秒) |
---|---|---|
至含有 1000 個屬性的 CLR 物件 |
950 |
1200 |
至 1000 個含有單一屬性的 CLR 物件 |
115 |
314 |
繫結至 ItemsSource
請試想一個案例:您有一個 CLR List<T> 物件,其中包含您想在 ListBox 中顯示的員工清單。 為了要建立這兩個物件的對應關係,您將員工清單繫結至 ListBox 的 ItemsSource 屬性。 但是,假設有個新員工加入您的群組。 您可能認為要在您已繫結的 ListBox 值中插入這個新員工,只要將這個員工加入至您的員工清單中,資料繫結引擎就會自動辨認這個變更。 但這個想法是錯的;事實上,ListBox 中並不會自動反映這個變更。 這是因為 CLR List<T> 物件不會自動引發集合變更事件。 為了讓 ListBox 採用變更,您必須重新建立員工清單,再重新附加至 ListBox 的 ItemsSource 屬性。 雖然這個解決方法有效,但是會嚴重影響效能。 每次當您重新指派 ListBox 的 ItemsSource 給新物件,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> 物件 |
1656 |
20 |
將 IList 繫結至 ItemsControl 而非 IEnumerable
如果您可以選擇要將 IList<T> 還是 IEnumerable 繫結至 ItemsControl 物件,請選擇 IList<T> 物件。 將 IEnumerable 繫結至 ItemsControl 會強制 WPF 建立一個包裝函式 IList<T> 物件,這表示您的效能會受到第二個物件的負荷的不必要影響。
請不要為了資料繫結將 CLR 物件轉換為 XML
WPF 讓您可以將資料繫結至 XML 內容,但是將資料繫結至 XML 內容會比將資料繫結至 CLR 物件更慢。 如果資料繫結是唯一的目的,請不要將 CLR 物件資料轉換為 XML。