如何在我创建的另一个窗口中从WebBrowser显示弹出窗口?

8
我正在尝试在我的应用程序中实现一个简单的Web浏览器控件,以帮助将Web应用程序集成到我正在创建的工具集中。
问题是,这个Web应用程序绝对喜欢弹出窗口...
当弹出窗口打开时,它会在一个IE窗口中打开,而该窗口不是我的主窗口所在的MDI容器窗体的子窗口。
如何让通过单击WebBrowser中的链接创建的所有弹出窗口都成为我的MDI容器的子窗口(类似于设置窗体的MDIParent属性)?
提前感谢。

你说的弹出窗口是指什么?是window.open还是window.createpopup? - Sheng Jiang 蒋晟
4个回答

26

Web浏览器控件支持NewWindow事件,可以用它来通知弹出窗口的出现。然而,Winforms包装器并不允许您对其进行太多操作,只能取消弹出窗口。原生的COM包装器允许传递一个新的Web浏览器实例,并将该实例用于显示弹出窗口。

要利用此功能需要一些工作。首先,使用项目+添加引用、浏览选项卡,选择c:\windows\system32\shdocvw.dll。这将添加对原生COM接口的引用。

创建一个作为弹出窗口的表单。在其中放置一个WebBrowser控件,使其代码看起来类似于以下内容:

public partial class Form2 : Form {
    public Form2() {
        InitializeComponent();
    }
    public WebBrowser Browser {
        get { return webBrowser1; }
    }
}

Browser属性提供了访问将用于在弹出窗口中显示网页的浏览器的途径。

现在回到主窗体。在其上放置一个WebBrowser控件,并将其代码设置为如下:

public partial class Form1 : Form {
    public Form1() {
        InitializeComponent();
        webBrowser1.Url = new Uri("http://google.com");
    }
    SHDocVw.WebBrowser nativeBrowser;
    protected override void OnLoad(EventArgs e) {
        base.OnLoad(e);
        nativeBrowser = (SHDocVw.WebBrowser)webBrowser1.ActiveXInstance;
        nativeBrowser.NewWindow2 += nativeBrowser_NewWindow2;
    }
    protected override void OnFormClosing(FormClosingEventArgs e) {
        nativeBrowser.NewWindow2 -= nativeBrowser_NewWindow2;
        base.OnFormClosing(e);
    }

    void nativeBrowser_NewWindow2(ref object ppDisp, ref bool Cancel) {
        var popup = new Form2();
        popup.Show(this);
        ppDisp = popup.Browser.ActiveXInstance;
    }
}

OnLoad方法获取本机COM接口的引用,然后订阅NewWindow2事件处理程序。我确保在FormClosing事件处理程序中取消了该事件的订阅,但不确定是否必要。谨慎起见,最好这样做。

NewWindow2事件处理程序是关键所在,请注意第一个参数允许传递未类型化的引用。那应该是弹出窗口中的本机浏览器。因此,我创建了Form2实例并将其Show()。请注意Show()的参数,它确保弹出窗口是一个拥有的窗口。根据您的应用程序需要进行替换,我假设您想在您的情况下创建一个MDI子窗口。

请注意,此事件不会为JavaScript使用alert()时显示的窗口触发。浏览器不将该窗口视为HTML弹出窗口,并且不使用浏览器窗口显示它,因此您无法拦截或替换它。


这个解决方案已经不再有用了! - Amir
3
在Windows 8上使用IE10和VS2012重新测试后,仍然很好用。请点击“提问”按钮而不是投票按钮。 - Hans Passant
您还可以从“添加引用”屏幕安装COM组件。选择COM并选择Microsoft Internet Controls。 - Guillaume Schuermans
1
@rima,请不要只说它不起作用,也请提供您的理由。 - Stuart
一个好奇的问题——为什么在窗体构造函数中ActiveX实例为空,在onLoad中不为空,这背后有一些值得了解的背景吗? - mlvljr

3
我发现最好的方法是实现/下沉 NewWindow3 事件
像这里其他答案中提到的一样,添加对 c:\windows\system32\shdocvw.dll 的引用。 添加事件处理程序
SHDocVw.WebBrowser wbCOMmain = (SHDocVw.WebBrowser)webbrowser.ActiveXInstance;
wbCOMmain.NewWindow3 += wbCOMmain_NewWindow3;

