這是一個「快速&髒」的解決方案。這不是100%萬無一失,但它不會阻塞UI線程,它應該是令人滿意的原型WebBrowser控件自動化程序:
private async void testButton_Click(object sender, EventArgs e)
{
await Task.Factory.StartNew(
() =>
{
stepTheWeb(() => wb.Navigate("www.yahoo.com"));
stepTheWeb(() => wb.Navigate("www.microsoft.com"));
stepTheWeb(() => wb.Navigate("asp.net"));
stepTheWeb(() => wb.Document.InvokeScript("eval", new[] { "$('p').css('background-color','yellow')" }));
bool testFlag = false;
stepTheWeb(() => testFlag = wb.DocumentText.Contains("Get Started"));
if (testFlag) { /* TODO */ }
// ...
}
);
}
private void stepTheWeb(Action task)
{
this.Invoke(new Action(task));
WebBrowserReadyState rs = WebBrowserReadyState.Interactive;
while (rs != WebBrowserReadyState.Complete)
{
this.Invoke(new Action(() => rs = wb.ReadyState));
System.Threading.Thread.Sleep(300);
}
}
這裏是一個比較testButton_Click
方法的通用版本:
private async void testButton_Click(object sender, EventArgs e)
{
var actions = new List<Action>()
{
() => wb.Navigate("www.yahoo.com"),
() => wb.Navigate("www.microsoft.com"),
() => wb.Navigate("asp.net"),
() => wb.Document.InvokeScript("eval", new[] { "$('p').css('background-color','yellow')" }),
() => {
bool testFlag = false;
testFlag = wb.DocumentText.Contains("Get Started");
if (testFlag) { /* TODO */ }
}
//...
};
await Task.Factory.StartNew(() => actions.ForEach((x)=> stepTheWeb (x)));
}
[更新]
我已經適應了我的「快速&髒」的借用和sligthly重構@Noseratio's NavigateAsync
method from this topic樣品。 新的代碼版本將在UI線程上下文中自動執行/執行,不僅僅是導航操作,還包括Javascript/AJAX調用 - 任何「lamdas」/一個自動化步驟任務實現方法。
所有代碼評論/評論都非常受歡迎。特別是從@Noseratio
。我們要共同努力,讓這個世界變得更美好;)
public enum ActionTypeEnumeration
{
Navigation = 1,
Javascript = 2,
UIThreadDependent = 3,
UNDEFINED = 99
}
public class ActionDescriptor
{
public Action Action { get; set; }
public ActionTypeEnumeration ActionType { get; set; }
}
/// <summary>
/// Executes a set of WebBrowser control's Automation actions
/// </summary>
/// <remarks>
/// Test form shoudl ahve the following controls:
/// webBrowser1 - WebBrowser,
/// testbutton - Button,
/// testCheckBox - CheckBox,
/// totalHtmlLengthTextBox - TextBox
/// </remarks>
private async void testButton_Click(object sender, EventArgs e)
{
try
{
var cts = new CancellationTokenSource(60000);
var actions = new List<ActionDescriptor>()
{
new ActionDescriptor() { Action =()=> wb.Navigate("www.yahoo.com"), ActionType = ActionTypeEnumeration.Navigation} ,
new ActionDescriptor() { Action =() => wb.Navigate("www.microsoft.com"), ActionType = ActionTypeEnumeration.Navigation} ,
new ActionDescriptor() { Action =() => wb.Navigate("asp.net"), ActionType = ActionTypeEnumeration.Navigation} ,
new ActionDescriptor() { Action =() => wb.Document.InvokeScript("eval", new[] { "$('p').css('background-color','yellow')" }), ActionType = ActionTypeEnumeration.Javascript},
new ActionDescriptor() { Action =
() => {
testCheckBox.Checked = wb.DocumentText.Contains("Get Started");
},
ActionType = ActionTypeEnumeration.UIThreadDependent}
//...
};
foreach (var action in actions)
{
string html = await ExecuteWebBrowserAutomationAction(cts.Token, action.Action, action.ActionType);
// count HTML web page stats - just for fun
int totalLength = 0;
Int32.TryParse(totalHtmlLengthTextBox.Text, out totalLength);
totalLength += !string.IsNullOrWhiteSpace(html) ? html.Length : 0;
totalHtmlLengthTextBox.Text = totalLength.ToString();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Error");
}
}
// asynchronous WebBroswer control Automation
async Task<string> ExecuteWebBrowserAutomationAction(
CancellationToken ct,
Action runWebBrowserAutomationAction,
ActionTypeEnumeration actionType = ActionTypeEnumeration.UNDEFINED)
{
var onloadTcs = new TaskCompletionSource<bool>();
EventHandler onloadEventHandler = null;
WebBrowserDocumentCompletedEventHandler documentCompletedHandler = delegate
{
// DocumentCompleted may be called several times for the same page,
// if the page has frames
if (onloadEventHandler != null)
return;
// so, observe DOM onload event to make sure the document is fully loaded
onloadEventHandler = (s, e) =>
onloadTcs.TrySetResult(true);
this.wb.Document.Window.AttachEventHandler("onload", onloadEventHandler);
};
this.wb.DocumentCompleted += documentCompletedHandler;
try
{
using (ct.Register(() => onloadTcs.TrySetCanceled(), useSynchronizationContext: true))
{
runWebBrowserAutomationAction();
if (actionType == ActionTypeEnumeration.Navigation)
{
// wait for DOM onload event, throw if cancelled
await onloadTcs.Task;
}
}
}
finally
{
this.wb.DocumentCompleted -= documentCompletedHandler;
if (onloadEventHandler != null)
this.wb.Document.Window.DetachEventHandler("onload", onloadEventHandler);
}
// the page has fully loaded by now
// optional: let the page run its dynamic AJAX code,
// we might add another timeout for this loop
do { await Task.Delay(500, ct); }
while (this.wb.IsBusy);
// return the page's HTML content
return this.wb.Document.GetElementsByTagName("html")[0].OuterHtml;
}
你** **必須使用DocumentCompleted事件。所有你需要做的就是跟蹤*完成了什麼*。該事件已經告訴你,你會得到e.Url屬性。如果您需要了解更多信息,請使用跟蹤狀態的變量。一個簡單的整數或枚舉將會很好。 –