本指南面向正在评估从 WebView2 迁移至 DotNetBrowser 的 .NET 开发者。这是由两部分组成的系列文章的第一部分。
本指南的每一节都侧重于嵌入 WebView2
控件的应用程序中常见的一个功能领域。
我们将提供现有代码的直接替换,重点介绍 DotNetBrowser 和 WebView2 之间的细微差别,并提供相关文档的实用链接。
本指南主要介绍如何迁移至 DotNetBrowser。如需了解为何要迁移,请查看 DotNetBrowser 与 WebView2 的对比文章。
安装依赖项
将 DotNetBrowser 添加到项目最简便的方式是通过 NuGet。安装与应用程序匹配的特定于 UI 的包,NuGet 将自动引入所需的 核心 API 包和平台 Chromium 二进制文件。
例如,以下是如何为在 Windows x64 上运行的 WPF 项目添加 DotNetBrowser:
-
DotNetBrowser.Wpf.x64
– WPF 集成。 -
(传递引用)
DotNetBrowser.Win-x64
– 核心 API。 -
(传递引用)
DotNetBrowser.Chromium.Win-x64
– 捆绑的 Chromium。
您可以在安装指南中查看适用于不同 UI 库和平台的完整 NuGet 包列表。
若不使用 NuGet,您可以将 DotNetBrowser 作为 DLL 文件或 VSIX 包添加到项目中。
初始化浏览器
初始化 WebView2 和 DotNetBrowser 在概念上是相似的。在基于 XAML 的库中,您需要在 XAML 中添加浏览器控件,然后在代码后台文件中初始化 Chromium engine。
MainWindow.xaml:
WebView2
<Window ...
x:Class="WebView2Example.MainWindow"
xmlns:local="clr-namespace:WebView2Example"
Title="WebView2" Width="800" Height="600" Closed="Window_Closed">
<Grid>
<Wpf:WebView2 Name="webView"/>
</Grid>
</Window>
DotNetBrowser
<Window ...
x:Class="DotNetBrowserExample.MainWindow"
xmlns:local="clr-namespace:DotNetBrowserExample"
Title="DotNetBrowser" Width="800" Height="600" Closed="Window_Closed">
<Grid>
<WPF:BrowserView Name="browserView"/>
</Grid>
</Window>
MainWindow.xaml.cs:
WebView2
public partial class MainWindow : Window
{
private const string Url = "https://example.com/";
public MainWindow()
{
InitializeComponent();
InitializeWebViewAsync();
}
async void InitializeWebViewAsync()
{
await webView.EnsureCoreWebView2Async(null);
webView.CoreWebView2.Navigate(Url);
}
private void Window_Closed(object sender, EventArgs e)
{
webView.Dispose();
}
}
DotNetBrowser
public partial class MainWindow : Window
{
private const string Url = "https://example.com/";
private readonly IBrowser browser;
private readonly IEngine engine;
public MainWindow()
{
IEngine engine = EngineFactory.Create(RenderingMode.HardwareAccelerated);
browser = engine.CreateBrowser();
InitializeComponent();
browserView.InitializeFrom(browser);
browser.Navigation.LoadUrl(Url);
}
private void Window_Closed(object sender, EventArgs e)
{
engine?.Dispose();
}
}
在 WebView2 中,Engine 基本是隐藏的。您可以在 XAML 中放置一个 WebView2
控件,并调用 EnsureCoreWebView2Async()
在后台启动运行时。初始化完成后,您可以使用 CoreWebView2
进行导航或与页面交互。
在 DotNetBrowser 中,Engine 是显式的。您可以创建一个 IEngine
对象,然后创建 IBrowser
,最后将浏览器连接到 BrowserView
UI 控件。
这种分离使您可以更好地控制浏览器生命周期、资源使用和会话隔离。您可以在一个应用程序中创建多个 Engine,并在每个 Engine 中创建多个 Browser。IBrowser
实例从一开始就功能齐全,它们可以在无头模式下工作。
关闭浏览器
WebView2 将 Edge 的生命周期与最后一个活跃控件绑定;一旦所有 WebView2 实例关闭,整个 Engine 就会退出。
DotNetBrowser 允许您关闭单个 Browser 或整个 Engine。请注意,Engine 的生命周期独立于单个 Browser:您始终需要显式关闭 IEngine
。
WebView2
// 关闭单个 WebView2 控件。
webView.Dispose();
// 一旦所有控件都关闭,Edge 进程就会退出。
DotNetBrowser
// 关闭单个 Browser。
browser.Dispose();
browser.Disposed += (sender, args) =>
{
Console.WriteLine("The browser was closed!");
};
// 关闭单个 Engine。它将自动关闭其中的所有 Browser。
//
// 它不会关闭其他 IEngine 实例。
engine.Dispose();
engine.Disposed += (sender, args) =>
{
Console.WriteLine("The engine was closed!");
};
请注意,browser.Disposed
事件与 WebView2 的 WindowCloseRequested
事件不等效。后者仅在 JavaScript 在顶层视图上调用 window.close()
时才会引发。前者始终在 Browser 被处置时引发,无论处置方式如何。
导航
WebView2 和 DotNetBrowser 中的导航 API 基本等效:
WebView2
webView.CoreWebView2.Navigate("https://www.example.com");
webView.CoreWebView2.GoBack();
webView.CoreWebView2.GoForward();
webView.CoreWebView2.Reload();
webView.CoreWebView2.Stop();
bool canGoBack = webView.CoreWebView2.CanGoBack;
bool canGoForward = webView.CoreWebView2.CanGoForward;
DotNetBrowser
browser.Navigation.LoadUrl("https://example.com");
browser.Navigation.GoBack();
browser.Navigation.GoForward();
browser.Navigation.Reload();
browser.Navigation.Stop();
bool canGoBack = browser.Navigation.CanGoBack;
bool canGoForward = browser.Navigation.CanGoForward;
导航事件
本节介绍了 DotNetBrowser 中与 WebView2 导航事件最接近的等效项。在多个情况下,您需要组合多个事件或处理程序来完全复制 WebView2 语义。
将 NavigationStarting
替换为 NavigationStarted
以检测导航何时开始,并使用 StartNavigationHandler
取消导航。
WebView2
// 当 Browser 开始加载新页面时触发。
webView.CoreWebView2.NavigationStarting += (sender, args) =>
{
Console.WriteLine("Navigation started.");
// 取消导航。
args.Cancel = true;
};
DotNetBrowser
// 当 Browser 开始加载新页面时触发。
browser.Navigation.NavigationStarted += (sender, args) =>
{
Console.WriteLine("Navigation started.");
};
// 取消导航。
browser.Navigation.StartNavigationHandler =
new Handler<
StartNavigationParameters,
StartNavigationResponse>
(p => StartNavigationResponse.Ignore());
将 ContentLoading
替换为 LoadStarted
以检测加载何时开始,并使用 FrameLoadFailed
来处理错误。
WebView2
webView.CoreWebView2.ContentLoading += (sender, args) =>
{
Console.WriteLine("Loading started.");
// 如果加载的内容是错误页面,则返回 True。
bool isErrorPage = args.IsErrorPage;
};
DotNetBrowser
browser.Navigation.LoadStarted += (sender, args) =>
{
Console.WriteLine("Loading started.");
};
// 内容加载失败时触发。
browser.Navigation.FrameLoadFailed += (sender, args) =>
{
NetError errorCode = args.ErrorCode;
};
将 NavigationCompleted
事件替换为 NavigationFinished
事件:
WebView2
webView.CoreWebView2.NavigationCompleted += (sender, args) =>
{
var webErrorStatus = args.WebErrorStatus;
var isSuccess = args.IsSuccess;
};
DotNetBrowser
browser.Navigation.NavigationFinished += (sender, args) =>
{
// 如果导航失败,则为错误代码。
var errorCode = args.ErrorCode;
// 指示导航是否导致错误页面。
var isErrorPage = args.IsErrorPage;
};
将 FrameCreated
替换为同名事件 FrameCreated
。您可以使用此事件缓存 IFrame
实例。但是,JavaScript 上下文和 DOM 树可能无法立即使用。使用 FrameDocumentLoadFinished
和 InjectJsHandler
来确定页面何时准备好进行交互。
WebView2
webView.CoreWebView2.FrameCreated += (sender, args) =>
{
CoreWebView2Frame frame = args.Frame;
};
DotNetBrowser
browser.FrameCreated += (sender, args) =>
{
IFrame frame = args.Frame;
};
将 FrameNavigationCompleted
替换为 FrameLoadFinished
和 FrameLoadFailed
的组合:
WebView2
// 当子框架触发 body.onload 或加载因错误终止时触发。
webView.CoreWebView2.FrameNavigationCompleted += (sender, args) =>
{
CoreWebView2WebErrorStatus errorStatus = args.WebErrorStatus;
int httpStatusCode = args.HttpStatusCode;
bool isSuccess = args.IsSuccess;
};
DotNetBrowser
browser.Navigation.FrameLoadFinished += (sender, args) =>
{
IFrame frame = args.Frame;
string validatedUrl = args.ValidatedUrl;
};
browser.Navigation.FrameLoadFailed += (sender, args) =>
{
IFrame frame = args.Frame;
NetError errorCode = args.ErrorCode;
string validatedUrl = args.ValidatedUrl;
};
SourceChanged
事件在 DotNetBrowser 中没有等效项。 最接近的替代方案是带有条件的 NavigationStarted
事件:
WebView2
webView.CoreWebView2.SourceChanged += (sender, args) =>
{
var isNewDocument = args.IsNewDocument;
};
DotNetBrowser
browser.Navigation.NavigationStarted += (sender, args) =>
{
if (args.IsMainFrame)
{
var isNewDocument = !args.IsSameDocument;
}
};
访问导航指南,查看 DotNetBrowser 中导航事件的完整列表。
使用 POST 数据加载
在 WebView2 和 DotNetBrowser 中,您都可以使用 POST 请求导航到 URL:
WebView2
var postData = "username=test&password=1234";
var byteArray = Encoding.UTF8.GetBytes(postData);
var stream = new MemoryStream(byteArray);
var request = webView.CoreWebView2.Environment.CreateWebResourceRequest(
uri: "https://example.com",
Method: "POST",
postData: stream,
Headers: "Content-Type: application/x-www-form-urlencoded\r\n"
);
webView.CoreWebView2.NavigateWithWebResourceRequest(request);
DotNetBrowser
KeyValuePair<string, string>[] formData =
{
new KeyValuePair<string, string>("username", "test"),
new KeyValuePair<string, string>("password", "1234")
};
LoadUrlParameters request = new LoadUrlParameters("https://example.com")
{
UploadData = new FormData(formData)
};
browser.Navigation.LoadUrl(request);
发送 POST 数据时,DotNetBrowser 会自动设置 Content-Type
和 Content-Length
标头。对于多部分表单,它还将包含表单边界以及每个部分的 Content-Disposition
和 Content-Type
。
您可以覆盖默认标头,并在 HttpHeaders 参数中添加更多标头。
JavaScript
两个库都允许您执行脚本、接收结果,并允许页面脚本调用 .NET 代码。差异在于类型和连接方式。
何时开始执行 JavaScript
Chromium 完成页面加载后,需要额外时间来创建 JavaScript 上下文和 DOM 树。只有在此步骤完成后,您才能成功执行 JavaScript。
将 WebView 的两个 DOMContentLoaded
事件替换为单个 FrameDocumentLoadFinished
事件,以确保框架已准备好执行 JavaScript:
WebView2
webView.CoreWebView2.DOMContentLoaded += (sender, args) =>
{
Console.WriteLine("Ready to execute JS in the main frame.");
};
webView.CoreWebView2.FrameCreated += (s, e) =>
{
var frame = e.Frame;
frame.DOMContentLoaded += (sender, args) =>
{
Console.WriteLine("Ready to execute JS in an iframe.");
};
};
DotNetBrowser
browser.Navigation.FrameDocumentLoadFinished += (sender, args) =>
{
if (args.Frame.IsMain)
{
Console.WriteLine("准备在主框架中执行 JS。");
}
else
{
Console.WriteLine("准备在 iframe 中执行 JS。");
}
};
如果您想在页面上的任何脚本运行之前执行 JavaScript,您很可能正在使用 AddScriptToExecuteOnDocumentCreatedAsync
方法。DotNetBrowser 中的替代方案是 InjectJsHandler
:
WebView2
webView.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync("...");
DotNetBrowser
browser.InjectJsHandler = new Handler<InjectJsParameters>(args =>
{
args.Frame.ExecuteJavaScript<IJsObject>("...");
});
从 .NET 调用 JavaScript
WebView2 始终返回 JSON 字符串。undefined
和无法序列化的对象等特殊值将作为字面字符串 "null"
返回。
DotNetBrowser 将 JavaScript 值转换为相应的 .NET 类型。基本值自动映射到本地 .NET 类型,而复杂值则包装为 IJsObject
、IJsArray
或 IJsFunction
:
WebView2
// 返回字符串 "4"。
string four = await webView.CoreWebView2.ExecuteScriptAsync("2 + 2");
// 返回字符串 "null",因为 `window` 无法序列化。
string window = await webView.CoreWebView2.ExecuteScriptAsync("window");
// 您也可以异步执行相同的操作:
var result =
await webView.CoreWebView2.ExecuteScriptWithResultAsync("2 + 2");
string fourAsync = scriptResult.ResultAsJson;
DotNetBrowser
// 以双精度值返回数字。
double four = await browser.MainFrame.ExecuteJavaScript<double>("2 + 2");
// 返回函数式 IJsObject 包装器。
IJsObject window = await args.Frame.ExecuteJavaScript<IJsObject>("window");
访问 JavaScript 指南了解更多详情。
从 JavaScript 调用 .NET
WebView2 提供两种通信模式:通过 window.chrome.webview.postMessage()
传递消息,以及通过注入的 COM 可见宿主对象直接调用。
DotNetBrowser 使用直接调用,并允许您将任意 .NET 对象注入 JavaScript。无需 COM。
WebView2
window.chrome.webview.postMessage({action: "hello", name: "John"});
window.chrome.webview.hostObjects.bridge.SayHello("John");
DotNetBrowser
// 您可以模拟注入对象的相同路径:
window.chrome.webview.postMessage({action: "hello", name: "John"});
window.chrome.webview.hostObjects.bridge.SayHello("John");
// 或者将 .NET 对象注入到任意位置:
window.postMessage({action: "hello", name: "John"});
window.myHostObjects.SayHello("John");
如果您使用的是 postMessage()
方法,则可以在 DotNetBrowser 中模拟它,并避免重写现有的 JavaScript 代码。但您需要将 WebMessageReceived
事件处理程序替换为一个对象:
WebView2
webView.CoreWebView2.WebMessageReceived += (s, e) =>
{
var jsonString= e.WebMessageAsJson;
Console.WriteLine($"{jsonString}");
};
DotNetBrowser
public class Bridge
{
public void postMessage(Object message)
{
// 注意,如果底层 JS 对象不是字符串,
// 则 `message` 将不是字符串。
Console.WriteLine($"{message}");
}
}
val bridge = new Bridge();
browser.InjectJsHandler = new Handler<InjectJsParameters>(args =>
{
// 创建一个与 WebView2 中名称和路径相同的 JavaScript 对象。
IJsObject webview = args.Frame.ExecuteJavaScript<IJsObject>(
"(window.chrome.webview = {})");
// 创建 `postMessage` 属性并将其赋值给 .NET 方法。
webview.Properties["postMessage"] = (Action<Object>) bridge.postMessage;
});
同样,您可以用任意 .NET 对象替换注入的 COM 对象:
WebView2
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDual)]
public class Bridge
{
public void SayHello(string name)
{
MessageBox.Show($"Hello {name}!");
}
}
webView.CoreWebView2.AddHostObjectToScript("bridge", new Bridge());
DotNetBrowser
public class Bridge
{
public void SayHello(string name)
{
MessageBox.Show($"Hello {name}!");
}
}
browser.InjectJsHandler = new Handler<InjectJsParameters>(args =>
{
// 创建一个与 WebView2 中名称和路径相同的 JavaScript 对象。
IJsObject webview = args.Frame.ExecuteJavaScript<IJsObject>(
"(window.chrome.webview = {hostObjects: {}})");
IJsObject hosts = webview.Properties["hostObjects"] as IJsObject;
// 创建 `postMessage` 属性并将其赋值给 .NET 方法。
hosts.Properties["bridge"] = new Bridge();
});
查看JavaScript 指南了解更多关于 .NET-JavaScript 桥接的信息。
拦截流量
在 WebView2 中,流量拦截分为两步。首先,注册过滤器来隔离特定请求,然后注册处理程序来处理这些请求:
WebView2
// 仅拦截图片资源的 HTTPS 请求。
webView.CoreWebView2
.AddWebResourceRequestedFilter(
"https://*", CoreWebView2WebResourceContext.Image);
webView.CoreWebView2.WebResourceRequested += (sender, args) =>
{
if (...) {
// 不处理此请求。
return;
}
// 提供您自己的响应。
var response =
webView.CoreWebView2.Environment.CreateWebResourceResponse(...);
args.Response = response;
};
默认情况下,这些过滤器适用于标准方案:http
、https
和 file
。如果您想拦截 custom-scheme://
的请求,则需要在初始化 Engine 时注册自定义方案:
WebView2
var schemes = new List<CoreWebView2CustomSchemeRegistration>()
{
new CoreWebView2CustomSchemeRegistration("custom-scheme")
};
var options = new CoreWebView2EnvironmentOptions(
null, null, null, false, schemes);
var env = await CoreWebView2Environment.CreateAsync(null, null, options);
await webView.EnsureCoreWebView2Async(env);
// 现在,下面这个方法也能正常工作了:
webView.CoreWebView2
.AddWebResourceRequestedFilter("custom-scheme://*", ...);
在 DotNetBrowser 中,无需预先过滤:每个请求都会触发相应协议的 InterceptRequestHandler
。你只需为需要拦截的协议分别注册处理程序:
DotNetBrowser
// 创建带有不同方案的请求处理程序的 Engine。
EngineOptions options = new EngineOptions.Builder
{
Schemes =
{
{Scheme.Create("custom-scheme"), new CustomSchemeInterceptor()},
{Scheme.Create("https"), new HttpsSchemeInterceptor()}
}
}.Build();
var engine = EngineFactory.Create(options);
在此处理程序中,您可以让请求通过,或提供您自己的响应:
DotNetBrowser
class CustomSchemeInterceptor :
IHandler<InterceptRequestParameters, InterceptRequestResponse>
{
public InterceptRequestResponse Handle(InterceptRequestParameters p)
{
string uri = p.UrlRequest.Url;
UrlRequestJob urlRequestJob = null;
if (p.UrlRequest.ResourceType == ResourceType.Image)
{
// 提供您自己的自定义响应。
urlRequestJob =
p.Network.CreateUrlRequestJob(p.UrlRequest,
new UrlRequestJobOptions
{
HttpStatusCode = HttpStatusCode.OK,
Headers = new List<HttpHeader>
{
new HttpHeader("Content-Type", "text/plain"),
}
});
byte[] body = loadImageFromDisk(p.UrlRequest.Url);
urlRequestJob.Write(body);
urlRequestJob.Complete();
return InterceptRequestResponse.Intercept(urlRequestJob);
}
// 让其他请求通过。
return InterceptRequestResponse.Proceed();
}
}
请求拦截会产生性能开销。如果您只需要阻止不需要的请求,请使用更轻量级的 SendUrlRequestHandler
。
有关处理自定义方案的更多信息,请参阅拦截请求指南。
DOM
WebView2 不提供原生 DOM API;要与页面内容交互,只能执行 JavaScript 并解析返回的 JSON 结果。
DotNetBrowser 提供两种选项:您可以继续使用现有的 JavaScript 片段,或切换到类型化的 DOM API,该 API 将元素作为 .NET 对象公开。
WebView2 代码的直接替换代码看起来非常相似:
WebView2
string jsCode = "document.getElementById('username').value";
string username = await webView.CoreWebView2.ExecuteScriptAsync(jsCode);
DotNetBrowser
string jsCode = "document.getElementById('username').value";
string username = await browser.MainFrame.ExecuteJavaScript<string>(jsCode);
Alternatively, you can use the DotNetBrowser DOM API:
DotNetBrowser
// 重用现有的 JavaScript 选择器。
IElement element = await frame.ExecuteJavaScript<IElement>(
"document.getElementById('username')");
string username = (element as IInputElement).Value;
// 或者使用 C# 选择器。
IDocument document = browser.MainFrame.Document;
IInputElement inputElement =
document.GetElementById("username") as IInputElement;
string username = inputElement.Value;
为什么要使用 DOM API?它带来更友好的 IntelliSense、支持编译期重构,并提供强类型的 C# 体验。更多详情请参见 DOM 指南。
JavaScript 对话框
在 WebView2 中,您可以通过单个 ScriptDialogOpening
事件处理程序来处理所有 JavaScript 对话框。而在 DotNetBrowser 中,您需要为每种对话框类型分别设置独立的处理程序来替代这一方式。
WebView2
webView.CoreWebView2.ScriptDialogOpening += (sender, args) =>
{
switch (args.Kind)
{
case CoreWebView2ScriptDialogKind.Alert:
Console.WriteLine("Alert dialog called.");
break;
case CoreWebView2ScriptDialogKind.Confirm:
Console.WriteLine("Confirm dialog called.");
args.Accept();
break;
case CoreWebView2ScriptDialogKind.Prompt:
Console.WriteLine("Prompt dialog called.");
args.ResultText = "some response";
args.Accept();
break;
case CoreWebView2ScriptDialogKind.Beforeunload:
Console.WriteLine("Beforeunload dialog called.");
break;
}
};
DotNetBrowser
var dialogs = browser.JsDialogs;
dialogs.AlertHandler = new Handler<AlertParameters>(p =>
{
Console.WriteLine("Alert dialog called.");
});
dialogs.ConfirmHandler =
new Handler<ConfirmParameters, ConfirmResponse>(p =>
{
Console.WriteLine("Confirm dialog called.");
return ConfirmResponse.Ok();
});
dialogs.PromptHandler =
new Handler<PromptParameters, PromptResponse>(p =>
{
Console.WriteLine("Prompt dialog called.");
return PromptResponse.SubmitText("some response");
});
dialogs.BeforeUnloadHandler =
new Handler<BeforeUnloadParameters, BeforeUnloadResponse>(p =>
{
Console.WriteLine("Beforeunload dialog called.");
return BeforeUnloadResponse.Leave();
});
如果您未设置处理程序,DotNetBrowser 会自动处理这些对话框。在 BrowserView
内部,它会使用应用程序的 UI 库来呈现对话框。在该上下文之外,它会跳过这些对话框,就像用户按下了“取消”按钮一样。
原生对话框
原生对话框由浏览器操作触发,例如文件上传按钮、PDF 保存提示、表单重新提交警告、外部应用启动触发器、<input type="color">
颜色选择器等。
在 WebView2 中,覆盖原生对话框需要采用一些临时性的变通方法。
而在 DotNetBrowser 中,您可以通过注册相应的事件处理程序来覆盖任意原生对话框:
-
OpenDirectoryHandler
用于“打开目录”对话框。 -
OpenFileHandler
用于“打开文件”对话框。 -
OpenMultipleFilesHandler
用于“打开多个文件”对话框。 -
OpenExternalAppHandler
用于浏览器尝试打开外部应用时触发的对话框。 -
RepostFormHandler
用于“确认表单重新提交”对话框。 -
SaveAsPdfHandler
用于在通过“打印预览”对话框将页面保存为 PDF 时选择文件。 -
SaveFileHandler
用于“保存文件”对话框。 -
SelectColorHandler
用于颜色选择器。
以下示例演示如何重写 OpenFileHandler
:
DotNetBrowser
browser.Dialogs.OpenFileHandler =
new Handler<OpenFileParameters, OpenFileResponse>(parameters =>
{
var initialDirectory = parameters.InitialDirectory;
var acceptableExtensions = parameters.AcceptableExtensions;
return OpenFileResponse.SelectFile(...);
});
更多示例请参考对话框指南。
弹出窗口
当 JavaScript 调用 window.open 或用户点击带有 target="_blank"
的链接时,浏览器会创建一个弹出窗口。
在 WebView2 中,弹出窗口的创建通过 NewWindowRequested
事件处理程序进行控制。您可以选择阻止弹出窗口、在当前 WebView2 控件中打开它,或允许其创建新窗口。
在 DotNetBrowser 中,弹出窗口的创建由 CreatePopupHandler
管理,您可以在其中决定是否允许或隐藏弹出窗口。如果您决定创建弹出窗口,DotNetBrowser 会实例化一个新的 IBrowser
并将其传递给 OpenPopupHandler
。然后,您可以使用 BrowserView
在 UI 中显示它,也可以在无头模式下运行它。
虽然不直接支持在同一个 BrowserView
控件中显示新浏览器,但可以通过以下方式模拟实现:
WebView2
webView.CoreWebView2.NewWindowRequested += (sender, e) =>
{
if (openInSameBrowser)
{
e.NewWindow = (CoreWebView2)sender;
}
else
{
// 阻止默认弹出行为。
e.Handled = true;
// 创建一个新的弹出窗口。
CreatePopup(e);
}
};
private async void CreatePopup(CoreWebView2NewWindowRequestedEventArgs popup)
{
//创建并配置新窗口。
Window popupWindow = new Window();
...
// 创建一个新的 WebView2 控件来显示新的浏览器。
WebView2 popupWebView = new WebView2();
popupWindow.Content = popupWebView;
popupWindow.Show();
CoreWebView2Environment env = webView.CoreWebView2.Environment;
await popupWebView.EnsureCoreWebView2Async(env);
popupWebView.CoreWebView2.Navigate(popup.Uri);
}
DotNetBrowser
browser.CreatePopupHandler =
new Handler<CreatePopupParameters, CreatePopupResponse>(p =>
{
if (openInSameBrowser)
{
browser.Navigation.LoadUrl(p.TargetUrl);
return CreatePopupResponse.Suppress();
}
return CreatePopupResponse.Create();
});
browser.OpenPopupHandler = new Handler<OpenPopupParameters>(p =>
{
Action createPopupAction = () =>
{
CreatePopupWindow(p.PopupBrowser, p.Rectangle);
};
Dispatcher.BeginInvoke(createPopupAction);
});
void CreatePopupWindow(IBrowser popupBrowser, Rectangle rect)
{
BrowserView popupBrowserView = new BrowserView();
popupBrowserView.InitializeFrom(popupBrowser);
Dispatcher.UIThread.InvokeAsync(() =>
{
// Create and configure the new window.
Window popupWindow = new Window();
window.Content = browserView;
popupWindow.Show();
...
});
}
如果未配置处理程序,DotNetBrowser 会自动管理弹出窗口。当位于 BrowserView
中时,它将使用应用程序的 UI 库打开一个新窗口,否则会隐藏对话框。
总结
在本指南中,我们介绍了如何创建和关闭浏览器、导航页面、与 JavaScript 交互、处理对话框和弹出窗口,以及拦截网络流量。
在即将发布的第二部分将继续讲解从 WebView2 迁移至 DotNetBrowser 的进阶主题,包括:
-
代理与身份验证流程
-
Cookie 和持久化存储
-
Profiles(配置文件)
-
上下文菜单
-
下载
-
打印 API
-
DevTools 集成
-
Chrome 扩展程序