编程语言
首页 > 编程语言> > C# Winform WebBrowser控件

C# Winform WebBrowser控件

作者:互联网

C# WinForm WebBrowser 

1、主要用途:使用户可以在窗体中导航网页。

2、注意:WebBrowser 控件会占用大量资源。使用完该控件后一定要调用 Dispose 方法,以便确保及时释放所有资源。必须在附加事件的同一线程上调用 Dispose 方法,该线程应始终是消息或用户界面 (UI) 线程。

3、WebBrowser 使用下面的成员可以将控件导航到特定 URL、在导航历史记录列表中向后和向前移动,还可以加载当前用户的主页和搜索页:

1.URL属性:可读、可写,用于获取或设置当前文档的 URL。 

WebBrowser 控件维护浏览会话期间访问的所有网页的历史记录列表。设置Url属性时,WebBrowser 控件导航到指定的 URL 并将该 URL 添加到历史记录列表的末尾。

WebBrowser 控件在本地硬盘的缓存中存储最近访问过的站点的网页。每个页面都可以指定一个到期日期,指示页面在缓存中保留的时间。当控件定位到某页时,如果该页具有缓存的版本,则直接显示缓存中的内容而不必重新下载该页,从而节省了时间。使用 Refresh 方法强制 WebBrowser控件通过下载来重新加载当前页,从而确保控件显示最新版本。

注意:即使已请求了另一个文档,该属性也包含当前文档的 URL。如果设置该属性的值,然后立即再次检索该值,要是 WebBrowser 控件尚未来得及加载新文档,则检索到的值可能与设置的值不同。

2.Navigate方法: 将指定位置的文档加载到 WebBrowser 控件中。

3.GoBack方法:如果导航历史记录中的上一页可用,则将 WebBrowser 控件导航到该页。

如果导航成功,则返回true;如果导航历史记录中的上一页不可用,则返回false。

 WebBrowser 控件维护浏览会话期间访问的所有网页的历史记录列表。可以使用GoForward方法实现一个“后退”按钮。

使用 CanGoBack 属性确定导航历史记录是否可用以及是否包含上一页。处理 CanGoBackChanged 事件,在 CanGoBack 属性值更改时接收通知。

4.GoForward方法:如果导航历史记录中的下一页可用,则将 WebBrowser 控件导航到该页。

如果导航成功,则返回true;如果导航历史记录中的下一页不可用,则返回false。

WebBrowser 控件维护浏览会话期间访问的所有网页的历史记录列表。可以使用 GoForward 方法实现一个“前进”按钮.

使用 CanGoForward 属性确定导航历史记录是否可用以及是否包含当前页之后的页。处理 CanGoForwardChanged 事件,在 CanGoForward 属性值更改时接收通知 

  5.GoHome方法:将 WebBrowser 控件导航到当前用户的主页。

  6.GoSearch方法:将 WebBrowser 控件导航到当前用户的默认搜索页。 

    默认搜索页存储在注册表的 HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Main\Search Page 注册表项下。

    若要使用其他搜索页而不是默认搜索页,请调用 Navigate 方法或指定 Url 属性。

  7.Refresh方法:重新加载当前显示在 WebBrowser 控件中的文档。

  8.Stop方法:取消所有挂起的导航并停止所有动态页元素(如背景声音和动画)。

如果导航不成功,则显示一页指示出现的问题。使用这些成员中的任何一个进行导航都会导致在导航的不同阶段发生 Navigating、Navigated 和DocumentCompleted 事件。


4、ObjectForScripting 属性:获取或设置一个对象,该对象可由显示在 WebBrowser 控件中的网页所包含的脚本代码访问。

使用该属性启用 WebBrowser 控件承载的网页与包含 WebBrowser 控件的应用程序之间的通信。使用该属性可以将动态 HTML (DHTML) 代码与客户端应用程序代码集成在一起。为该属性指定的对象可作为 window.external 对象(用于主机访问的内置 DOM 对象)用于网页脚本。

可以将此属性设置为希望其公共属性和方法可用于脚本代码的任何 COM 可见的对象。可以通过使用 ComVisibleAttribute 对类进行标记使其成为 COM 可见的类。

若要从客户端应用程序代码调用网页中定义的函数,请使用可从 Document 属性检索的 HtmlDocument 对象的 HtmlDocument.InvokeScript 方法。

5、AllowNavigation属性:获取或设置一个值,该值指示控件在加载其初始页之后是否可以导航到其他页。

6、AllowWebBrowserDrop属性:获取或设置一个值,该值指示 WebBrowser 控件是否导航到拖放到它上面的文档。

7、WebBrowserShortcutsEnabled属性:是否启用WebBrowser自带的快捷键。

8、ScriptErrorsSuppressed 属性:获取或设置一个值,该值指示出现脚本错误时,WebBrowser 控件是否显示错误对话框。

9、IsWebBrowserContextMenuEnabled属性:是否启用右键菜单。


源:MSDN http://msdn.microsoft.com/zh-cn/library/system.windows.forms.webbrowser(v=vs.80).aspx

 

C#WinForm WebBrowser (二) 实用方法总结 

实用方法1:获取状态栏信息

void webBrowser1_StatusTextChanged(object sender, EventArgs e)
{
    label1.Text = webBrowser1.StatusText;
}

实用方法2:页面跳转后改变地址栏地址

