我在Windows窗体中的跨线程调用有什么问题?

时间:2010-06-15 14:43:52

标签: c# winforms invoke multithreading showdialog

我遇到了Windows窗体应用程序的问题。

表单必须显示在另一个帖子中。所以在表单类中,我有以下代码:

private delegate void DisplayDialogCallback();

public void DisplayDialog()
{
    if (this.InvokeRequired)
    {
        this.Invoke(new DisplayDialogCallback(DisplayDialog));
    }
    else
    {
        this.ShowDialog();
    }
}

现在,每次运行时,行InvalidOperationException都会this.ShowDialog();投放{/ 1}}:

“跨线程操作无效:控制'SampleForm'从其创建的线程以外的线程访问。”

这段代码有什么问题?这不是一种进行跨线程调用的有效方法吗? ShowDialog()是否有特别之处?

7 个答案:

答案 0 :(得分:8)

您可能在表单显示之前执行此代码 因此,InvokeRequired正在返回false

答案 1 :(得分:5)

我相信这里发生的事情是这个代码在Form显示之前运行。

在.Net中创建Form时,它不会立即获得特定线程的亲和力。只有在执行某些操作时,如显示它或抓住手柄才能获得亲和力。在此之前,InvokeRequired很难正常运行。

在这种特殊情况下,没有建立关联,也不存在父控件,因此InvokeRequired返回false,因为它无法确定原始线程。

解决此问题的方法是在UI线程上创建控件时为其建立关联。执行此操作的最佳方法是向控件询问其句柄属性。

var notUsed = control.Handle;

答案 2 :(得分:4)

试试这个:

private delegate void DisplayDialogCallback();

public void DisplayDialog()
{
    if (this.InvokeRequired)
    {
        this.Invoke(new DisplayDialogCallback(DisplayDialog));
    }
    else
    {
        if (this.Handle != (IntPtr)0) // you can also use: this.IsHandleCreated
        {
            this.ShowDialog();

            if (this.CanFocus)
            {
                this.Focus();
            }
        }
        else
        {
            // Handle the error
        }
    }
}

请注意InvokeRequired返回

  

如果控件的Handle是,则为true   在与...不同的线程上创建   调用线程(表示你   必须通过调用控件   一个调用方法);否则,错误。

因此,如果尚未创建控件,则返回值为false

答案 3 :(得分:1)

您可能会在显示表单之前获取此代码,因此尚未创建窗口句柄。

您可以在代码之前添加此代码,所有代码都应该是好的:

if (! this.IsHandleCreated)
   this.CreateHandle();

编辑:您的代码还有另一个问题。显示表单后,您无法再次调用ShowDialog()。您将收到无效的操作异常。您可能希望像其他人提议的那样修改此方法。

你最好直接从调用类调用ShowDialog(),并为BringToFront()或类似的东西提供另一种方法......

答案 4 :(得分:0)

您可以尝试针对不同的控件进行测试。

例如,您可以访问Application.Forms集合

public Control GetControlToInvokeAgainst()
{
    if(Application.Forms.Count > 0)
    {
        return Application.Forms[0];
    }
    return null;
}

然后在您的DisplayDialog()方法中,在尝试执行invokerequired调用之前,调用GetControlToInvokeAgainst()并测试null。

答案 5 :(得分:0)

很可能尚未创建控件的句柄,在这种情况下Control.InvokeRequired会返回false

检查Control.IsHandleCreated属性以查看是否属于这种情况。

答案 6 :(得分:0)

我也认为SLaks是正确的。来自msdn(http://msdn.microsoft.com/en-us/library/system.windows.forms.control.invokerequired.aspx):

  

如果找不到合适的句柄,InvokeRequired方法返回false。

如果在您的情况下可行,我会尝试将创建和显示控件组合在一个方法中,即:

public DisplayDialog static Show()
{
  var result = new DisplayDialog; //possibly cache instance of the dialog if needed, but this could be tricky
  result.ShowDialog(); 
  return result;
}

你可以从另一个线程调用Show