使用DataTable作为DataSource在网格中增加加载

时间:2015-07-21 12:57:59

标签: c# winforms datagridview datatable lazy-loading

在我的窗口应用程序中有许多带网格的屏幕。
我已经使用DataTable作为网格的DataSource,而DataTable有一些非常大的数据集(> 50,000),如果我们在加载UI时一次加载所有内容,则需要花费大量时间在屏幕上加载数据在所有数据都没有加载之前没有响应,所以我使用Background Worker在该网格中实现了增量加载 这是代码:

// 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交互的一些问题,UI不会多次挂起2-3秒,直到DataBase中的所有记录都被加载到网格中。

我经历了this,但是在那个例子中使用了DataModel,但在我的情况下,没有DataModel它们只是从DataBase中获取DataTable,现在我们无法移动到DataModel。

是有没有其他方法可以通过良好的UI交互实现增量加载?
或者是否有任何方法可以在当前场景中实现IBindingList

2 个答案:

答案 0 :(得分:2)

您可以通过将DataGridView从BindingMode更改为VirtualMode来实现此目的。

以下更改将尽可能多地重用您已有的内容,您将看到DataGridView以递增方式加载。我不知道你一次获取多少记录,但你可以保持这个数字很低。

将属性VirtualMode设置为true。从属性DataSource中删除任何值。在DataGrid中添加尽可能多的Unbounded列,因为DataGrid中有列(如果需要,可以自动完成)。

CellValueNeeded添加一个事件处理程序。

将以下代码添加到该处理程序:

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

在你的backgroundworker1上将属性WorkerReportsProgress设置为True
使用以下代码向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属性。如果它可以显示记录并且它将其滚动条调整为相同,则向数据网格发出信号。

答案 1 :(得分: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);
            }
        }
    }

希望这有帮助:)