BackgroundWorker 填充 DataGridView

问题描述 投票:0回答:3

编辑:使用此解决:http://reedcopsey.com/2011/11/28/launching-a-wpf-window-in-a-separate-thread-part-1/

在我的项目(.net/windows 表单)中,我正在用一个大的

DataGridView
填充一个
DataTable
。填充最多可能需要 20 秒,所以我想要一个动画加载窗口。如果线程繁忙,此动画将冻结,因此我将不得不为窗口或填充
DataGridView
.

使用新线程

我试过使用

BackgroundWorker
来显示表格,但它会是一个正确形状的空白窗口。

我也尝试过使用

BackgroundWorker
来填充
DataGridView
,但它会抛出一个错误,指出
DataGridView
正在被一个与创建它的线程不同的线程访问。由于
DataGridView
是在设计器类中创建的,我不能只在新线程中创建它——而且这个解决方案听起来不太优雅。

我在填写

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();
            }
        }
    }
}
c# .net multithreading winforms backgroundworker
3个回答
2
投票

第二种方法是正确的:使用

BackgroundWorker
做任何在主线程中完成会冻结 UI 的工作。关于您遇到的异常,那是因为您正在进行的调用不是线程安全的(因为控件是在调用其方法的线程上创建的)。请查看 MSDN 的此链接 以了解如何使用 backgroundWorker 的事件驱动模型在线程之间进行此类调用。

来自链接:

有两种方法可以安全地调用 Windows 窗体控件 没有创建该控件的线程。您可以使用

System.Windows.Forms.Control.Invoke
调用创建的委托的方法 在主线程中,它又调用控件。或者,你可以 实现一个
System.ComponentModel.BackgroundWorker
,它使用一个 事件驱动模型将后台线程中完成的工作与 报告结果。

看看第二个例子,它使用 backgroundWorker 演示了这种技术

编辑

从您的评论中,我了解到这里真正的问题是尺寸。问题是拥有

DataGridView
控件的线程是显示它的线程,所以无论如何,如果您一次加载所有数据,它都会冻结该线程绘制所有数据所花费的时间屏幕上。幸运的是,您不是第一个遇到此问题的人,微软这次为您解决了问题。我认为,这是 XY 问题 的一个很好的例子,真正的问题是你想加载一个巨大的数据集 (X),而如何做到这一点的答案是 here,但你问的是如何在不冻结用户界面 (Y) 的情况下填充数据网格时显示加载图标,这是您尝试的解决方案。

这是从技术角度来看,但从 UI/UX 角度来看,我希望您考虑表单的实际使用。您真的需要每次访问此部分时都加载所有数据吗?如果答案是否定的,也许你可以考虑实现一些分页(例如here)。 200 列也意味着水平滚动,即使对于超宽显示器也是如此。我真的无法弄清楚您需要一次在列表/表格视图上显示所有信息的用户案例。我认为也许实现一个 Master-Detail 类型的接口可能更有用。

编辑 2.0

我认为你可以尝试在另一个线程中创建一个全新的

Form
窗口,将它放在你的数据网格上并在那里绘制加载动画。同时在主窗口中,您可以绘制网格而无需绘制,从而使加载动画冻结(另一个线程负责绘制)。您可能想要保留对线程的引用并终止它,或者最好尝试保留对 From 的引用并更优雅地关闭它。

我从来没有这样做过,但是,我想我记得从一些遗留应用程序中做类似的事情,而且它很丑陋。


0
投票

CheckForIllegalCrossThreadCalls = false;

在“btnGetData_Click”事件开始时;


-2
投票

使用显示弹出加载窗口的异步任务,直到 DataGridView 被填满。

这里是 Microsoft 为异步编程编写的文章的链接:https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/

© www.soinside.com 2019 - 2024. All rights reserved.