好吧,我有一个包含5个WebBrowser
控件的表单,每个控件都打开一个页面并处理DocumentComplete
事件。问题是有时事件不会触发。阅读后我遇到了一个答案,如果线程繁忙,Navigated
,DocumentComplete
等事件将不会触发,所以我尝试在单独的线程中创建WebBrowser
然后添加它到导致交叉线程异常的形式
代码
new Thread(() =>
{
WebBrowser1 = new WebBrowser
{
Location = new Point(15, 14),
MinimumSize = new Size(20, 20),
Name = "WebBrowser1",
ScriptErrorsSuppressed = true,
Size = new Size(250, 370),
TabIndex = 0,
Url = new Uri("", UriKind.Relative)
};
((WebBrowser_V1) WebBrowser1.ActiveXInstance).NewWindow += (string u, int f, string n, ref object d, string h, ref bool p) =>
{
p = true;
WebBrowser1.Navigate(u);
};
WebBrowser1.DocumentCompleted += async (sender, args) =>
{
// Code...
};
WebBrowser1.Navigated += (sender, args) =>
{
// Code...
};
WebBrowser1.Navigate(Service.LinkTL.Find(_ => _.Valid)?.Use()?.Address);
//Cross-Thread Exception
base.Invoke(new MethodInvoker(() =>
{
Controls.Add(WebBrowser1);
}));
Application.Run();
}) {ApartmentState = ApartmentState.STA}.Start();
我需要WebBrowser
控件在表单中可见,但能够拦截所有触发的事件。
编辑:如果我调用WebBrowser
,我会获得Controls created on one thread cannot be parented to a control on a different thread
例外。
答案 0 :(得分:0)
您需要在跨线程操作上检查InvokeRequired(请参阅:https://msdn.microsoft.com/en-us/library/system.windows.forms.control.invokerequired(v=vs.110).aspx)。
如果是,则需要从代码中调用委托。
myFormControl1.Invoke(myFormControl1.myDelegate);
典型的模式是:
private void DoFoo() {
if (myFormControl1.InvokeRequired) {
myFormControl1.Invoke(new MethodInvoker(() => { DoFoo(); }));
} else {
object1.Visible = true;
object2.Visible = false;
}
}
此主题讨论了如何干净地实现此方法而不会弄脏您的代码:Automating the InvokeRequired code pattern
线程中一个有用的答案,显示了一个处理调用的静态方法:
public static void InvokeIfRequired(this Control control, MethodInvoker action)
{
if (control.InvokeRequired) {
control.Invoke(action);
} else {
action();
}
}
然后定义代表如:
richEditControl1.InvokeIfRequired(() =>
{
// Do anything you want with the control here
richEditControl1.RtfText = value;
RtfHelpers.AddMissingStyles(richEditControl1);
});
使用它来完成您的具体问题:
public static class myExtension
{
public static void InvokeIfRequired(this Control control, MethodInvoker action)
{
// See Update 2 for edits Mike de Klerk suggests to insert here.
if (control.InvokeRequired)
{
control.Invoke(action);
}
else
{
action();
}
}
}
// ...
protected WebBrowser WebBrowser1;
public void loadBrowser()
{
new Thread(() =>
{
var WebBrowser1 = new WebBrowser
{
Location = new Point(15, 14),
MinimumSize = new Size(20, 20),
Name = "WebBrowser1",
ScriptErrorsSuppressed = true,
Size = new Size(250, 370),
TabIndex = 0,
};
var form = new Form()
{
Name = "Other Form",
Text = "Thread Start Browser",
AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F),
AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font,
ClientSize = new System.Drawing.Size(284, 261),
};
form.Controls.Add(WebBrowser1);
form.Show();
//((WebBrowser_V1)WebBrowser1.ActiveXInstance).NewWindow += (string u, int f, string n, ref object d, string h, ref bool p) =>
//{
// p = true;
// WebBrowser1.Navigate(u);
//};
//WebBrowser1.DocumentCompleted += async (sender, args) =>
//{
// // Code...
//};
WebBrowser1.Navigated += (sender, args) =>
{
// Code...
};
//WebBrowser1.Navigate(Service.LinkTL.Find(_ => _.Valid)?.Use()?.Address);
WebBrowser1.Navigated += (sender, args) =>
{
// Code...
};
//WebBrowser1.Navigate(Service.LinkTL.Find(_ => _.Valid)?.Use()?.Address);
this.InvokeIfRequired(() =>
{
this.WebBrowser1 = WebBrowser1;
this.CreatedBrowser();
});
Application.Run();
})
{ ApartmentState = ApartmentState.STA }.Start();
}
private void Form1_Load(object sender, EventArgs e)
{
loadBrowser();
}
public void CreatedBrowser()
{
Console.WriteLine("Created browser");
}
private void btnGo_Click(object sender, EventArgs e)
{
string url = txtUrl.Text;
WebBrowser1.InvokeIfRequired(() => WebBrowser1.Navigate(url));
}
这是一个屏幕截图: