2015-07-21 67 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列表的结果是可以接受的 –

相关问题