我有一个WebBrowser实例的线程,我附加了一个DocumentCompleted事件。但是根据我的观察,我的事件没有被提出,因为线程在它发生之前就结束了。当我将MessageBox.Show放在线程的末尾时,它会为事件提供时间。但是如何在没有MessageBox的情况下让线程等待呢?
WebBrowser browser;
Thread t = new Thread(() =>
{
string url = string.Format("webpage.com");
browser = new WebBrowser();
browser.ScriptErrorsSuppressed = true;
browser.Navigate(url);
browser.DocumentCompleted += browser_DocumentCompleted;
browser.DocumentCompleted += (o, a) =>
{
//MessageBox.Show("In DocumentCompleted.");
List<Status> statusy = new List<Status>();
IHTMLDocument2 currentDoc = (IHTMLDocument2)browser.Document.DomDocument;
//parsing the html doc
string Statuses = "";
foreach (Status status in statusy)
{
Statuses += String.Format("{0} {1} - {2} --> {3}{4}", status.Date, status.Time, status.Centre, status.Message, Environment.NewLine);
}
MessageBox.Show(Statuses);
};
MessageBox.Show("In thread!!!!");
});
t.SetApartmentState(ApartmentState.STA);
t.Start();
答案 0 :(得分:3)
您必须调用Application.Run()。不仅要确保您的线程不会太快结束,还需要让WebBrowser提升其事件。使用消息循环是一种标准方法,其中大量线程组件(如WebBrowser)确保在创建对象的同一线程上引发其事件。它实现了STA合同。
MessageBox.Show()在底层使用的消息循环使自己成为模态,这就是为什么它在你使用MessageBox时正常工作的原因。与Application.Run()实现的消息循环没有本质上的区别。
使用Application.ExitThread()来结束线程。必须在调用Application.Run()的同一线程上调用它。在DocumentCompleted事件处理程序中执行此操作时,这不会成为问题。
答案 1 :(得分:2)
主要问题是Web浏览器控件需要正确的消息循环才能正常工作。您正在启动的线程没有这样的消息循环,因此Web浏览器实际上无法做太多。
最简单的解决方案是简单地在表单中托管浏览器控件 - 这使您可以轻松控制浏览器的生命周期,并且可以轻松地维护消息循环(这是Application.Run
所做的)。
如果这不适用于您(也就是说,您根本不想显示任何表单),则需要制作无形式的消息循环。使用代码的最简单示例:
WebBrowser browser;
Thread t = new Thread(() =>
{
string url = string.Format("google.com");
browser = new WebBrowser();
browser.ScriptErrorsSuppressed = true;
browser.Navigate(url);
browser.DocumentCompleted += (o, a) =>
{
MessageBox.Show(browser.Document.Title);
Application.ExitThread();
};
Application.Run();
});
t.SetApartmentState(ApartmentState.STA);
t.Start();
如果你需要等待来自其他线程的事件,有很多方法可以同步。 Task
类是一种简单的方法,可以同时传递数据并使用基于TaskCompletionSource
的良好接口。例如,如果我想异步等待文档的标题,它就像这样简单:
WebBrowser browser;
var tcs = new TaskCompletionSource<string>();
Thread t = new Thread(() =>
{
string url = string.Format("google.com");
browser = new WebBrowser();
browser.ScriptErrorsSuppressed = true;
browser.Navigate(url);
browser.DocumentCompleted += (o, a) =>
{
tcs.SetResult(browser.Document.Title);
Application.ExitThread();
};
Application.Run();
});
t.SetApartmentState(ApartmentState.STA);
t.Start();
Console.WriteLine(await tcs.Task);
当然,这假设被调用者是异步方法,但是你可以对该任务做很多其他事情 - 例如,注册一个延续。
您不需要保留对Thread
实例的引用 - 启动的线程是根,因此永远不会收集它们。
答案 2 :(得分:0)
如何在DocumentCompleted
事件处理程序中设置标志,然后等待您正在执行MessageBox.Show()
的位置设置标志?
答案 3 :(得分:0)
使用旗帜。在DocumentCompleted
事件处理程序中,将标志设置为false
。
然后使用while语句,如:
bool Flag = true;
browser.DocumentCompleted += (o, a) =>
{
//MessageBox.Show("In DocumentCompleted.");
List<Status> statusy = new List<Status>();
IHTMLDocument2 currentDoc = (IHTMLDocument2)browser.Document.DomDocument;
//parsing the html doc
string Statuses = "";
foreach (Status status in statusy)
{
Statuses += String.Format("{0} {1} - {2} --> {3}{4}", status.Date, status.Time, status.Centre, status.Message, Environment.NewLine);
}
MessageBox.Show(Statuses);
Flag = false;
};
while (Flag) { }