//在Navigated事件处理函数中改变地址栏地址是最恰当的:
private void webBrowser1_Navigated(object sender, WebBrowserNavigatedEventArgs e)
{
    textBox1.Text = webBrowser1.Url.ToString();
}

实用方法3:设置单选框

//建议使用执行单击事件的方式来设置单选框,而不是修改属性:
webBrowser1.Document.GetElementById("RBT_A").InvokeMember("click");

实用方法4:设置联动型下拉列表

//比较常见的联动型多级下拉列表就是省/市县选择了,这种情况下直接设置选择项的属性不会触发联动,需要在最后执行触发事件函数才能正常工作:

foreach (HtmlElement f in s.GetElementsByTagName("option"))
{
    if (f.InnerText == "北京")
    {
        f.SetAttribute("selected", "selected");
    }
    else
    {
        f.SetAttribute("selected", "");
    }
}
s.RaiseEvent("onchange");

以上四种方法转于:http://www.cnblogs.com/SkyD/archive/2009/04/23/1441696.html

实用方法一:在WinForm中相应Web事件
假设HTML源代码如下:

  

HtmlDocument htmlDoc = webBrowser.Document; 
HtmlElement btnElement = htmlDoc.All["btnClose"]; 
if (btnElement != null) 

    btnElement.click += new HtmlElementEventHandler(HtmlBtnClose_Click); 
}


//很简单吧?那么稍稍高级一点的——我们都知道一个HTML元素可能有很多各种各样的事件,而HtmlElement这个类只给出最常用、共通的几个。那么,如何响应其他事件呢?这也很简单,只需要调用HtmlElement的AttachEventHandler就可以了:

btnElement.AttachEventHandler("onclick", new EventHandler(HtmlBtnClose_Click));  
//这一句等价于上面的btnElement.click += new HtmlElementEventHandler(HtmlBtnClose_Click); 

对于其他事件,把"onclick"换成该事件的名字就可以了。例如:

formElement.AttachEventHandler("onsubmit", new EventHandler(HtmlForm_Submit)); 

  

实用方法二:模拟表单自动填写和提交

假设有一个最简单的登录页面,输入用户名密码,点“登录”按钮即可登录。已知用户名输入框的id(或Name,下同)是username,密码输入框的id是password,“登录”按钮的id是submitbutton,那么我们只需要在webBrowser的DocumentCompleted事件中使用下面的代码即可:

HtmlElement btnSubmit = webBrowser.Document.All["submitbutton"]; 
HtmlElement tbUserid = webBrowser.Document.All["username"]; 
HtmlElement tbPasswd = webBrowser.Document.All["password"]; 

if (tbUserid == null || tbPasswd == null || btnSubmit == null) 
    return; 

tbUserid.SetAttribute("value", "smalldust"); 
tbPasswd.SetAttribute("value", "12345678"); 

btnSubmit.InvokeMember("click");

关于表单的提交,的确还有另一种方法就是获取form元素而不是button,并用form元素的submit方法:

HtmlElement formLogin = webBrowser.Document.Forms["loginForm"];  
//……  
formLogin.InvokeMember("submit"); 

本文之所以没有推荐这种方法,是因为现在的网页,很多都在submit按钮上添加onclick事件,以对提交的内容做最基本的验证。如果直接使用form的submit方法,这些验证代码就得不到执行,有可能会引起错误。 

实用方法三:调用脚本

首先是调用Web页面的脚本中已经定义好的函数。假设HTML中有如下JavaScript

function DoAdd(a, b) {
    return a + b;
}

那么,我们要在WinForm调用它,只需如下代码即可:

object oSum = webBrowser.Document.InvokeScript("DoAdd", new object[] { 1, 2 });
int sum = Convert.ToInt32(oSum);

 

其次,如果我们想执行一段Web页面中原本没有的脚本,该怎么做呢?这次.Net的类没有提供,看来还要依靠COM了。IHTMLWindow2可以将任意的字符串作为脚本代码来执行。

string scriptline01 = @"function ShowPageInfo() {";
string scriptline02 = @"     var numLinks = document.links.length; ";
string scriptline03 = @"     var numForms = document.forms.length; ";
string scriptline04 = @"     var numImages = document.images.length; ";
string scriptline05 = @"     var numScripts = document.scripts.length; ";
string scriptline06 = @"     alert('网页的统计结果:\r\n链接数:' + numLinks + ";
string scriptline07 = @"        '\r\n表单数:' + numForms + ";
string scriptline08 = @"        '\r\n图像数:' + numImages + ";
string scriptline09 = @"        '\r\n脚本数:' + numScripts);}";
string scriptline10 = @"ShowPageInfo();";

string strScript = scriptline01 + scriptline02 + scriptline03 + scriptline04 + scriptline05 +
                   scriptline06 + scriptline07 + scriptline08 + scriptline09 + scriptline10;

IHTMLWindow2 win = (IHTMLWindow2)webBrowser.Document.Window.DomWindow;
win.execScript(strScript, "javascript");
 

以上三种实用方法转于:http://www.cnblogs.com/smalldust/archive/2006/03/08/345561.html

最后:在脚本中调用WinForm里的代码,这个可能吗? 呵呵,当然是可能的。
下面的代码示例演示如何使用 ObjectForScripting 属性。在该示例中,ObjectForScripting 属性被设置为当前窗体。

view sourceprint?

using System; 


using System.Windows.Forms; 

using System.Security.Permissions; 

  


[PermissionSet(SecurityAction.Demand, Name="FullTrust")] 


