2010-09-16 32 views
0

我們在綁定到對象集合的窗口上有數據網格。我們在這個窗口上表現非常糟糕;加載需要長達20秒,然後每次滾動數據網格時需要5-7秒。迄今共有12個項目。當我們調查時,看起來放緩的主要原因是財產吸收者;我們的一些獲得者被稱爲20,000次以上(每個對象1666.667次)!儀器顯示我們的吸氣劑不是特別是慢;最慢的一次花費了0.002秒。不幸的是,當你乘以0.0002 * 20k時,你很容易想到我們正在經歷的延遲。DataGrid過度數據綁定屬性獲取調用

我們坐下來創建了一個問題證明樣例項目。在這個項目中,我們創建了一個簡單的類,一個簡單的視圖模型和一個非常基本的窗口。我們添加了一些代碼來試圖觀察問題。雖然我們沒有看到任何接近之前我們看到的問題的嚴重程度,但這是一個更簡單的屏幕;對我們更令人不安的是,我們確實看到了我們認爲會「過度」使用財產獲得者的證據。在這個例子中,當你運行屏幕時,它們從1開始;當你向下滾動然後返回時,其中一些現在可以達到5或6;向下滾動,你會看到一些最底部的9個左右。

在這個概念證明的情況下,這不是問題;但我們的實際對象要複雜得多,而且我們無法承受40秒的延遲訪問房產20,000次!有人知道這裏發生了什麼嗎?我怎樣才能讓網格更積極地輪詢我的對象?我們正在使用WPF-Toolkit DataGrid和.NET 3.5版本。針對PoC樣本代碼如下:

***** WINDOW CODE *****

<Window x:Class="PocApp.Window1" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:WpfToolkit="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit" 
    Title="Window1" Height="200" Width="200"> 
    <Grid> 
     <WpfToolkit:DataGrid ItemsSource="{Binding Path=MyEntries}"> 

     </WpfToolkit:DataGrid> 
    </Grid> 
</Window> 

*****窗口代碼隱藏*****

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Windows; 
using System.Windows.Controls; 
using System.Windows.Data; 
using System.Windows.Documents; 
using System.Windows.Input; 
using System.Windows.Media; 
using System.Windows.Media.Imaging; 
using System.Windows.Navigation; 
using System.Windows.Shapes; 

namespace PocApp 
{ 
    /// <summary> 
    /// Interaction logic for Window1.xaml 
    /// </summary> 
    public partial class Window1 : Window 
    { 
     public Window1() 
     { 
      InitializeComponent(); 
      this.DataContext = new EntryViewModel(); 
     } 
    } 
} 

*****入門級*****

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

namespace PocApp 
{ 
    public class Entry 
    { 
     public Entry(string first, string last) 
     { 
      FirstName = first; 
      LastName = last; 
     } 

     public string FirstName 
     { 
      get 
      { 
       firstCall++; 
       System.Console.WriteLine("FirstName Call:" + firstCall); 
       return _firstname; 
      } 
      set 
      { 
       _firstname = value; 
      } 
     } 
     public int FirstNameCallCount 
     { 
      get 
      { 
       return firstCall; 
      } 
     } 
     public string LastName 
     { 
      get 
      { 
       lastCall++; 
       System.Console.WriteLine("LastName Call:" + lastCall); 
       return _lastname; 
      } 
      set 
      { 
       _lastname = value; 
      } 
     } 
     public int LastNameCallCount 
     { 
      get 
      { 
       return lastCall; 
      } 
     } 
     private string _firstname,_lastname; 
     private int firstCall,lastCall = 0; 
    } 
} 

*****輸入視圖模型類*****

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Windows.Data; 

namespace PocApp 
{ 
    public class EntryViewModel 
    { 
     public EntryViewModel() 
     { 
      List<Entry> myCoolEntries = new List<Entry>() 
      { 
       new Entry("A","A1"), 
       new Entry("B","B1"), 
       new Entry("C","C1"), 
       new Entry("D","D1"), 
       new Entry("E","E1"), 
       new Entry("F","F1"), 
       new Entry("G","G1"), 
       new Entry("H","H1"), 
       new Entry("I","I1"), 
       new Entry("J","J1"), 
       new Entry("K","K1"), 
       new Entry("L","L1"), 
       new Entry("M","M1"), 
       new Entry("N","N1"), 
       new Entry("O","O1"), 
       new Entry("P","P1"), 
       new Entry("Q","Q1"), 
      }; 
      MyEntries = (CollectionView)CollectionViewSource.GetDefaultView(myCoolEntries); 
     } 
     public CollectionView MyEntries 
     { 
      get; 
      private set; 
     } 
    } 
} 

回答

2

您在觀念證明中看到的數字實際上是正常的。它們是數據網格中行的虛擬化的結果;當一個項目滾動出視圖時,容器行將被重新用於顯示一個新輸入的項目,從而保持相對於創建和操縱的實際視覺控件的內存使用率較低。顯示另一個項目意味着重新查詢綁定的屬性。

通常我不會想象這會是一個問題;我已經使用了非常複雜的業務對象,並且在數據網格中顯示了大量數據,而沒有提到您遇到的那些問題。我建議查看應用於數據網格的任何樣式,因爲它們可能會干擾行的虛擬化;另外,需要2毫秒才能執行的屬性獲取者並不是我會打電話的快速:)

編輯:實際上,避免額外的對屬性getters的調用只能通過禁用虛擬化來實現,這將創建巨大性能問題 - 即使是數據網格中的「小」(數千)數量的行,也浪費了大量內存和abismal滾動性能。但是,這些僅僅是我可以說的沒有更多信息的唯一事情。如果可能,嘗試在獲取者被調用時遍歷代碼,並更精確地確定大多數調用的來源和時間。如果滾動時發生問題,那麼你的實體確實非常複雜,我以這種方式質疑在數據網格中顯示它們的價值。

另一個編輯:我更仔細地重讀這個問題,我注意到你說項?!我很抱歉,但現在我堅持認爲數據網格不應該被指責,除非你的對象有成千上萬的屬性,所有的屬性都綁定到數據網格中的列,我懷疑這是完全可能的。請檢查代碼和樣式的其餘部分,並嘗試確定我們可能建議您的任何潛在問題區域......不確定還有什麼要說的。還要檢查實體是否不會不必要地引發NotifyPropertyChanged事件,因爲這會導致綁定控件重新查詢屬性。

+0

我們發現了這個問題。在我們的實際實現中,綁定的對象是DataRow對象的包裝器。他們依靠DataTable.RowChanged事件爲它們處理NotifyPropertyChanged。事實證明,當這些行滾動進入和離開視圖時,RowChanged事件被激發多次。禁用該事件使我們的通話總數從200k +增加到50左右。感謝關於NotifyPropertyChanged的提示。 – GWLlosa 2010-09-17 14:31:44

相關問題