BackgroundWorker 填充 DataGridView
BackgroundWorker to fill a DataGridView
编辑:使用此解决:http://reedcopsey.com/2011/11/28/launching-a-wpf-window-in-a-separate-thread-part-1/
在我的项目(.net/windows 表格)中,我正在用大 DataTable
填充 DataGridView
。填充最多可能需要 20 秒,所以我想要一个动画加载 window。如果线程繁忙,此动画将冻结,因此我将不得不为 window 或填充 DataGridView
.
使用新线程
我试过使用 BackgroundWorker
来显示表格,但在正确的形状下它会是空白的白色 window。
我也尝试过使用 BackgroundWorker
来填充 DataGridView
,但它会抛出一个错误,指出 DataGridView
正在被另一个线程访问它是为创建的。由于 DataGridView
是在设计器 class 中创建的,我不能只在新线程中创建它 - 而且该解决方案听起来不太优雅。
我在填写 DataGridView
时显示工作动画表单的最佳方式是什么?
编辑:答案没有解决我的问题,所以我尝试将代码分解为可以在此处显示的内容。我之前没有这样做,因为它看起来并没有足够的相关性来处理大约 1k 行代码。此处提供的代码中可能缺少某些内容或先前实验的一些残余。请忽略错误的函数命名,这是一个遗留问题,一旦我开始工作我会修复它。部分代码是古老的。
我已经 运行 没有错误地做到了,但是 frmLoading 仍然没有动画(如果我在工作线程不忙的时候让它保持活动状态的话)。
namespace a
{
public partial class frmMain : DockContent, IPlugin
{
//...
private delegate void SafeCallDelegate(DataTable dt);
private Thread thread1 = null;
private frmLoading frmLoading = new frmLoading();
public frmMain()
{
//...
}
//...
private void FillDataGrid(DataTable dt)
{
if(this.InvokeRequired)
{
var d = new SafeCallDelegate(FillDataGrid);
Invoke(d, new object[] { dt });
}
else
{
//...
DataGridFiller(dt);
}
}
private void DataGridFiller(DataTable dt)
{
BindingSource dataSource = new BindingSource(dt, null);
//...
dgvData.DataSource = dataSource;
//...
frmLoading.Hide();
}
private void btnGetData_Click(object sender, EventArgs e)
{
DataTable dt = [...];
// Wenn Daten vorhanden sind, dann anzeigen
if (dt != null)
{
//...
frmLoading.Show();
thread1 = new Thread(() => FillDataGrid(dt));
thread1.Start();
}
}
}
}
使用显示 pop-up 加载 window 直到 DataGridView 填满的异步任务。
这是 Microsoft 为异步编程制作的 link 到 write-up:https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/
第二种方法是正确的:使用 BackgroundWorker
来执行任何会冻结 UI 的工作(如果在主线程中完成)。关于您遇到的异常,那是因为您正在进行的调用不是 thread-safe (因为控件是在调用其方法的线程上创建的)。请查看 this link to MSDN 以了解如何使用 backgroundWorker 的 event-driven 模型在线程之间进行此类调用。
来自link:
There are two ways to safely call a Windows Forms control from a
thread that didn't create that control. You can use the
System.Windows.Forms.Control.Invoke
method to call a delegate created
in the main thread, which in turn calls the control. Or, you can
implement a System.ComponentModel.BackgroundWorker
, which uses an
event-driven model to separate work done in the background thread from
reporting on the results.
查看第二个示例,它使用 backgroundWorker 演示了此技术
编辑
从您的评论中我了解到这里真正的问题是大小。问题是拥有 DataGridView
控件的线程是显示它的线程,所以无论如何,如果您一次加载所有数据,它都会冻结该线程绘制所有数据所花费的时间屏幕上的数据。幸运的是,您不是第一个遇到此问题的人,微软这次为您解决了问题。我认为,这是 XY problem is, the real problem is that you want to load a huge dataset (X) and the answer on how to do that is here 的一个很好的例子,但是您却问如何在填充数据网格时显示加载图标而没有 UI 冻结 (Y),这是您尝试的解决方案。
这是从技术角度来看的,但是,从 UI/UX 的角度来看,我希望您考虑表格的实际用途。您真的需要每次访问此部分时都加载所有数据吗?如果答案是否定的,也许你可以考虑实现一些分页(例如 here). Also 200 colums means horizontal scroll even for an ultrawide monitor. I can't really figure out the user case for you to need all that information on a list/table view at once. I think that maybe implemeting a Master-Detail 类型的界面可能更有用。
编辑 2.0
我认为您可以尝试在另一个线程中创建一个全新的 Form
window,将其放在您的数据网格上并在那里绘制加载动画。同时,在主 window 中,您可以绘制网格而无需其绘制,从而使加载动画冻结(另一个线程负责绘制)。您可能想要保留对线程的引用并将其终止,或者最好尝试保留对 From 的引用并更优雅地关闭它。
我从来没有这样做过,但是,我想我记得从某个遗留应用程序中做过类似的事情,而且它很丑陋。
编辑:使用此解决:http://reedcopsey.com/2011/11/28/launching-a-wpf-window-in-a-separate-thread-part-1/
在我的项目(.net/windows 表格)中,我正在用大 DataTable
填充 DataGridView
。填充最多可能需要 20 秒,所以我想要一个动画加载 window。如果线程繁忙,此动画将冻结,因此我将不得不为 window 或填充 DataGridView
.
我试过使用 BackgroundWorker
来显示表格,但在正确的形状下它会是空白的白色 window。
我也尝试过使用 BackgroundWorker
来填充 DataGridView
,但它会抛出一个错误,指出 DataGridView
正在被另一个线程访问它是为创建的。由于 DataGridView
是在设计器 class 中创建的,我不能只在新线程中创建它 - 而且该解决方案听起来不太优雅。
我在填写 DataGridView
时显示工作动画表单的最佳方式是什么?
编辑:答案没有解决我的问题,所以我尝试将代码分解为可以在此处显示的内容。我之前没有这样做,因为它看起来并没有足够的相关性来处理大约 1k 行代码。此处提供的代码中可能缺少某些内容或先前实验的一些残余。请忽略错误的函数命名,这是一个遗留问题,一旦我开始工作我会修复它。部分代码是古老的。
我已经 运行 没有错误地做到了,但是 frmLoading 仍然没有动画(如果我在工作线程不忙的时候让它保持活动状态的话)。
namespace a
{
public partial class frmMain : DockContent, IPlugin
{
//...
private delegate void SafeCallDelegate(DataTable dt);
private Thread thread1 = null;
private frmLoading frmLoading = new frmLoading();
public frmMain()
{
//...
}
//...
private void FillDataGrid(DataTable dt)
{
if(this.InvokeRequired)
{
var d = new SafeCallDelegate(FillDataGrid);
Invoke(d, new object[] { dt });
}
else
{
//...
DataGridFiller(dt);
}
}
private void DataGridFiller(DataTable dt)
{
BindingSource dataSource = new BindingSource(dt, null);
//...
dgvData.DataSource = dataSource;
//...
frmLoading.Hide();
}
private void btnGetData_Click(object sender, EventArgs e)
{
DataTable dt = [...];
// Wenn Daten vorhanden sind, dann anzeigen
if (dt != null)
{
//...
frmLoading.Show();
thread1 = new Thread(() => FillDataGrid(dt));
thread1.Start();
}
}
}
}
使用显示 pop-up 加载 window 直到 DataGridView 填满的异步任务。
这是 Microsoft 为异步编程制作的 link 到 write-up:https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/
第二种方法是正确的:使用 BackgroundWorker
来执行任何会冻结 UI 的工作(如果在主线程中完成)。关于您遇到的异常,那是因为您正在进行的调用不是 thread-safe (因为控件是在调用其方法的线程上创建的)。请查看 this link to MSDN 以了解如何使用 backgroundWorker 的 event-driven 模型在线程之间进行此类调用。
来自link:
There are two ways to safely call a Windows Forms control from a thread that didn't create that control. You can use the
System.Windows.Forms.Control.Invoke
method to call a delegate created in the main thread, which in turn calls the control. Or, you can implement aSystem.ComponentModel.BackgroundWorker
, which uses an event-driven model to separate work done in the background thread from reporting on the results.
查看第二个示例,它使用 backgroundWorker 演示了此技术
编辑
从您的评论中我了解到这里真正的问题是大小。问题是拥有 DataGridView
控件的线程是显示它的线程,所以无论如何,如果您一次加载所有数据,它都会冻结该线程绘制所有数据所花费的时间屏幕上的数据。幸运的是,您不是第一个遇到此问题的人,微软这次为您解决了问题。我认为,这是 XY problem is, the real problem is that you want to load a huge dataset (X) and the answer on how to do that is here 的一个很好的例子,但是您却问如何在填充数据网格时显示加载图标而没有 UI 冻结 (Y),这是您尝试的解决方案。
这是从技术角度来看的,但是,从 UI/UX 的角度来看,我希望您考虑表格的实际用途。您真的需要每次访问此部分时都加载所有数据吗?如果答案是否定的,也许你可以考虑实现一些分页(例如 here). Also 200 colums means horizontal scroll even for an ultrawide monitor. I can't really figure out the user case for you to need all that information on a list/table view at once. I think that maybe implemeting a Master-Detail 类型的界面可能更有用。
编辑 2.0
我认为您可以尝试在另一个线程中创建一个全新的 Form
window,将其放在您的数据网格上并在那里绘制加载动画。同时,在主 window 中,您可以绘制网格而无需其绘制,从而使加载动画冻结(另一个线程负责绘制)。您可能想要保留对线程的引用并将其终止,或者最好尝试保留对 From 的引用并更优雅地关闭它。
我从来没有这样做过,但是,我想我记得从某个遗留应用程序中做过类似的事情,而且它很丑陋。