1

可能我没有正确理解这个主题。这里有个问题...

C# Windows 应用程序 (.NET 2.0)。MainForm 有一个“查询”按钮。当用户推送它时,应该会发生以下情况:

private void btnQuery_Click(object sender, EventArgs e)
{
    querier = new Querier();
    OutputForm outputForm = new OutputForm();
    querier.ProcessAll(outputForm.OutputReceived);
    outputForm.ShowDialog();
}

Querier是工人。它创建一个后台线程并运行它来做一些事情。这OutputForm是一个带有txtOutput多行文本框的简单表单,应该显示工作线程的输出。

为了允许工作线程发送其输出,querier.ProcessAll()方法接收一个回调处理程序。这是它的实现:

public void OutputReceived(string message)
{
    if (this.InvokeRequired)
        this.Invoke((MethodInvoker)delegate() { this.OutputReceived(message); });
    else if (!string.IsNullOrEmpty(message))
        txtOutput.AppendText(message + Environment.NewLine);
}

所以基本上工作线程运行并使用使用 的OutputReceived()方法发送输出Invoke(),因为工作线程不能直接访问 txtOutput 字段。

请注意,这outputForm.ShowDialog()称为 AFTER querier.ProcessAll()。那是因为ShowDialog()正在阻塞。

但问题就在这里。如果工作线程在实际显示对话框之前发送任何输出,我会收到有关跨线程操作的异常!this.InvokeRequired()当我调试它时,我在方法中看到由于某种原因OutputReceived()返回"false"!这就是为什么工作线程尝试txtOutput直接访问并崩溃的原因。

问题显然是关于线程和ShowDialog(). 如果我Thread.Sleep()在工作线程的开头添加,则会显示对话框,然后一切正常。

你能解释一下这种行为吗?

4

2 回答 2

2

最好的办法是确保长时间运行的任务在第一次显示另一个表单之前不会真正开始。Shown多亏了 中的事件,这并不难Form

private void btnQuery_Click(object sender, EventArgs e)
{
    querier = new Querier();
    OutputForm outputForm = new OutputForm();
    outputForm.Shown += delegate { querier.ProcessAll(outputForm.OutputReceived); };
    outputForm.ShowDialog();
}
于 2012-11-06T16:02:28.040 回答
0

对不起,我找到了答案!

有一种特殊情况何时InvokeRequired()返回"false"。这是尚未创建控件句柄的时候。在这种情况下,禁止打电话Invoke()- 所以InvokeRequired()试图保护你,有点。

现在我CreateHandle()在 的 CTOR 中调用方法OutputForm。通过这种方式,甚至在显示对话框之前就创建了句柄,因此InvokeRequired()可以按预期工作。

于 2012-11-06T16:02:44.837 回答