[System.Runtime.InteropServices.ComVisibleAttribute(true)] 

public class Form1 : Form 


    private WebBrowser webBrowser1 = new WebBrowser(); 

    private Button button1 = new Button(); 


  


    [STAThread] 

    public static void Main() 

    { 


        Application.EnableVisualStyles(); 

        Application.Run(new Form1()); 


    } 

  


    public Form1() 

    { 


        button1.Text = "call script code from client code"; 

        button1.Dock = DockStyle.Top; 


        button1.Click += new EventHandler(button1_Click); 

        webBrowser1.Dock = DockStyle.Fill; 


        Controls.Add(webBrowser1); 

        Controls.Add(button1); 


        Load += new EventHandler(Form1_Load); 

    } 


  


    private void Form1_Load(object sender, EventArgs e) 

    { 


        webBrowser1.AllowWebBrowserDrop = false; 

        webBrowser1.IsWebBrowserContextMenuEnabled = false; 

        webBrowser1.WebBrowserShortcutsEnabled = false; 


        webBrowser1.ObjectForScripting = this; 


        // Uncomment the following line when you are finished debugging. 

        //webBrowser1.ScriptErrorsSuppressed = true; 

  


        webBrowser1.DocumentText = 


            "

你也能使用这技术传递整个对象到一个网页。为了实现它,在你的IDispatch实现中创建一个方法,传递回你的网页可以用的对象。

可以看看示例代码中使用 ATL的IDispatch自动化实现的一个例子 。

译者注:IE也扩展了浏览器的文档对象模型,使得你在脚本中可以通过扩展对象的menuArguments属性访问当前窗口对象。

IDocHostUIHandler::GetOptionKeyPath

IDocHostUIHandler::GetOptionKeyPath是自定义浏览器控件的一个非常有力的工具。 许多浏览器控件显示和行为设定被储存在注册表中HKEY_CURRENT_USER键的下面。IDocHostUIHandler::GetOptionKeyPath给你一个机会为你的浏览器控件的特定实例覆盖这些注册表设定。它通过让你提供一个替代的注册表位置来实现,浏览器控件将会在这里读取注册表设置。

IDocHostUIHandler::GetOptionKeyPath的一个实现传递给你让浏览器控件读取注册表设置的位置的一个字符串。浏览器控件将会找寻在HKEY_CURRENT_USER键下面的这一个键。

例子

HRESULT CBrowserHost::GetOptionKeyPath(LPOLESTR *pchKey,

    DWORD dwReserved)

{

    HRESULT hr;

    #define CCHMAX 256

    size_t cchLength;

    if (pchKey)

    {

        WCHAR* szMyKey = L"Software\MyCompany\MyApp";

        hr = StringCchLengthW(szMyKey, CCHMAX, &cchLength);

        //TODO: 在这里处理错误。

        *pchKey = (LPOLESTR)CoTaskMemAlloc((cchLength + 1) * sizeof(WCHAR));

        if (*pchKey)

            hr = StringCchCopyW(*pchKey, cchLength + 1, szKey);

        //TODO: 在这里处理错误。

        hr = (*pchKey) ? S_OK : E_OUTOFMEMORY;

    }

    else

        hr = E_INVALIDARG;

    return hr;

}

和IDocHostUIHandler::GetHostInfo一样,确保为你的字符串使用 CoTaskMemAlloc分配内存。

告诉浏览器控件该在哪里找寻你的注册表设置实际上是第一步——就程序运行来说是第二步。 你的程序必须在被IDocHostUIHandler::GetOptionKeyPath告诉的位置设置一个注册表键,这样浏览器控件才可以去读取。有多种方法来完成这个步骤。一个方法是当应用程序被安装的时候执行一个注册表脚本。另外的一个方法是当应用程序启动的时候,用代码来完成。这里是改变默认值字体,大小和颜色的一个设定。

例子

HRESULT SetSomeKeys()

{

    HKEY hKey = NULL;

    HKEY hKey2 = NULL;

    HKEY hKey3 = NULL;

    DWORD dwDisposition = NULL;

    LONG lResult = NULL;

    #define CBMAX 256

    size_t cbLength;

    RegCreateKeyEx(HKEY_CURRENT_USER, _T("Software\MyCompany\MyApp"),

        NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE,

        NULL, &hKey, &dwDisposition);

    RegCreateKeyEx(hKey, _T("Main"), NULL, NULL, REG_OPTION_NON_VOLATILE,

        KEY_SET_VALUE, NULL, &hKey2, &dwDisposition);

    RegSetValueEx(hKey2, _T("Use_DlgBox_Colors"), NULL, REG_SZ,

        (CONST BYTE*)_T("no"), sizeof(_T("no")));

    RegCloseKey(hKey2);

    RegCreateKeyEx(hKey, _T("Settings"), NULL, NULL, REG_OPTION_NON_VOLATILE,

        KEY_SET_VALUE, NULL, &hKey2, &dwDisposition);

    RegSetValueEx(hKey2, _T("Anchor Color"), NULL, REG_SZ,

        (CONST BYTE*)_T("0,255,255"), sizeof(_T("0,255,255")));

    RegSetValueEx(hKey2, _T("Text Color"), NULL, REG_SZ,

        (CONST BYTE*)_T("255,0,255"), sizeof(_T("255,0,255")));

    RegCloseKey(hKey2);

    RegCreateKeyEx(hKey, _T("International\Scripts"), NULL, NULL,

        REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL,

        &hKey2, &dwDisposition);

    BYTE bDefaultScript = 0x3;

    RegSetValueEx(hKey2, _T("Default_Script"), NULL, REG_BINARY,

        &bDefaultScript, sizeof(bDefaultScript));

    RegCreateKeyEx(hKey2, _T("3"), NULL, NULL, REG_OPTION_NON_VOLATILE,

        KEY_SET_VALUE, NULL, &hKey3, &dwDisposition);

    BYTE bSize = 0x4; // Value from 0 - 4. 2 is medium.

    TCHAR* szFontName = _T("Comic Sans MS");

    TCHAR* szFixedFontName = _T("Courier");

    HRESULT hr = StringCbLength(szFontName, CBMAX, &cbLength);

    //TODO: 在这里处理错误。

    RegSetValueEx(hKey3, _T("IEPropFontName"), NULL, REG_SZ,

        (CONST BYTE*)szFontName, cbLength + sizeof(TCHAR));

    hr = StringCbLength(szFixedFontName, CBMAX, &cbLength);

    //TODO: 在这里处理错误。

    RegSetValueEx(hKey3, _T("IEFixedFontName"), NULL, REG_SZ,

        (CONST BYTE*)szFixedFontName, cbLength + sizeof(TCHAR));

    RegSetValueEx(hKey3, _T("IEFontSize"), NULL, REG_BINARY, &bSize, sizeof(bSize));

    RegCloseKey(hKey3);

    RegCloseKey(hKey2);

    RegCloseKey(hKey);

    return S_OK;

}

IDocHostUIHandler2

IDocHostUIHandler2 只有一个方法,IDocHostUIHandler2::GetOverrideKeyPath。它运行非常类似于IDocHostUIHandler::GetOptionKeyPath的一个功能。它指出你修改自默认注册表设置的集成浏览器使用的注册表设置的位置。IDocHostUIHandler2::GetOverrideKeyPath 的一个实现看起来会很类似于IDocHostUIHandler::GetOptionKeyPath的一个实现。

GetOptionKeyPath 和 GetOverrideKeyPath 的比较

你或许没看到IDocHostUIHandler::GetOptionKeyPath和IDocHostUIHandler2::GetOverrideKeyPath之间的任何不同。在他们之间的不同是微妙的, 但是重要的。如果你实现 IDocHostUIHandler::GetOptionKeyPath,你的浏览器控件实例将会忽略任何IE的用户设定。这些设定被储存在注册表的HKEY_CURRENT_USER/Software/Microsoft/Internet Explorer下面。如果你实现IDocHostUIHandler2::GetOverrideKeyPath,你的浏览器控件实例将会合并任何的用户设定—字体设定,菜单扩展,诸如此类——到它的显示和行为中。

举例说明在IDocHostUIHandler::GetOptionKeyPath和IDocHostUIHandler2::GetOverrideKeyPath之间的不同,让我们重新看看IDocHostUIHandler::ShowContextMenu那段的示例代码。记住这一行:

spCT->Exec(&CGID_ShellDocView, SHDVID_ADDMENUEXTENSIONS, 0, &var1, &var2);

如果你已经实现IDocHostUIHandler::GetOptionKeyPath,因为菜单扩展信息被储存在当前用户的注册表信息中,所以这一行不会加入任何自定义项目到快捷菜单。如果你已经实现IDocHostUIHandler2::GetOverrideKeyPath, 这一个行会添加在HKEY_CURRENT_USER/Software/Microsoft/Internet Explorer/MenuExt面定义的任何目前用户定义的菜单扩展, 除非你明确地在你的自定义注册信息位置提供一个空的或替代的MenuExt键。

控制导航

你可能想知道在IDocHostUIHandler那一节为什么不提到 IDocHostUIHandler::TranslateUrl,作为在你希望控制页面导航时实现的方法。原因是这一个方法不是控制导航的最通用的技术。 除非你直接地集成MSHTML,这一个方法将没有控制导航的效果。作为替代,通过实现IDispatch::Invoke,处理DISPID_BEFORENAVIGATE2,你可以控制导航。例如,下列代码避免导航到一个特别的网址,如果使用者尝试这么做,会显示 "没有允许导航" 错误页。

例子

case DISPID_BEFORENAVIGATE2:

{

    CComBSTR url = ((*pDispParams).rgvarg)[5].pvarVal->bstrVal;

    if (url == "http://www.adatum.com" || url == "http://www.adatum.com/")

    {

        CComPtr

        CComPtr

        spDisp->QueryInterface(IID_IWebBrowser2, (void**)&spBrowser);

        spBrowser->Stop();

        CComBSTR newURL = "L"res://webhost.exe/nonavigate.htm";

        spBrowser->Navigate(newURL, NULL, NULL, NULL, NULL);

        ((*pDispParams).rgvarg)[0].boolVal = TRUE;

    }

    break;

}

IDocHostShowUI

这个接口给你对浏览器控件显示的信息对话框和帮助的控制。它工作机理和IDocHostUIHandler和IDocHostUIHandler2一样,你实现它,这样在浏览器控件显示它自己的任何的信息或帮助之前 ,能调用你的IDocHostShowUI的方法。这给你一个机会阻止浏览器控件显示任何东西,而且使你能够改为显示你自己的自定义信息或帮助。 IDocHostShowUI有两个方法,IDocHostShowUI::ShowMessage和IDocHostShowUI::ShowHelp。

IDocHostShowUI::ShowMessage

返回 S_OK禁用浏览器控件的信息对话框。任何其他的返回数值,像S_FALSE或E_NOTIMPL,允许浏览器控件显示它的信息对话框。

你通过这个方法能做的一件好的事情是为你的应用程序自定义信息框标题,替代 "Microsoft Internet Explorer" 。你能通过比较lpstrCaption和储存在Shdoclc.dll中的IE使用的字符串资源来完成它。它的ID是IDS_MESSAGE_BOX_TITLE,数值是2213。下列示例代码演示你可能需要做的工作。

例子

HRESULT CBrowserHost::ShowMessage(HWND hwnd,

    LPOLESTR lpstrText,

    LPOLESTR lpstrCaption,

    DWORD dwType,

    LPOLESTR lpstrHelpFile,

    DWORD dwHelpContext,

    LRESULT *plResult)

{

    USES_CONVERSION;

    TCHAR pBuffer[50];

    // 窗口标题"Microsoft Internet Explorer"的资源标识

    #define IDS_MESSAGE_BOX_TITLE 2213

    //载入Shdoclc.dll 和IE消息框标题字符串

    HINSTANCE hinstSHDOCLC = LoadLibrary(TEXT("SHDOCLC.DLL"));

    if (hinstSHDOCLC == NULL)

    {

        // 载入模块错误 -- 尽可能安全地失败

        return;

    }

    LoadString(hinstSHDOCLC, IDS_MESSAGE_BOX_TITLE, pBuffer, 50);

    // 比较IE消息框标题字符串和lpstrCaption

    // 如果相同,用自定义标题替换

    if (_tcscmp(OLE2T(lpstrCaption), pBuffer) == 0)

        lpstrCaption = L"Custom Caption";

    // 创建自己的消息框并且显示

        *plResult = MessageBox(OLE2T(lpstrText), OLE2T(lpstrCaption), dwType);

    //卸载Shdoclc.dll并且返回

    FreeLibrary(hinstSHDOCLC);

    return S_OK;

}

安全警告:不正确地使用LoadLibrary能载入错误的动态链接库(DLL)来威胁你的应用程序的安全。关于该如何正确地用微软Windows的不同版本载入DLL的信息,参照 LoadLibrary的文档。

IDocHostShowUI::ShowHelp

这一个方法在当IE需要显示帮助时被调用,举例来说当 F1 键被按下时,而且工作方式和IDocHostShowUI::ShowMessage类似。返回S_OK覆盖IE的帮助,或另外的HRESULT值让IE执行自己的帮助。

控制下载和执行

浏览器控件给你它的下载,显示设置和执行的控制权。 为了要得到这些控制,你实现你的宿主的IDispatch接口,使得它处理DISPID_AMBIENT_DLCONTROL。当浏览器控件被实例化的时候,它将会以这一个ID调用你的IDispatch::Invoke。将pvarResult设置为下列的标识的一个位与的组合,指明你的配置。

· DLCTL_DLIMAGES , DLCTL_VIDEOS 和 DLCTL_BGSOUNDS: 如果这些标识被设定,图像,视频和背景音乐将会被从服务器下载并且显示或播放,否则将不被下载和显示。

· DLCTL_NO_SCRIPTS 和 DLCTL_NO_JAVA: 脚本和Java小程序将不被运行。

· DLCTL_NO_DLACTIVEXCTLS 和 DLCTL_NO_RUNACTIVEXCTLS: ActiveX 控件将不被下载或者运行。

· DLCTL_DOWNLOADONLY: 网页只将会被下载,不显示。

· DLCTL_NO_FRAMEDOWNLOAD:浏览器控件将会下载并且解析框架集页面,但是不会下载和解析框架集中单独的框架。

· DLCTL_RESYNCHRONIZE 和 DLCTL_PRAGMA_NO_CACHE: 这些标志导致Internet缓冲的刷新。通过 DLCTL_RESYNCHRONIZE,服务器将会被请求更新状态。如果服务器指出缓存信息是最新的,将会使用 缓存文件。通过DLCTL_PRAGMA_NO_CACHE,不管文件的更新状态如何,文件都会被从服务器重新下载。

· DLCTL_NO_BEHAVIORS: 行为不被下载并且在文件中被禁用。

· DLCTL_NO_METACHARSET_HTML: 忽略在META元素中指明的字符集。

· DLCTL_URL_ENCODING_DISABLE_UTF8 和 DLCTL_URL_ENCODING_ENABLE_UTF8: 这些标志的功能类似于IDocHostUIHandler::GetHostInfo中使用的DOCHOSTUIFLAG_URL_ENCODING_DISABLE_UTF8和DOCHOSTUIFLAG_URL_ENCODING_ENABLE_UTF8标志。不同是只有在浏览器控件被初始化的时候,DOCHOSTUIFLAG标志才会被检查。这里的环境特性变化的下载标志在每当浏览器控件需要运行一个下载时被检查。

· DLCTL_NO_CLIENTPULL: 不运行客户端重定位页面操作(译者注:例如 的默认行为)。

· DLCTL_SILENT: 在下载期间没有用户界面显示。

· DLCTL_FORCEOFFLINE: 浏览器控件总是在脱机模式中操作。

· DLCTL_OFFLINEIFNOTCONNECTED 和 DLCTL_OFFLINE: 这些标志是相同的。如果不连接到英特网,浏览器控件将会在脱机模式中操作。

DISPID_AMBIENT_DLCONTROL和标志的数值是在mshtmdid.h被定义的。

最初,当对IDispatch::Invoke调用开始的时候, pvarResult参数指向的VARIANT是VT_EMPTY类型。 你必须为任何有效的设定设置它为VT_I4类型。你可以在VARIANT的lVal成员中存储标志数值。

大部份标志数值有否定的效果,也就是说,他们避免行为正常地发生。举例来说,如果你不自定义浏览器控件行为,那么通常脚本会被执行。 但是如果你设定DLCTL_NOSCRIPTS 标志,脚本将不会在控制的那个实例中运行。然而,三个标志— DLCTL_DLIMAGES , DLCTL_VIDEOS 和 DLCTL_BGSOUNDS的作用正好相反。你必须全部设置标志,使得浏览器控件以它的默认行为执行关于图像,视频和声音的处理。

下列示例代码使得一个浏览器控件实例下载并且显示图像和视频,但是不处理背景音乐,因为DLCTL_BGSOUNDS没有被明确地设定。浏览器控件显示的页上的脚本运行被禁用。

STDMETHODIMP CAtlBrCon::Invoke(DISPID dispidMember, REFIID riid,

    LCID lcid, WORD wFlags,

    DISPPARAMS* pDispParams,

    VARIANT* pvarResult,

    EXCEPINFO* pExcepInfo,

    UINT* puArgErr)

{

    switch (dispidMember)

    {

        case DISPID_AMBIENT_DLCONTROL:

            pvarResult->vt = VT_I4;

            pvarResult->lVal = DLCTL_DLIMAGES | DLCTL_VIDEOS | DLCTL_NO_SCRIPTS;

            break;

        default:

            return DISP_E_MEMBERNOTFOUND;

    }

    return S_OK;

}

IHostDialogHelper

IHostDialogHelper是一个你能根据你的爱好创建对话框的接口。这一个接口有一个方法,IHostDialogHelper::ShowHTMLDialog。这一个方法提供如同功能ShowHTMLDialog一般的服务,但是使用起来稍微比较容易一点。

为了要使用IHostDialogHelper,你从头产生对话框辅助对象。在这里是你使用CoCreateInstance的方式创建它。接口和ID在 mshtmhst.h 中被定义。

例子

IHostDialogHelper* pHDH;

IMoniker* pUrlMoniker;

BSTR bstrOptions = SysAllocString(L"dialogHeight:30;dialogWidth:40");

BSTR bstrPath = SysAllocString(L"c:\dialog.htm");

CreateURLMoniker(NULL, bstrPath, &pUrlMoniker);

// 创建对话框辅助对象

CoCreateInstance(CLSID_HostDialogHelper,

    NULL,

    CLSCTX_INPROC,

    IID_IHostDialogHelper,

    (void**)&pHDH);

//调用ShowHTMLDialog 创建对话框

pHDH->ShowHTMLDialog(NULL,

    pUrlMoniker,

    NULL,

    bstrOptions,

    NULL,

    NULL);

//释放资源

SysFreeString(bstrPath);

SysFreeString(bstrOptions);

pUrlMoniker->Release();

pHDH->Release();

译者注:如果要使用对话框来获得用户输入,你可能需要传递两个参数到ShowHTMLDialog。关于ShowHTMLDialog参数的说明,参见Platform SDK文档。ShowHTMLDialog和ShowHTMLDialogEx 似乎一直是MSHTML.DLL导出的两个函数,微软把它封装为接口,可能是在为未来的兼容性作准备。

控制新的窗口

控制浏览器控件的一个重要的方法是控制导航。你在前面已经看见如何在IDispatch::Invoke中拦截DISPID_BEFORENAVIGATE2来实现控制你的浏览器控件的导航位置。另外一个导航的重要的方面是要控制导航发生方式, 尤其是打开新的窗口的时候。让我们举例来说, 使用者右击一个链接,选择 "在新窗囗中打开" 或某一页包含像这样的脚本:

window.open("www.msn.com")

默认地,浏览器控件对这行代码的处理是通过打开IE的一个新的实例来显示网页。这可能正好是你的应用程序需要的,但是也可能不是。也许你需要在当前的浏览器控件实例中打开所有链接,或者你将在你控制下的浏览器控件的一个新的实例——具有你的用户界面和你的商标——打开链接。

你可以在你的IDispatch实现中拦截一个事件——DWebBrowserEvents2::NewWindow2——来控制它。你的控制需要连接到DWebBrowserEvents2的连接点来拦截这一个事件。

你连接到了DWebBrowserEvents2之后,实现你的IDispatch::Invoke以处理 DISPID_NEWWINDOW2。在为DISPID_NEWWINDOW2的IDispatch::Invoke函数调用中,数组pDispParams包含两个参数。第一个,序号是零, 是一个布尔类型的数值,告诉浏览器控件是否取消新的窗囗。默认它是假值,而且将会打开一个新的窗囗。如果你要完全取消新窗囗的创建, 设定标志到真值。

序号为一的参数是一个IDispatch接口的指针。你可以将这一个参数设定为你已经创建的浏览器控件的IDispatch。当你传回这样一个IDispatch之后,MSHTML将会使用你给出的控件打开链接。

译者注:MFC中的DHTML类和类向导默认支持这个事件。需要更多信息的话,参见MSJ1998年7月份的文章Keeping an Eye on Your Browser by Monitoring Internet Explorer 4.0 Events,以及 微软知识库文章 Q184876 HOWTO: Use the WebBrowser Control NewWindow2 Event

显示一个正数对话框

IE6或者更高版本中,你可以在用户浏览一个合法的安全超文本传输协议(HTTPS)站点的时候显示证书对话框。这和用户点击IE中的锁图标效果相同。你可以通过 DWebBrowserEvents2::SetSecureLockIcon事件来显示你自己的图标。

#define SHDVID_SSLSTATUS 33

IOleCommandTarget *pct;

if (SUCCEEDED(pWebBrowser2->QueryInterface(IID_IOleCommandTarget, (void **) &pct)))

{

   pct->Exec(&CGID_ShellDocView, SHDVID_SSLSTATUS, 0, NULL, NULL);

   pct->Release();

}

信息栏

Windows XP SP2 中的Internet Explorer 6 引入了一个新的安全用户界面元素,称为信息栏。在特定操作被阻止的时候,信息栏给用户显示一个用户界面元素。特别的,它会在以下操作被阻止的时候显示。

· 弹出窗口初始化(参见 弹出窗口杀手)

· 

· 文件下载 (see 文件下载的限制)

· 

· 安装ActiveX 控件(see ActiveX 的限制)

· 

· ActiveX控件安全提示的原因是用户安全设置或者是控件未标记为脚本安全的。

· 

· 文件的扩展名和多用途因特网邮件扩展类型(MIME)不符的(参见 MIME 处理)

· 

· 网络协议锁死的内容(参见 协议)

· 

信息栏是Windows XP SP2 中的Internet Explorer 6引入的安全特性之一。和其他安全特性控制一样,可以通过一个注册表键来控制:(FEATURE_SECURITYBAND). 默认情况下IE(iexplorer.exe) 和Windows 资源管理器(explorer.exe) 在这个安全特性控制下。下面显示注册表键和启用过程:

HKEY_LOCAL_MACHINE (or HKEY_CURRENT_USER)

SOFTWARE

Microsoft

Internet Explorer

Main

FeatureControl

FEATURE_SECURITYBAND

iexplorer.exe= 0x00000001

explorer.exe= 0x00000001

process name.exe=0x00000001

这个FEATURE_SECURITYBAND 安全特性控制影响IE是否显示信息栏,信息栏在一个操作被阻止的时候提示用户。它不控制操作的阻止过程。

一个集成浏览器控件的程序可以通过将其进程添加到这个注册表键来启用信息栏。这可以通过调用CoInternetSetFeatureEnabled函数来在运行时执行。如果一个应用程序并未在这个安全特性控制下,那么浏览器控件的行为和Internet Explorer 6 SP1b中的一样.

没有方法通过脚本来访问这个特性。

在FEATURE_SECURITYBAND及相关安全特性控制下的应用程序可以使用信息栏应用程序编程接口(API)来在一个URL 操作被禁止时自定义显示的用户界面。为信息栏引入了很多新的OLECMDID命令。头三个是属于CGID_DocHostCommandHandler组。宿主应用程序应该在它们的IDocHostUIHandler实现的同一个对象中实现IOleCommandTarget ,以接受来自浏览器控件的IOleCommandTarget::Exec调用。

· OLECMDID_PAGEACTIONBLOCKED

· 

· OLECMDID_PAGEACTIONUIQUERY

· 

· OLECMDID_FOCUSVIEWCONTROLS

· 

宿主应用程序可以使用下面两个新的OLECMDID 命令来执行浏览器控件的IOleCommandTarget::Exec调用。

· OLECMDID_FOCUSVIEWCONTROLSQUERY

· 

· OLECMDID_SHOWPAGEACTIONMENU

· 

这个示例使用IWebBrowser2::ExecWB 来执行OLECMDID_SHOWPAGEACTIONMENU 命令。

   POINT pt = { 0 };

   GetCursorPos(&pt);

   CComVariant varHwnd((LONG)hwnd);

   CComVariant varX(pt.x);

   CComVariant varY(pt.y);

   SAFEARRAY* psa = SafeArrayCreateVector(VT_VARIANT, 0, 3);

   LONG lIndex = 0;

   SafeArrayPutElement(psa, &lIndex, &varHwnd);

   lIndex++;

   SafeArrayPutElement(psa, &lIndex, &varX);

   lIndex++;

   SafeArrayPutElement(psa, &lIndex, &varY);

   CComVariant varArgIn;

   V_VT(&varArgIn) = VT_ARRAY | VT_I4;

   V_ARRAY(&varArgIn) = psa;

   pBrowser->ExecWB(OLECMDID_SHOWPAGEACTIONMENU, (OLECMDEXECOPT)dwPageActionFlags, &varArgIn, NULL);

另外,应用程序可以实现IInternetSecurityManager来重载默认的安全区域设置,参见创建一个自定义URL安全管理器以获得更多信息.

结论

你现在有许多技术,可以根据你的处理来自定义浏览器控件。这个文章决不是没有遗漏的,但是希望你现在可以自行发现超越本文的技术。检查IE注册表设置中那些你可以用IDocHostUIHandler::GetOptionKeyPath或IDocHostUIHandler2::GetOverrideKeyPath修改的信息。记住许多注册表设置相互依赖。你可能必须做一些实验来发现注册表设置可以多么的有效地自定义;如果需要控制浏览器控件的拖放行为,你也可以去看看IDocHostUIHandler::GetDropTarget。

C# WinForm WebBrowser (五) 讨厌的问题 

WebBrowse 编辑模式 中几个讨厌的问题:

1、当设置DocumentText属性值时会一直弹出一个“可恶的保存对话框”

现我已知的较好的策略有:
1)在设置两个DocumentText属性值之间使用webBrowser1.Document.OpenNew(true)方法,但这个方法会引发一些问题。详细内容见下。
2)在设置DocumentText属性之前将编辑模式改为浏览模式,设置完后再将浏览模式改为编辑模式。


