【问题标题】:How to open a link in a native browser from CefSharp 3如何从 CefSharp 3 在本机浏览器中打开链接
【发布时间】:2015-09-23 07:52:12
【问题描述】:

我需要在 CefSharp 3 的本机浏览器中打开链接。我需要在 CefSharp 3 的 chromium 浏览器中运行整个应用程序,但表单除外。当我单击表单的链接按钮(例如 - 注册按钮。它有一个注册表单的链接)时,我需要在本机浏览器中打开此链接(例如 - Internet Explorer)。

我们可以在 CefSharp 中实现这一点吗?

我搜索了谷歌以及堆栈溢出。但找不到解决办法。

【问题讨论】:

    标签: c# .net wpf cefsharp


    【解决方案1】:

    根据 holroy 的建议,我已经在 CefSharp.Example 包的 RequestHandler 类中实现了 OnBeforeNavigation() 方法。

    这是工作代码,

     bool IRequestHandler.OnBeforeBrowse(IWebBrowser browserControl,
     IBrowser browser, IFrame frame, IRequest request, bool isRedirect)
             {
                 // If the url is Google open Default browser
                 if (request.Url.Equals("http://google.com/"))
                 {
                     // Open Google in Default browser 
                     System.Diagnostics.Process.Start("http://google.com/");
                     return true;
                 }else
                 {
                     // Url except Google open in CefSharp's Chromium browser
                     return false;
                 }
             }
    

    我希望这将对其他人有所帮助。

    谢谢,

    【讨论】:

    • 注意:函数签名已更改。在bool isRedirect 之前有一个bool userGesture
    【解决方案2】:

    首先您需要创建一个自定义的BrowserRequestHandler 类,例如:

    public class BrowserRequestHandler : IRequestHandler
    {
        public bool OnBeforeBrowse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, bool isRedirect)
        {
            // Open in Default browser
            if (!request.Url.StartsWith("file:"))
            {
                System.Diagnostics.Process.Start(request.Url);
                return true;
            }
            return false;
        }
    
        public bool OnOpenUrlFromTab(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl,
            WindowOpenDisposition targetDisposition, bool userGesture)
        {
            return false;
        }
    
        public bool OnCertificateError(IWebBrowser browserControl, IBrowser browser, CefErrorCode errorCode, string requestUrl,
            ISslInfo sslInfo, IRequestCallback callback)
        {
            callback.Dispose();
            return false;
        }
    
        public void OnPluginCrashed(IWebBrowser browserControl, IBrowser browser, string pluginPath)
        {
            throw new Exception("Plugin crashed!");
        }
    
        public CefReturnValue OnBeforeResourceLoad(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request,
            IRequestCallback callback)
        {
            return CefReturnValue.Continue;
        }
    
        public bool GetAuthCredentials(IWebBrowser browserControl, IBrowser browser, IFrame frame, bool isProxy, string host, int port,
            string realm, string scheme, IAuthCallback callback)
        {
            callback.Dispose();
            return false;
        }
    
        public bool OnSelectClientCertificate(IWebBrowser browserControl, IBrowser browser, bool isProxy, string host, int port,
            X509Certificate2Collection certificates, ISelectClientCertificateCallback callback)
        {
            callback.Dispose();
            return false;
        }
    
        public void OnRenderProcessTerminated(IWebBrowser browserControl, IBrowser browser, CefTerminationStatus status)
        {
            throw new Exception("Browser render process is terminated!");
        }
    
        public bool OnQuotaRequest(IWebBrowser browserControl, IBrowser browser, string originUrl, long newSize,
            IRequestCallback callback)
        {
            callback.Dispose();
            return false;
        }
    
        public void OnResourceRedirect(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response,
            ref string newUrl)
        {
            var url = newUrl;
            newUrl = url;
        }
    
        public bool OnProtocolExecution(IWebBrowser browserControl, IBrowser browser, string url)
        {
            return url.StartsWith("mailto");
        }
    
        public void OnRenderViewReady(IWebBrowser browserControl, IBrowser browser)
        {
    
        }
    
        public bool OnResourceResponse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response)
        {
            return false;
        }
    
        public IResponseFilter GetResourceResponseFilter(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request,
            IResponse response)
        {
            return null;
        }
    
        public void OnResourceLoadComplete(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request,
            IResponse response, UrlRequestStatus status, long receivedContentLength)
        {
    
        }
    }
    

    这段代码的重要部分是:

    public bool OnBeforeBrowse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, bool isRedirect)
    {
        // Open in Default browser
        if (!request.Url.StartsWith("file:"))
        {
            System.Diagnostics.Process.Start(request.Url);
            return true;
        }
        return false;
    }
    

    在我的例子中,我在 CEF 中打开本地保存的 HTML 文件,如果这些本地保存的 HTML 文件有外部链接,它们会在默认浏览器中打开。

    现在您已经创建了自定义BrowserRequestHandler,您需要将其分配给浏览器。

    如果您使用的是 MVVM,您可以在模型中创建一个BrowserRequestHandler,并将其分配给浏览器控件的RequestHandlerdependency 属性。但就我而言,存在时间问题,因为我有几个浏览器实例在动态打开选项卡,并且浏览器打开速度不够快并引发错误。

    因此,第二个选项是使用交互命名空间设置 Loaded 事件,如下所示:

    <wpf:ChromiumWebBrowser Address="{Binding Html, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" x:Name="ChromiumWebBrowser">
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="Loaded">
                <i:InvokeCommandAction Command="{Binding BrowserLoadedCommand}"/>
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </wpf:ChromiumWebBrowser>
    

    之后,您可以在视图模型中获取浏览器(或在管理器类中执行此操作),并将其 RequestHandler 设置为您的自定义 BrowserRequestHandler,如下所示:

    browser.RequestHandler = new BrowserRequestHandler();
    

    【讨论】:

    • 您可以通过从 DefaultRequestHandler 派生 BrowserRequestHandler 来跳过编写除 OnBeforeBrowse() {...} 方法之外的所有方法。它将提供所有样板
    【解决方案3】:

    似乎可以通过使用 OnBeforeNavigation 或 OnBeforeBrowse 事件来实现。请参阅“CEF 论坛”的以下参考资料:

    OnBeforeNavigation 方法的建议实现(来自Sending information from Chromium Embeded (Javascript) to a containing C++ application):

        //declare (i.e. in header) 
        virtual bool OnBeforeNavigation(CefRefPtr<CefBrowser> browser, 
            CefRefPtr<CefFrame> frame, CefRefPtr<CefRequest> request, 
            NavigationType navigation_type, bool is_redirect)  OVERRIDE; 
    
        //implementation 
        bool CClientApp::OnBeforeNavigation(CefRefPtr<CefBrowser> browser, 
            CefRefPtr<CefFrame> frame, CefRefPtr<CefRequest> request, 
            NavigationType navigation_type, bool is_redirect)
        {
            CefString cefval = request->GetURL(); 
            CString csval = cefval.c_str(); 
    
            if ( /* Match csval against your url/link */ )
            {
                //process the command here 
    
                //this is a command and not really intended for navigation 
                return true; 
            }
    
            return false; //true cancels navigation, false allows it 
        }
    

    免责声明:我自己没有尝试过这段代码,但它应该可以解决问题

    【讨论】:

    【解决方案4】:

    我尝试按照建议使用 RequestHandler,但是我发现当链接在浏览器中正确打开时,ChromiumWebBrowser 仍在打开一个空的 Chromium 窗口。我发现使用 ILifeSpanHandler 可以更顺利地实现这一点,而不是使用 RequestHandler,这让我可以捕捉弹出窗口的打开并在那时重定向进程。

    这是我用于 ILifeSpanHandler 的代码:

    public class ChromiumLifeSpanHandler : ILifeSpanHandler
        {
            public bool DoClose(IWebBrowser chromiumWebBrowser, IBrowser browser)
            {
                return false;
            }
    
            public void OnAfterCreated(IWebBrowser chromiumWebBrowser, IBrowser browser)
            {
    
            }
    
            public void OnBeforeClose(IWebBrowser chromiumWebBrowser, IBrowser browser)
            {
    
            }
    
            public bool OnBeforePopup(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, string targetUrl, string targetFrameName, WindowOpenDisposition targetDisposition, bool userGesture, IPopupFeatures popupFeatures, IWindowInfo windowInfo, IBrowserSettings browserSettings, ref bool noJavascriptAccess, out IWebBrowser newBrowser)
            {
                newBrowser = null;
                System.Diagnostics.Process.Start(targetUrl);
                return true;
            }
        }
    

    这是我在 WPF 中实现它的方式(我只是把它放在一个网格中以便有一个地方显示静态资源):

    <Grid>
        <Grid.Resources>
            <local:ChromiumLifeSpanHandler x:Key="popupHandler"/>
        </Grid.Resources>
        <cef:ChromiumWebBrowser Address="{Binding TwitterFeedAddress}" 
                                LifeSpanHandler="{StaticResource popupHandler}"/>
    </Grid>
    

    【讨论】:

    • 简单漂亮,谢谢!
    【解决方案5】:

    更正 Chaya 答案中的一个小类型(这也对我有用)。 x:Key="" 缺少 L。

    <Grid>
        <Grid.Resources>
            <local:ChromiumLifeSpanHandler x:Key="popupHandler"/>
        </Grid.Resources>
        <cef:ChromiumWebBrowser Address="{Binding TwitterFeedAddress}" 
                                LifeSpanHandler="{StaticResource popupHandler}"/>
    </Grid>
    

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-08-11
    • 1970-01-01
    • 2012-02-21
    • 1970-01-01
    • 1970-01-01
    • 2018-10-04
    • 2014-03-12
    相关资源
    最近更新 更多