4

我正在尝试在从 sql 服务器查询的网格上显示一些信息。数据收集可能需要大约 10 秒,所以我不想锁定 UI 线程。

我目前有如下代码:

ThreadPool.QueueUserWorkItem(DataUpdateThread, new UpdateParams(year));  

private struct UpdateParams
{
    internal string year;

    internal UpdateParams(string year)
    {
        this.year = year;
    }
}

private void DataUpdateThread(object state)
{
    DataTable dTable = new DataTable();
    try
    {
        this.Invoke((MethodInvoker)delegate
        {
            //stop data editing on the grid
            //scrolling marquee for user
            marquee.Visible = true;
            marquee.Enabled = true;
            grdMain.Visible = false;
            grdMain.DataSource = null;
        });

            UpdateParams parameters = (UpdateParams)state;            
            dTable = GetData(parameters.year);
    }
    catch (Exception ex)
    {
        this.Invoke((MethodInvoker)delegate
        {
            //log error + end user message
        });
    }
    finally
    {
        this.Invoke((MethodInvoker)delegate
        {
            grdMain.DataSource = dTable;
            grdMainLevel1.RefreshData();
            marquee.Visible = false;
            marquee.Enabled = false;
            grdMain.Visible = true;
        });
   }
}

这在大多数情况下都有效,除非它所在的表单在更新完成之前关闭,否则它将因错误而崩溃:

在创建窗口句柄之前,不能对控件调用 Invoke 或 BeginInvoke。

我理解错误将是因为表单不再存在,所以当 finally 部分尝试在 UI 线程上调用该方法时它不能。

有没有更好的方法来做这件事?我想我可以处理调用错误,但它看起来很乱,我想我可能错过了一个更简单的方法。

4

3 回答 3

4

您可以检查表单是否已关闭,如果表单已关闭,则不要执行调用。if (this.IsHandleCreated)应该管用。然而,这仍然会产生问题,因为表单可以在检查和调用之间关闭BeginInvoke。唯一的“完全证明”解决方案是将整个调用包含在 try/catch 中。

于 2010-10-25T10:40:24.853 回答
1

尝试使用InvokeRequired()之前Invoke()

于 2010-10-25T10:55:23.387 回答
1

InvokeSynchronizationContext在后台使用特殊的 WinForms ,您可以SynchronizationContext.Current在应用程序的任何位置访问它。

在反射器中进行一些戳后更正:实际上 Invoke 通过 PostMessage 进行编组的直接方式,它是在幕后使用 SynchronizationContext 的 BackgroundWorker。如果没有窗口句柄,调用将抛出。

基本上,您需要在启动线程之前将其存储在一个变量中,例如,当您仍在您的 UI 线程中时,并在线程代码中使用PostSend上下文的方法。这样做将在没有窗口句柄的情况下正确编组这些东西。

于 2010-10-25T11:14:25.657 回答