2017-08-31 65 views
0

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

通常,从我所看到的情况来看,只需将相同的数据添加到数据源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;我n组件初始化和tab1source.DataSource = fooLists.GetListById(1).calculatedList;而不是dataGridView1.DataSource。它确实有所帮助,因为列表现在可以按照它的方式点击,但列表中仍然只有少量记录。

没有dataGridView1.Refresh(),dataGridView1.RefreshEdit()dataGridView1.Update()帮助,虽然使列表加载稍微有点fancier(可能是由于延迟他们介绍:))。我试图做一些“保护”(信号量,所以代理不会再次被调用,而工作;尝试捕获,虽然没有例外抛出;重写前的数据清除...),但“更好的版本“和这个一样糟糕,只会使代码变得暗淡。

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

+1

fooLists是什么类型? – WithMetta

+0

@WithMetta这是一个列表清单的自定义类。该方法返回标准列表。 – Midas

+0

看看https://social.msdn.microsoft.com/Forums/en-US/18a9762e-ac67-48a7-a372-55307fe344f3/how-do-you-refresh-data-bound-to-a-datagridview ?forum = winformsdatacontrols 您可能需要调用父控件的刷新方法。另外我不确定表达式“dataGridView1.DataSource = fooLists.GetListById(1).calculateList;”有效地执行不止一次。想想看。您将数据源设置为您已经设置的相同对象。如果是这样的话,它可能会在内部检查它并且不更新视图。 – WithMetta

回答

0

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

如果您的'DataSource implements this interface, then after adding an item to your collection an event is raised. The class that holds the DataSource , 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可以用于通知他人而不是MyForm以通知关于读取数据。

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

您也可以将IBindingList直接分配给DataGridView,因此不需要使用BindingSource。保留BindingSource的唯一原因是如果你想访问Current项目,或者如果你想自由填充你的DataGridView除了你的BindingList的内容以外的其他项目。

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

System.Components.BindingList<T>是一个功能有限的类。如果你想订购的行中的DataGridView,或只显示匹配某些谓词,或从多个来源结合项目到一个DataGridView的项目,可以考虑使用Equin.ApplicationFramework.BindingListView

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 
} 

转眼间,这是所有:免费排序或您的点击列标题列。考虑检查他们的示例项目,了解过滤的工作原理以及如何使用多个源。

// Show only Brummies 
this.myReadData.ApplyFilter(person => person.Address.City == "Birmingham"); 

// Remove the filter, show everyone again 
this.myReadData.RemoveFilter();