事件方法
void wbCOMmain_NewWindow3(ref object ppDisp, 
                          ref bool Cancel, 
                          uint dwFlags, 
                          string bstrUrlContext, 
                          string bstrUrl)
{
    // bstrUrl is the url being navigated to
    Cancel = true; // stop the navigation

    // Do whatever else you want to do with that URL
    // open in the same browser or new browser, etc.
}
  • 将 "Interop.SHDocVw" 组件的 "嵌入互操作类型" 设置为 false
  • 将“本地副本”设置为 true。

有关此帮助的来源,请参见MSDN 帖子


2

优化了Hans的答案,您可以使用未发布的Winforms WebBrowser.AttachInterface和DetachInterface方法派生出用于访问COM的WebBrowser而不添加引用。

更详细的内容在此处

以下是代码:

使用方法(将您的WebBrowser实例更改为WebBrowserNewWindow2)

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        this.webBrowser1.NewWindow2 += webBrowser_NewWindow2;
    }

    protected override void OnFormClosing(FormClosingEventArgs e)
    {
        webBrowser1.NewWindow2 -= webBrowser_NewWindow2;
        base.OnFormClosing(e);
    }

    void webBrowser_NewWindow2(object sender, WebBrowserNewWindow2EventArgs e)
    {
        var popup = new Form1();
        popup.Show(this);
        e.PpDisp = popup.Browser.ActiveXInstance;
    }
    public WebBrowserNewWindow2 Browser
    {
        get { return webBrowser1; }
    }
}

代码:

using System;
using System.Security.Permissions;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace SHDocVw
{
    public delegate void WebBrowserNewWindow2EventHandler(object sender, WebBrowserNewWindow2EventArgs e);

    public class WebBrowserNewWindow2EventArgs : EventArgs
    {
        public WebBrowserNewWindow2EventArgs(object ppDisp, bool cancel)
        {
            PpDisp = ppDisp;
            Cancel = cancel;
        }

        public object PpDisp { get; set; }
        public bool Cancel { get; set; }
    }

    public class WebBrowserNewWindow2 : WebBrowser
    {
        private AxHost.ConnectionPointCookie _cookie;
        private WebBrowser2EventHelper _helper;

        [PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")]
        protected override void CreateSink()
        {
            base.CreateSink();

            _helper = new WebBrowser2EventHelper(this);
            _cookie = new AxHost.ConnectionPointCookie(
                this.ActiveXInstance, _helper, typeof(DWebBrowserEvents2));
        }

        [PermissionSetAttribute(SecurityAction.LinkDemand, Name = "FullTrust")]
        protected override void DetachSink()
        {
            if (_cookie != null)
            {
                _cookie.Disconnect();
                _cookie = null;
            }
            base.DetachSink();
        }

        public event WebBrowserNewWindow2EventHandler NewWindow2;

        private class WebBrowser2EventHelper : StandardOleMarshalObject, DWebBrowserEvents2
        {
            private readonly WebBrowserNewWindow2 _parent;

            public WebBrowser2EventHelper(WebBrowserNewWindow2 parent)
            {
                _parent = parent;
            }

            public void NewWindow2(ref object pDisp, ref bool cancel)
            {
                WebBrowserNewWindow2EventArgs arg = new WebBrowserNewWindow2EventArgs(pDisp, cancel);
                _parent.NewWindow2(this, arg);
                if (pDisp != arg.PpDisp)
                    pDisp = arg.PpDisp;
                if (cancel != arg.Cancel)
                    cancel = arg.Cancel;
            }
        }

        [ComImport, Guid("34A715A0-6587-11D0-924A-0020AFC7AC4D"),
        InterfaceType(ComInterfaceType.InterfaceIsIDispatch),
        TypeLibType(TypeLibTypeFlags.FHidden)]
        public interface DWebBrowserEvents2
        {
            [DispId(0xfb)]
            void NewWindow2(
                [In, Out, MarshalAs(UnmanagedType.IDispatch)] ref object ppDisp,
                [In, Out] ref bool cancel);
        }
    }
}

0

我知道这个问题很老了,但我是这样解决的:添加新引用,在COM中选择Microsoft Internet Controls,在代码中,在打开新窗口的点击之前添加以下内容:

SHDocVw.WebBrowser_V1 axBrowser = (SHDocVw.WebBrowser_V1)webBrowser1.ActiveXInstance;
axBrowser.NewWindow += axBrowser_NewWindow;

然后添加以下方法:

void axBrowser_NewWindow(string URL, int Flags, string TargetFrameName, ref object PostData, string Headers, ref bool Processed)
{
    Processed = true;
    webBrowser1.Navigate(URL);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接