[WPF - 之一問(wèn)一答系列] 如何從WPF的WebBrowser控件中獲得WebResponse內(nèi)容?為何WebBrowser控件的Navigated事件參數(shù)NavigationEventArgs的WebResponse屬性始終為null?
問(wèn):
如何從WPF的WebBrowser控件中獲得WebResponse內(nèi)容?為何WebBrowser控件的Navigated事件參數(shù)NavigationEventArgs的WebResponse屬性始終為null?
我們?cè)谝粋€(gè)WPF的WebBrowser的Navigated事件中,嘗試去輸出NavigationEventArgs e的WebResponse屬性,他始終是null。比如,
XAML代碼:
<WebBrowser x:Name="browser"/>C#代碼:
browser.Navigated += new NavigatedEventHandler(browser_Navigated);
browser.Navigate(new Uri("//省略...
void browser_Navigated(object sender, NavigationEventArgs e)
{
Console.WriteLine(e.WebResponse.Headers); //輸出null
}[相關(guān)MSDN英文貼:http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/856608db-48ba-4492-bff1-3721618ff3ae]
答:
一般都會(huì)認(rèn)為從WebBrowser中獲得WebResponse內(nèi)容,可以從Navigated事件的NavigationEventArgs參數(shù)中獲得。但是,事實(shí)是這個(gè)參數(shù)始終在返回null值,導(dǎo)致無(wú)法真正獲得返回的內(nèi)容。
解決這個(gè)問(wèn)題,首先想到的是去了解WebBrowser控件的基本組成。我們知道,IE的結(jié)構(gòu)是如下:
而對(duì)于一個(gè)WebBrowser控件(無(wú)論是Winform的還是WPF的),都是和這個(gè)架構(gòu)類(lèi)似的,不同的只是最上層的UI封裝。控件來(lái)說(shuō),是沒(méi)有IExplore.exe的包裝的,所以一般控件就沒(méi)有IE的某些特性,但是從底層來(lái)說(shuō),他們都使用了ShDocVw.dll和MSHTML.dll,控件只是對(duì)下方組件的一次封裝。那么這個(gè)Navigated事件是從誰(shuí)拋出的呢?答案是ShDocVw.dll,當(dāng)然,面向不同的封裝,拋到上方會(huì)遇到不同的處理。Wnform中的WebBrowserNavigatedEventArgs中沒(méi)有封裝WebResponse信息,而從WPF的WebBrowser中,卻包含了這個(gè)屬性。
接下來(lái),從表象很難找到原因了,這個(gè)時(shí)候就需要借助下工具,比如我使用了Reflector (從這里可以下載到試用版:http://reflector.red-gate.com/download.aspx?TreatAsUpdate=1)去反編譯了一些源代碼,嘗試去分析下為什么會(huì)一直返回null。通過(guò)工具分析,我們可以找到下面的一個(gè)調(diào)用列表:
在NavigationEventArgs類(lèi)中的_webResponse成員實(shí)際就是我們要分析的對(duì)象,他僅由NavigationEventArgs的構(gòu)造函數(shù)調(diào)用。而從他的構(gòu)造函數(shù)的被調(diào)用列表中,我們發(fā)現(xiàn)了兩類(lèi)方法,一類(lèi)是被定義在MS.Internal.Controls.WebBrowserEvent中的,還有是定義在System.Windows.Navigation.NavigationService中的。當(dāng)然,我們可以想到,第一類(lèi)是所謂WebBrowser控件的內(nèi)部組件類(lèi),這個(gè)類(lèi)實(shí)際就是所謂架構(gòu)中對(duì)于ShDocVw.dll的一次.Net封裝。所以這個(gè)類(lèi)的方法就是WebBrowser在Navigated的時(shí)候調(diào)用的。點(diǎn)擊顯示MS.Internal.Controls.WebBrowserEvent.NavigateComplete2方法的邏輯代碼:
[SecurityTreatAsSafe, SecurityCritical]
public void NavigateComplete2(object pDisp, ref object url)
{
//省略…
NavigationEventArgs e = new NavigationEventArgs(uri, null, null, null, null, true);
this._parent.OnNavigated(e);
//省略…
}可以看出,代碼中直接使用null值構(gòu)造了NavigationEventArgs參數(shù),然后觸發(fā)WebBrowser.Navigated事件。(當(dāng)然,你也可以看DocumentComplete方法,在這個(gè)里面一樣的用了null去構(gòu)造后觸發(fā)了WebBrowser.LoadCompleted事件)
到此可以解釋為什么WebResponse屬性總是返回null了。
等等,還沒(méi)有結(jié)束。那么怎么去獲得WebBrowser呢? 我們還省略沒(méi)有去看另外的幾個(gè)NavigationService的方法,比如System.Windows.Navigation.NavigationService.FireNavigated方法邏輯代碼:
private void FireNavigated(object navState)
{
object extraData = (navState is NavigateInfo) ? null : navState;
try
{
NavigationEventArgs e = new NavigationEventArgs(this.CurrentSource, this.Content, extraData, this._webResponse, this.INavigatorHost, this.IsNavigationInitiator);
//省略…
}可以看出,NavigationService在他的事件中封裝了WebResponse信息。這樣的話,就有了解決方案——
使用NavigationService去Navigate頁(yè)面,并且在Navigated事件中獲得。但是WebBrowser本身沒(méi)有支持NavigationService,在WPF中只有NavigationWindow和Frame支持了NavigationService,所以我們只需要使用這兩者,這里我的代碼是用了Frame替代了WebBrowser,
XAML:
<Frame x:Name="frame"/>
C#代碼:
frame.Navigated += new NavigatedEventHandler(frame_Navigated);
frame.NavigationService.Navigate(new Uri("http://www.microsoft.com"));
//省略...
void frame_Navigated(object sender, NavigationEventArgs e)
{
Console.WriteLine(e.WebResponse.Headers);
}
[備注:]在WebBrowser和Frame之間,我的第一感覺(jué)是,如果你需要一個(gè)瀏覽控件有依賴屬性來(lái)支持綁定等WPF特性的話,那么就選擇Frame吧,它包含你所需要的依賴屬性,而WebBrowser沒(méi)有。 不過(guò)我還會(huì)推薦下一個(gè)第三方的WPF瀏覽器控件:http://wpfchromium.codeplex.com/ 基于Google的Chromium項(xiàng)目的。
.gif)

浙公網(wǎng)安備 33010602011771號(hào)