9

我在使用 Windows 窗体应用程序时遇到问题。

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

private delegate void DisplayDialogCallback();

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

现在,每次我运行它时,InvalidOperationException都会抛出一个在线this.ShowDialog();

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

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

4

7 回答 7

8

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

于 2010-06-15T14:49:38.153 回答
5

我相信这里发生的事情是这段代码在Form显示之前就已经运行了。

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

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

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

var notUsed = control.Handle;
于 2010-06-15T14:51:54.357 回答
5

试试这个:

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!

于 2010-06-15T15:02:40.013 回答
1

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

您可以在代码之前添加此代码,一切都应该很好:

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

编辑:您的代码还有另一个问题。一旦显示表单,就不能再次调用 ShowDialog()。你会得到一个无效的操作异常。您可能希望按照其他人的建议修改此方法。

您可能会更好地直接从调用类调用 ShowDialog() 并为 BringToFront() 或类似的东西提供另一种方法......

于 2010-06-15T15:14:22.343 回答
0

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

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

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

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

于 2010-06-15T14:53:58.273 回答
0

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

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

于 2010-06-15T14:54:04.670 回答
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

于 2010-06-15T14:56:52.307 回答