2、监控Html内容的改变。

监控 WebBrowser 控件内容的改变


3、WebBrowse的Mouse事件。

WebBrowse本身没有Mouse的相关事件,但是我们可以借助WebBrowse中的Body元素来模拟一些简单的相关事件。

//在WinForm中注册Web事件
//假设HTML源代码如下:

 
// WinForm中注册Web事件
HtmlDocument htmlDoc = webBrowser.Document; 
HtmlElement btnElement = htmlDoc.All["btnClose"]; 
if (btnElement != null) 

    btnElement.click += new HtmlElementEventHandler(HtmlBtnClose_Click); 
}

WebBrowse中更多的实用方法参见:C#WinForm WebBrowser (二) 实用方法总结


4、webBrowser1.Document.OpenNew(true)的Bug

相关链接:
1)追踪导致 WebBrowser控件 编辑模式下 Ctrl + Z 失效的原因 【求大侠佐证,这算不算是微软的Bug呢?】

追踪导致 WebBrowser控件 编辑模式下 Ctrl + Z 失效的原因 【求大侠佐证,这算不算是微软的Bug呢?】 


导致Ctrl  + Z失效的原因由以下2点连锁引发而导致:

1、为了解决 WebBrowser 控件导航时弹出“保存对话框”,使用了 this.webBrowser.Document.OpenNew(true); // 防止 弹出保存对话框, 该方法指示新的文本改变将会在新窗口中打开。

