表单已关闭时调用时出错

时间:2010-10-25 10:34:37

标签: c# winforms invoke multithreading begininvoke

我正在尝试在从sql server查询的网格上显示一些信息。数据收集大约需要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线程上调用方法时它不能。

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

3 个答案:

答案 0 :(得分:4)

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

答案 1 :(得分:1)

尝试在InvokeRequired()

之前使用Invoke()

答案 2 :(得分:1)

Invoke在幕后使用特殊的WinForms SynchronizationContext,您可以在应用中的任意位置使用SynchronizationContext.Current进行访问。

在Reflector 中进行一些调整后的CORRECTION:实际上Invoke通过PostMessage直接进行编组,它是在后台使用SynchronizationContext的BackgroundWorker。如果它没有窗口句柄,则抛出将抛出。

基本上,您需要在启动线程之前将其存储在变量中,例如当你仍然在你的UI线程中,并在线程的代码中使用上下文的PostSend方法。这样做可以在没有窗口把手的情况下正确地整理东西。