如何从 CefSharp 3 在本机浏览器中打开 link
How to open a link in a native browser from CefSharp 3
我需要在 CefSharp 3 的本机浏览器中打开 link。我需要 运行 在 CefSharp 3 的 chromium 浏览器中打开整个应用程序,除了一个表单。当我单击表单的 link 按钮时(例如 - 注册按钮。它有一个 link 到注册表)我需要在本机浏览器中打开此 link (例如 - Internet Explorer) .
我们可以在 CefSharp 中实现吗?
我搜索了 google 以及堆栈溢出。但是找不到解决方法。
似乎可以通过使用 OnBeforeNavigation 或 OnBeforeBrowse 事件来实现。请参阅 "CEF Forum" 中的以下参考资料:
- How to have link open in user's default browser
- CEF 3 Open all link targets externally
- Open external browser from link
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
}
免责声明:我自己还没有尝试过这段代码,但它应该可以解决问题
按照 holroy 的建议,我在 CefSharp.Example 包中的 RequestHandler class 中实现了 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;
}
}
我希望这对以后的其他人有所帮助。
谢谢,
首先,您需要创建一个自定义 BrowserRequestHandler
class,例如:
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
并将其分配给浏览器控件的 RequestHandler
依赖项 属性。但就我而言,存在时间问题,因为我在动态打开选项卡时有多个浏览器实例,而浏览器打开速度不够快并引发错误。
因此第二个选项是使用交互命名空间设置一个 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>
在此之后,您可以在您的视图模型中获取浏览器(或在管理器 class 中执行此操作),并将其 RequestHandler
设置为您的自定义 BrowserRequestHandler
,如下所示:
browser.RequestHandler = new BrowserRequestHandler();
我尝试按照建议使用 RequestHandler,但我发现虽然 link 在浏览器中正确打开,但 ChromiumWebBrowser 仍在打开一个空的 Chromium window。我发现使用 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>
更正了 Chaya 答案中的一个小错误(对我也有用)。 x:Key="" 缺少 L。
<Grid>
<Grid.Resources>
<local:ChromiumLifeSpanHandler x:Key="popupHandler"/>
</Grid.Resources>
<cef:ChromiumWebBrowser Address="{Binding TwitterFeedAddress}"
LifeSpanHandler="{StaticResource popupHandler}"/>
</Grid>
我需要在 CefSharp 3 的本机浏览器中打开 link。我需要 运行 在 CefSharp 3 的 chromium 浏览器中打开整个应用程序,除了一个表单。当我单击表单的 link 按钮时(例如 - 注册按钮。它有一个 link 到注册表)我需要在本机浏览器中打开此 link (例如 - Internet Explorer) .
我们可以在 CefSharp 中实现吗?
我搜索了 google 以及堆栈溢出。但是找不到解决方法。
似乎可以通过使用 OnBeforeNavigation 或 OnBeforeBrowse 事件来实现。请参阅 "CEF Forum" 中的以下参考资料:
- How to have link open in user's default browser
- CEF 3 Open all link targets externally
- Open external browser from link
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
}
免责声明:我自己还没有尝试过这段代码,但它应该可以解决问题
按照 holroy 的建议,我在 CefSharp.Example 包中的 RequestHandler class 中实现了 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;
}
}
我希望这对以后的其他人有所帮助。
谢谢,
首先,您需要创建一个自定义 BrowserRequestHandler
class,例如:
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
并将其分配给浏览器控件的 RequestHandler
依赖项 属性。但就我而言,存在时间问题,因为我在动态打开选项卡时有多个浏览器实例,而浏览器打开速度不够快并引发错误。
因此第二个选项是使用交互命名空间设置一个 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>
在此之后,您可以在您的视图模型中获取浏览器(或在管理器 class 中执行此操作),并将其 RequestHandler
设置为您的自定义 BrowserRequestHandler
,如下所示:
browser.RequestHandler = new BrowserRequestHandler();
我尝试按照建议使用 RequestHandler,但我发现虽然 link 在浏览器中正确打开,但 ChromiumWebBrowser 仍在打开一个空的 Chromium window。我发现使用 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>
更正了 Chaya 答案中的一个小错误(对我也有用)。 x:Key="" 缺少 L。
<Grid>
<Grid.Resources>
<local:ChromiumLifeSpanHandler x:Key="popupHandler"/>
</Grid.Resources>
<cef:ChromiumWebBrowser Address="{Binding TwitterFeedAddress}"
LifeSpanHandler="{StaticResource popupHandler}"/>
</Grid>