2015-07-21 66 views
1

在我的窗口應用程序中,有很多帶有網格的屏幕。
我已經使用DataTable作爲網格的DataSource,並且DataTable具有一些非常大的數據集(> 50,000),如果我們一次加載所有內容,而在加載UI時得到不響應,則需要很長時間才能在屏幕上加載數據所有數據都未加載,
因此,我已使用Background Worker在該網格中實施了增量加載。
下面是代碼:使用DataTable作爲數據源在網格中增量加載

// DoWork Event of the background Wroker. 
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
    { 
     try 
     { 
      while (bgstop) 
      {     
       e.Result = addNewRecord(); 
       if (Convert.ToBoolean(e.Result) == false) 
       { 
        e.Cancel = true;      
        bgstop = false; 
        killBGWorker(); 
        break; 
       } 
      } 
     } 
     catch (Exception ex) 
     { 
      MessageBox.Show(ex.Message); 
     } 
    } 


    // to add/merge the records in the DataTable 
    private bool addNewRecord() 
    { 
     int flag = 0; 
     try 
     { 
      Thread.Sleep(500); //optional 
      DataTable tableAdd = getTableData(); 
      if (tableAdd.Rows.Count > 0) 
      { 
       dtRecords.Merge(tableAdd); // dtRecords is the DataTable which attached to grid 
       flag++; 
      } 
      else 
       backgroundWorker1.WorkerSupportsCancellation = true; 
     } 
     catch (Exception ex) 
     { 
      MessageBox.Show(ex.Message); 
     } 
     if (flag > 0) 
      return true; 
     else 
      return false; 
    } 


    // To get the next slot of Records from the DataBase 
    private DataTable getTableData() 
    { 
     DataTable dt = new DataTable(); 
     start = nextRows * noOfRows; 
     stop = start + noOfRows; 
     dt = SQLHelper.getAllRecords(totalRows,noOfRows, start + 1, stop); 
     nextRows++; 
     return dt; 
    } 

    // kill the backgroudworker after the all data/records get loaded from database to grid/DataTable 
    private void killBGWorker() 
    { 
     backgroundWorker1.WorkerSupportsCancellation = true; 
     backgroundWorker1.CancelAsync(); 
    } 

上面代碼中得到的第一個定義的記錄(比如200)和數量後,在後臺工作啓動,並開始在槽獲取數據和合並與網格的DataSource,直到所有的數據(比如> 50,000條記錄)都會被加載到網格中。
但是仍然存在一些與UI交互的問題,用戶界面在數據庫的所有記錄都被加載到網格中之前,很多時間都沒有掛上2-3秒。

我經歷了this,但是在那個例子中使用了DataModel,但在我的情況下沒有DataModel,他們只是從DataBase中取出DataTable,現在我們不能移動到DataModel。

是否有任何其他方式通過良好的UI交互實現增量加載?

在當前情況下,有什麼方法可以實現IBindingList

+0

是否可以接受簡單的讓用戶界面保持響應,或者是它重要的是你逐漸檢索行?用戶是否允許與部分數據集進行交互? – DrewJordan

+0

爲了獲得良好的用戶體驗,我需要逐行檢索行。並且是允許用戶與部分數據集進行交互。 – BhushanK

+0

用戶是否同時查看您的所有網格? –

回答

1

您可以通過將DataGridView從BindingMode更改爲VirtualMode來實現此目的。

以下更改將盡可能多地重複使用您已有的內容,並且您將看到DataGridView逐漸加載。我不知道你一次獲得多少記錄,但是你可以保持這個數字低。

將屬性VirtualMode設置爲true。刪除屬性DataSource的任何值。像DataGrid中的列一樣將多個無界列添加到DataGridView中(如果需要,這可以自動完成)。

CellValueNeeded添加事件處理程序。

下面的代碼添加到該處理程序:

private void dataGridView1_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e) 
{ 
    e.Value = dtRecords.Rows[e.RowIndex][e.ColumnIndex]; 
} 

在你backgroundworker1設置該屬性WorkerReportsProgress爲True
的事件處理程序添加到您的BackgroundWorker爲ProgressChanged。隨着下面的代碼:

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) 
{ 
    this.dataGridView1.RowCount = (int) e.UserState; 
} 

在你的方法addNewRecord下面加上這一行:

dtRecords.Merge(tableAdd); // dtRecords is the DataTable which attached to grid 
// added to bring the number of records to the UI thread 
backgroundWorker1.ReportProgress(42, dtRecords.Rows.Count); 

