我正在尝试从文件到列表加载数据,并立即在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控件的方法吗?提前谢谢。
答案 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
决定显示哪些读取数据以及显示哪种格式。这会增强MyForm
和MyBackGroundWorker
的重复使用: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
}