如何为显示的第二种形式获取同步上下文 [英] How to get a Synchronization Context for the second form shown
问题描述
改写并简化了整个帖子 [/EDIT]
Rephrased and Simplified whole post [/EDIT]
在此博客中,下面(我简化了一点)给出了一个示例,该示例使用SynchronizationContext对象在UI线程上运行Task:
In this blog, the following (I simplified it a bit) is given as an example of using a SynchronizationContext object to run a Task on the UI thread:
Task.Factory.StartNew(() =>"Hello World").ContinueWith(
task => textBox1.Text = task.Result,
TaskScheduler.FromCurrentSynchronizationContext());
我可以在一个新项目中重复这些结果,安全地更新UI,但是由于我当前项目中的任何原因(即使它正在工作),我都不能这样做.我收到了标准的不允许您从错误的线程更新UI"例外.
I can repeat these results in a fresh project, updating the UI safely, but for whatever reason in my current project (even though it's been working) I can't. I get the standard "You're not allowed to update the UI from the wrong thread" exception.
我的代码(在MainForm_Load(...)中)是这样的,它可以在新项目中工作,但要在主窗体中添加一个textBox1,但不能在我当前的项目中使用
My code (in MainForm_Load(...)) is like this, which works in a fresh Project w/ a textBox1 added to the main form, but does not work in my current project:
var one = Task.Factory.StartNew(
() => "Hello, my name is Inigo Montoya");
var two = one.ContinueWith(
task => textBox1.Text = one.Result,
TaskScheduler.FromCurrentSynchronizationContext());
任何人都对可能发生的事情有任何想法.
Anyone have any thoughts on what might be gong on.
我将错误追溯到对象的实例化,该对象使用一种形式来提示用户输入登录信息.仅在显示表单后才会发生错误.(如果我在表单的 Show
发生之前返回了一个硬编码的值,那么整个程序就可以正常工作.)
I've traced the error back to the instantiation of an object which uses a form to prompt the user for login information. The error only happens when the form has been shown. (If I return a hardcoded value before that Form's Show
happens the whole thing works fine).
新问题:如果我自己正在构造的表单在其显示之前先显示另一种表单,该如何获取SynchronizationContext?重现正在发生的事情的方法如下:
New question: How can I get the SynchronizationContext for the form which I'm constructing if its own constructor displays another form before it has been shown? Here's how you can reproduce what's happening:
1)创建两个表单:带有 TextBox
的Form1和带有 Button
1) Create two forms: Form1 with a TextBox
, and Form2 with a Button
2)创建一个类 OwnedBy1Uses2
Form1
:
public partial class Form1 : Form
{
OwnedBy1Uses2 member;
public Form1()
{
InitializeComponent();
member = new OwnedBy1Uses2();
}
private void Form1_Load(object sender, EventArgs e)
{
var ui = TaskScheduler.FromCurrentSynchronizationContext();
Task<string> getData = Task.Factory.StartNew(
() => "My name is Inigo Montoya...");
Task displayData = getData.ContinueWith(
t => textBox1.Text = t.Result, ui);
}
}
Form2
:
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
DialogResult = System.Windows.Forms.DialogResult.Cancel;
}
private void button1_Click(object sender, EventArgs e)
{
DialogResult = System.Windows.Forms.DialogResult.OK;
Hide();
}
}
OwnedBy1Uses2
:
class OwnedBy1Uses2
{
int x;
public OwnedBy1Uses2()
{
using (Form2 form = new Form2())
{
if (form.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
x = 1;
}
else
{
x = 2;
}
}
}
}
推荐答案
仅在主线程上是不够的.您需要有一个有效的 SynchronizationContext.Current
(在 FromCurrentSynchronizationContext
行上设置一个断点,并检查 SynchronizationContext.Current
的值;如果它是 null
,则出了点问题).
Just being on the main thread isn't sufficient. You need to have a valid SynchronizationContext.Current
(set a breakpoint on the FromCurrentSynchronizationContext
line and examine the value of SynchronizationContext.Current
; if it's null
, then something's wrong).
最干净的解决方案是从UI消息循环内执行任务代码,包括 FromCurrentSynchronizationContext
-即从诸如WinForms的 Form.Load
之类的代码或WPF的Window.Loaded
.
The cleanest fix is to execute your task code including FromCurrentSynchronizationContext
from within the UI message loop - that is, from something like Form.Load
for WinForms or Window.Loaded
for WPF.
WinForms中存在一个错误,即将其放入 Form.Load
还是不够的-实际上,您必须通过读取 Handle
属性来强制Win32句柄的创建.我以为该错误已得到修复,但我可能是错的.
There was a bug in WinForms where putting it in Form.Load
wasn't sufficient either - you actually had to force Win32 handle creation by reading the Handle
property. I was under the impression that this bug had been fixed, but I could be wrong.
编辑2(从评论复制):
我怀疑您的问题是您在 Application.Run
之外调用 ShowDialog
. ShowDialog
是一个嵌套的消息循环,但是在这种情况下,没有父消息循环.如果在 SynchronizationContext.Current
上设置监视并逐步执行 ShowDialog
,您将在显示对话框之前看到它是 WindowsFormsSynchronizationContext
,但是显示对话框后,更改为非WinForms SynchronizationContext
.将成员创建(包括 ShowDialog
)移动到 Load
事件可解决此问题.
I suspect your problem is that you're calling ShowDialog
outside of Application.Run
. ShowDialog
is a nested message loop, but in this case there's no parent message loop. If you set a watch on SynchronizationContext.Current
and step through the ShowDialog
, you'll see that it's a WindowsFormsSynchronizationContext
before the dialog is shown but changes to a non-WinForms SynchronizationContext
after the dialog is shown. Moving the member creation (including the ShowDialog
) to the Load
event fixes the problem.
这篇关于如何为显示的第二种形式获取同步上下文的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!