winforms BackgroundWorker填充DataGridView

z4bn682m  于 2023-04-07  发布在  其他
关注(0)|答案(3)|浏览(136)

编辑:使用此解决:http://reedcopsey.com/2011/11/28/launching-a-wpf-window-in-a-separate-thread-part-1/
在我的项目(.net/windows窗体)中,我正在用一个大的DataTable填充一个DataGridView。填充可能需要20秒,所以我想要一个动画加载窗口。如果线程忙碌,这个动画就会冻结,所以我必须使用一个新的线程来填充窗口或DataGridView
我尝试使用BackgroundWorker来显示窗体,但它将是一个形状正确的白色窗口。
我也试过使用BackgroundWorker来填充DataGridView,但是它会抛出一个错误,说DataGridView正在被另一个线程访问,而不是被创建它的线程访问。由于DataGridView是在designer类中创建的,我不能在新线程中创建它-而且这个解决方案听起来不是很优雅。
在填充DataGridView时,显示工作动画窗体的最佳方法是什么?
编辑:答案并没有解决我的问题,所以我试着把代码分解成可以在这里呈现的东西。我以前没有这样做,因为它看起来没有足够的相关性来处理一些1 k行代码。可能是在这里呈现的代码中缺少了一些东西或以前实验的一些残余。请忽略错误的函数命名,这是一个遗留的东西,我会修复,一旦我得到了它的工作。部分代码是古老的。
我已经使它运行没有错误,但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();
            }
        }
    }
}
kb5ga3dv

kb5ga3dv1#

第二种方法是正确的:使用BackgroundWorker来执行任何在主线程中执行时会冻结UI的工作。关于你得到的异常,那是因为你所做的调用不是线程安全的(因为控件是在调用其方法的线程上创建的)。请看一下this link to MSDN来了解如何在线程之间进行这种调用,使用backgroundWorker的事件驱动模型。
从链接:
有两种方法可以安全地从未创建Windows窗体控件的线程调用该控件。您可以使用System.Windows.Forms.Control.Invoke方法调用在主线程中创建的委托,该委托进而调用该控件。或者,您可以实现System.ComponentModel.BackgroundWorker,该方法使用事件驱动模型将后台线程中完成的工作与结果报告分开。
看一下第二个示例,它使用backgroundWorker演示了这种技术

编辑

从你的评论中,我明白了这里真实的的问题是大小。问题是拥有DataGridView控件的线程是显示它的线程,所以无论如何,如果你一次加载所有数据,它将冻结这个线程在屏幕上绘制所有数据的时间。幸运的是,你不是第一个遇到这个问题的线程,我认为这是XY problem的一个很好的例子,真实的的问题是你想加载一个巨大的数据集(X),答案是here但是你问的是如何在填充数据网格时显示加载图标而不出现UI冻结(Y),这是你尝试的解决方案。
这是从技术的Angular 来看,但从UI/UX的Angular 来看,我希望你考虑一下表单的实际用途。你真的需要每次访问此部分时加载所有数据吗?如果答案是否定的,也许你可以考虑实现一些分页(例如here)。即使对于超宽显示器,200列也意味着水平滚动。我真的不知道你需要一次在列表/表格视图上显示所有信息的用户情况。我认为实现一个Master-Detail类型的接口可能会更有用。

编辑2.0

我认为你可以尝试在另一个线程中创建一个全新的Form窗口,把它放在你的数据网格上并在那里绘制加载动画。同时在主窗口中,你可以绘制网格而不绘制它,使加载动画冻结。(另一个线程正在处理绘图)。您可能希望持有对该线程的引用并杀死它,或者最好尝试保存一个对From的引用并更优雅地关闭它。
我从来没有这样做过,但我想我记得从一些遗留应用程序做类似的事情,这是丑陋的。

4zcjmb1e

4zcjmb1e2#

CheckForIllegalCrossThreadCalls = false;

在“btnGetData_Click”事件的开始;

1zmg4dgp

1zmg4dgp3#

使用一个异步任务,在DataGridView填满之前显示弹出加载窗口。
这里有一个链接,指向微软为异步编程编写的一篇文章:https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/

相关问题