2、 由原因1导致 webBrowser 控件的编辑模式失效, 表面上看上去还是可以编辑的,但实际上新窗口内部已经不支持编辑了。

注:这里涉及到了WebBrowser控件的特殊性,它是由三层控件嵌套而成的,外面的两层是大概负责容器、 及 响应用户操作的, 而最内层的则是承载HTML标记,并通过渲染引擎展示HTML内容。用黑盒测试的方法推断,当使用webBrowser.Document.OpenNew(true);  方法时,最内层控件应该是一个新的实例, 表面上看上去还是可以编辑的,但实际上内部的新窗口已经不支持编辑了,进而导致了Ctrl + Z的失效!

测试代码如下:

public partial class FrmTest : Form
{
// 界面上有一个WebBrowser 和 4个Button

private string strUrl = "http://www.cnblogs.com/08shiyan";

public FrmTest()
{
InitializeComponent();
}

/// 
public void EditMode()
{
if (this.webBrowser1.Document != null)
{
mshtml.IHTMLDocument2 doc = this.webBrowser1.Document.DomDocument as mshtml.IHTMLDocument2;
doc.designMode = "on";
}
}

/// 
public void BrowseMode()
{
if (this.webBrowser1.Document != null)
{
mshtml.IHTMLDocument2 doc = this.webBrowser1.Document.DomDocument as mshtml.IHTMLDocument2;
doc.designMode = "off";
}
}

// 请确保该按钮是应用程序启动后第一次被点击
private void button1_Click(object sender, EventArgs e)
{
this.webBrowser1.DocumentText = string.Empty;
this.webBrowser1.Document.Write(string.Format("", this.strUrl));
this.EditMode();

this.webBrowser1.Document.OpenNew(true);
this.webBrowser1.Document.Write(string.Format("", this.strUrl));

// 注意此时Ctrl + Z 撤销操作将会失效
}

// 请确保该按钮是应用程序启动后第一次被点击
private void button2_Click(object sender, EventArgs e)
{
this.webBrowser1.DocumentText = string.Empty;
this.webBrowser1.Document.Write(string.Format("", this.strUrl));
this.EditMode();

this.webBrowser1.Document.OpenNew(true);
this.webBrowser1.Document.Write(string.Format("", this.strUrl));

this.EditMode(); // 与button1的差别是再次启用编辑模式 // 注意此时Ctrl + Z 撤销操作也会失效
}

// 请确保该按钮是应用程序启动后第一次被点击
private void button3_Click(object sender, EventArgs e)
{
this.webBrowser1.DocumentText = string.Empty;
this.webBrowser1.Document.Write(string.Format("", this.strUrl));
this.EditMode();

this.webBrowser1.Document.OpenNew(true);
this.webBrowser1.Document.Write(string.Format("", this.strUrl));

this.BrowseMode(); // 与button2 的区别是 先关闭编辑模式,再启用编辑模式
this.EditMode();

// 此时 Ctrl + Z 可以使用
}

// 重启应用程序
private void button4_Click(object sender, EventArgs e)
{
Application.Restart();
}

}

根据以上得出结论:
在“编辑模式”下: this.webBrowser.Document.OpenNew(true); 方法会打开一个新的“内部窗口”,而新窗口中“编辑模式”出现问题,导致Ctrl + Z ,Ctrl +Y操作失效。 此时 需要先关闭 “编辑模式” 然后再打开“编辑模式” Ctrl + Z, Ctrl +Y 才能正常使用。

标签:webBrowser1,控件,浏览器,C#,WebBrowser,NULL,DLCTL
来源: https://blog.51cto.com/anlaoliu/2835249