Winforms Datagridview无法从委托刷新

时间:2017-08-31 21:07:41

标签: c# winforms datagridview backgroundworker

我正在尝试从文件到列表加载数据,并立即在Winforms的Datagridview上显示数据。为此,我使用Backgroundworker在另一个线程中进行了阅读。问题是,它只更新一次,我不能让它显示更多的数据。不仅如此,当点击时,它会尝试访问带有-1索引的元素,当然这不存在,导致崩溃。

通常,根据我的看法,只需将相同的数据再次添加到数据源dataGridView1.DataSource = samelist;即可,但在这种情况下不行。

BackgroundWorker的工作

private void bw_DoWork(object sender, DoWorkEventArgs e)
{
    BackgroundWorker worker = sender as BackgroundWorker;

    //lotsofCode...
    while (readData != null)
    {
        fooLists.Add(readData);
        //someCalculations...
        worker.ReportProgress();
    }
}

BackgroundWorker的progressChanged

private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    this.Invoke((MethodInvoker)delegate { UpdateGridView(); });
}

UpdateGridView方法

private void UpdateGridView()
{
     if (fooLists.GetListById(1).calculatedList != null)
          dataGridView1.DataSource = fooLists.GetListById(1).calculatedList;
}

后来我在堆栈上读了一些线程,其中一个建议使用BindingSource作为“中间人”,所以现在我在组件初始化dataGridView1.DataSource = MyBindingSource;tab1source.DataSource = fooLists.GetListById(1).calculatedList;而不是dataGridView1.DataSource。它确实有帮助,因为列表现在可以点击,但是列表中只有很少的记录。

dataGridView1.Refresh()dataGridView1.RefreshEdit()dataGridView1.Update()都没有帮助,但使列表加载稍微更加漂亮(可能是由于他们引入的延迟:))。

我试过制作一些“保护措施”(信号量,因此代理人不会在工作时再次调用;尝试捕获,但没有异常被抛出;重新编写之前数据清除......)但是“更好版本“像这个一样糟糕,它只会使代码变暗。

我错过了更新Datagridview控件的方法吗?提前谢谢。

1 个答案:

答案 0 :(得分:0)

虽然你没有写它,但我认为你添加到dataSource的项目被添加到一个没有实现接口IBindingList的集合的原因。您可能使用一个简单的列表来保存您的读取数据。

如果您的数据源implements this interface, then after adding an item to your collection an event is raised. The class that holds the数据源, whether it is a DataGridView or a BindingSource`收到有关列表中更改的通知,并相应地更新其内容。

您的解决方案是将您的元素存储在类System.ComponentModel.BindingList<T>的对象中。

假设您要展示的项目属于班级MyReadData

class MyForm : Form
{
    public MyForm()
    {
         InitializeComponents();
         this.myReadItems = new BindingList<MyReadData>();
         this.MyBindingSource.DataSource = this.myReadItems;

         // if not already done in InitializeComponents
         this.MyDataGridView.DataSource = this.MyBindingSource;
    }

    private readonly BindingList<MyReadData> myReadItems;

    // whenever needed, start the BackGroundWorker.

    private void OnButtonReadFile_Click(object send, EventArgs e)
    {
         // create and start the backgroundworker
         BackGroundWorkdr worker = ...
         MyBackGroundWorkerParams params = ...
         worker.RunWorkerAsync(params);
    }

    private void bw_DoWork(object sender, DoWorkEventArgs e)
    {
        // I am certain the sender is my BackGroundWorker:
        BackgroundWorker worker = (BackGroundWorker)sender;
        MyBackGroundWorkerParams params = (MyBackGroundWorkerParams)e.Argument;

        // do some work using the params

        while (readData != null)
        {
            // some data read.
            // dont't add the data to the list, just report the data that must been added to the list:
            // someCalculations...
            int percentProgress = ...
            MyReadData dataToAddToGrid = ...
            worker.ReportProgress(percentProgress, dataToAddToGrid);
         }

         private void bw_progressChanged(object sender, ProgressChangedEventArgs e)
         {
             // no need to call invoke, this is already the context of your forms thread
             Debug.Assert(!This.InvokeReguired);
             MyReadData dataToAdddToGrid = (MyReadData)e.UserState;
             this.myReadItems.Add(dataToAddToGrid);
         }
}

主要区别在于您不应让BackgroundWorker将数据添加到显示数据列表中。 BackGroundWorker的任务是读取数据并向感兴趣的人报告所读取的数据。

由于MyForm显示读取数据的任务,让MyForm决定显示哪些读取数据以及显示哪种格式。这会增强MyFormMyBackGroundWorker的重复使用:MyForm可能会显示以不同方式提取的内容,而MyBackGroundWorker可用于通知其他人{{1}通知读取数据。

此外,进度更改事件处理程序的显示上下文是&#39; MyForm`的上下文,因此不需要调用。

您还可以将IBindingList直接分配给DataGridView,因此无需使用BindingSource。保留BindingSource的唯一原因是,如果您想要访问MyForm项,或者您希望自由地使用除BindingList内容之外的其他项填充DataGridView。

最后:解决方案最重要的部分是将项目添加到IBindingList。

Current是一个功能有限的类。如果您想在System.Components.BindingList<T>中订购行,或只显示与某个谓词匹配的项目,或者将来自多个来源的项目合并到一个DataGridView中,请考虑使用Equin.ApplicationFramework.BindingListView

DataGridView

Presto,即所有:通过单击列标题进行免费排序或列。考虑检查他们的示例项目,看看过滤如何工作以及如何使用多个来源。

using Equin.ApplicationFramework;

public MyForm()
{
     InitializeComponents();
     this.myReadItems = new BindingListView<MyReadData>(this.components);
     this.MyBindingSource.DataSource = this.myReadItems;
     this.MyDataGridView.DataSource = this.MyBindingSource;
}

private readonly BindingListView<MyReadData> myReadItems;

 private void bw_progressChanged(object sender, ProgressChangedEventArgs e)
 {
     MyReadData dataToAdddToGrid = (MyReadData)e.UserState;
     this.myReadItems.Add(dataToAddToGrid);
     // finished updating the list, DataGridView can be updated:
     this.myReadItems.Refresh();

     // this Refresh function allows you to change several items in the list
     // without unnecessary intermediate updates of your BindingSource and DataGridView
 }