而你的datagridview現在應該現在應該增量加載它的數據。訣竅確實是設置RowCount屬性。如果數據網格可以顯示記錄並且它的滾動條適應相同,則向數據網格發出信號。

+0

+1對於很好的解釋。但是如果我不使用DataGridView呢?我正在使用第三方(syncfusion,telrik等)網格來代替DGV。我想要這樣的解決方案,不依賴於UI控制。 – BhushanK

+1

我明白了。藉助VirtualMode,它變成了拉式模型,DGV決定何時詢問物品。如果您使用BindingSource,它將成爲推送模型,並且當集合更改時,控件需要再次遍歷完整列表以找出需要執行的操作。這不僅需要時間,還可能在UI線程上完成,這就解釋了你看到的滯後。如果在綁定源中將[RaiseListChangeEvents](https://msdn.microsoft.com/en-us/library/system.windows.forms.bindingsource.raiselistchangedevents(v = vs.110).aspx)設置爲false,您可以嘗試你稱'合併'幫助。 – rene

1

我的解決方案是使用BindingSource這樣的:

// To take data in silent 
    BackgroundWorker m_oWorker; 

    // To hold my data. tblDuToanPhanBo is my data type 
    List<tblDuToanPhanBo> lst2 = new List<tblDuToanPhanBo>(); 

    BindingSource bs = new BindingSource(); 

    // replace 50000 with your total data count 
    int totalData = 500000; 

    // No of rows to load a time by BackgroundWorker 
    int RowsToTake = 2000; 

    // No of rows loaded 
    int RowsTaken = 0; 

取數據的第一部分,讓BackgroundWorker做休息:

private void UserControl1_Load(object sender, EventArgs e) 
    { 
     m_oWorker = new BackgroundWorker(); 
     m_oWorker.DoWork += new DoWorkEventHandler(m_oWorker_DoWork); 
     m_oWorker.ProgressChanged += new ProgressChangedEventHandler (m_oWorker_ProgressChanged); 
     m_oWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler (m_oWorker_RunWorkerCompleted); 
     // QLQT is my DataContext 
     using (QLQT db = new QLQT()) 
     { 
      lst2.AddRange(db.tblDuToanPhanBos.Skip(RowsTaken).Take(RowsToTake).ToList()); 
     } 
     RowsTaken = lst2.Count; 
     bs.DataSource = lst2; 
     dataGridView1.DataSource = bs; 
     m_oWorker.RunWorkerAsync(); 
    } 

BackgroundWorker取數據的一個部分:

void m_oWorker_DoWork(object sender, DoWorkEventArgs e) 
    { 
     // Load data 
     using (QLQT db = new QLQT()) 
     { 
      lst2.AddRange(db.tblDuToanPhanBos.Skip(RowsTaken).Take(RowsToTake).ToList()); 
     } 
     // Update number of rows loaded 
     RowsTaken = lst2.Count; 
     if (((BackgroundWorker)sender).CancellationPending) 
     { 
      e.Cancel = true; 
      return; 
     } 
    } 

BackgroundWorker完成時,更新BindingSource,運行BackgroundWorker反對,直到所有加載的數據:

void m_oWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
    { 
     if (e.Cancelled) 
     { 
      MessageBox.Show("Loading Cancelled."); 
     } 
     else if (e.Error != null) 
     { 
      MessageBox.Show("Error while performing background operation."); 
     } 
     else 
     { 
      if (lst2.Count < totalData) 
      { 
       bs.ResetBindings(false); 
       m_oWorker.RunWorkerAsync(); 
      } 
      else 
      { 
       bs.ResetBindings(false); 
      } 
     } 
    } 

希望這有助於:)

+0

感謝您的漂亮的代碼,但我已經提到,我沒有DataModel爲我的應用程序,我使用DataTable作爲DataSource。第二件事是我也想從'RunWorkerCompleted'再次調用'DoWork',但是在經過[this]之後(http://programmers.stackexchange.com/questions/254728/backgroundworker-starting-again-in-completed-事件)鏈接,我改變了主意。 – BhushanK

+1

在完成「DoWork」而不是重複使用後創建新的「BackgroundWorker」可以嗎? '''BindingSource'''DataSource''可以設置爲'DataTable'而不是'List',我在這裏使用'BindingSource'的原因是它不需要我重置'DataGrid' /'DataGridView'''DataSource'時間。我不擅長使用'DataTable',所以我沒有太多用處(報告除外)。希望你很快找到另一個解決方案:) Ps:我沒有用'DataTable'測試,但我認爲它的工作。也許當我有時間。具有大約100,000條記錄的List列表的結果是可以接受的 –

相關問題