在阻塞 UI 线程时使用 WebBrowser 控件加载 html
Load html with WebBrowser Control while Blocking UI Thread
我有一个 .Net 4.5 WinForms 应用程序,其中 html 存储在数据库中。向用户显示页面列表。当他们单击一行时,页面将加载到 WebBrowser 中。我希望加载页面的过程与主 UI 线程同步(IOW,我不希望用户在加载页面之前能够执行任何其他操作)。加载 html 的代码是:
public class DocLoader
{
private readonly WebBrowser browser;
readonly TaskCompletionSource<object> loadedTCS = new TaskCompletionSource<object>();
public DocLoader(WebBrowser browser)
{
this.browser = browser;
}
private void LoadedComplete(object sender, WebBrowserDocumentCompletedEventArgs e)
{
Debug.WriteLine("doc completed");
loadedTCS.TrySetResult(null);
browser.DocumentCompleted -= LoadedComplete;
}
public Task LoadDoc(string html)
{
browser.DocumentCompleted += LoadedComplete;
browser.DocumentText = html;
return loadedTCS.Task;
}
}
像这样调用代码:
await new DocLoader(webBrowser1).LoadDoc(html);
将导致消息循环能够处理消息。这是不希望的。例如,用户可以在前一个文档完成加载之前单击列表中的另一个文档。
像这样调用代码:
new DocLoader(webBrowser1).LoadDoc(html).Wait();
导致 UI 冻结并且文档永远不会加载,大概是因为 DocumentCompleted 事件不会触发 w/o 消息循环 运行。
有没有办法完成这个任务并使进程与 UI 线程保持同步?
WebBrowser
需要其父线程发送消息才能正常运行。在你的例子中,它的父线程 是 主线程 UI,所以你不能用 Wait
阻塞它。一种 hack 可能是禁用整个 UI,因为 browser.DocumentText
异步加载相当快:
Cursor.Current = Cursors.WaitCursor;
mainFrom.Enabled = false;
try
{
await new DocLoader(webBrowser1).LoadDoc(html);
}
finally
{
mainFrom.Enabled = true;
Cursor.Current = Cursors.Default;
}
然而,这对用户来说非常不友好。 正确的方法 是在您的 WebBrowser
异步加载场景中支持取消。如果用户单击另一行(而前一行更新仍在等待中),您需要取消等待中的操作,然后开始新的操作。
我有一个 .Net 4.5 WinForms 应用程序,其中 html 存储在数据库中。向用户显示页面列表。当他们单击一行时,页面将加载到 WebBrowser 中。我希望加载页面的过程与主 UI 线程同步(IOW,我不希望用户在加载页面之前能够执行任何其他操作)。加载 html 的代码是:
public class DocLoader
{
private readonly WebBrowser browser;
readonly TaskCompletionSource<object> loadedTCS = new TaskCompletionSource<object>();
public DocLoader(WebBrowser browser)
{
this.browser = browser;
}
private void LoadedComplete(object sender, WebBrowserDocumentCompletedEventArgs e)
{
Debug.WriteLine("doc completed");
loadedTCS.TrySetResult(null);
browser.DocumentCompleted -= LoadedComplete;
}
public Task LoadDoc(string html)
{
browser.DocumentCompleted += LoadedComplete;
browser.DocumentText = html;
return loadedTCS.Task;
}
}
像这样调用代码:
await new DocLoader(webBrowser1).LoadDoc(html);
将导致消息循环能够处理消息。这是不希望的。例如,用户可以在前一个文档完成加载之前单击列表中的另一个文档。
像这样调用代码:
new DocLoader(webBrowser1).LoadDoc(html).Wait();
导致 UI 冻结并且文档永远不会加载,大概是因为 DocumentCompleted 事件不会触发 w/o 消息循环 运行。
有没有办法完成这个任务并使进程与 UI 线程保持同步?
WebBrowser
需要其父线程发送消息才能正常运行。在你的例子中,它的父线程 是 主线程 UI,所以你不能用 Wait
阻塞它。一种 hack 可能是禁用整个 UI,因为 browser.DocumentText
异步加载相当快:
Cursor.Current = Cursors.WaitCursor;
mainFrom.Enabled = false;
try
{
await new DocLoader(webBrowser1).LoadDoc(html);
}
finally
{
mainFrom.Enabled = true;
Cursor.Current = Cursors.Default;
}
然而,这对用户来说非常不友好。 正确的方法 是在您的 WebBrowser
异步加载场景中支持取消。如果用户单击另一行(而前一行更新仍在等待中),您需要取消等待中的操作,然后开始新的操作。