从 WebView2 迁移至 DotNetBrowser:第一部分

本指南面向正在评估从 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 树可能无法立即使用。使用 FrameDocumentLoadFinishedInjectJsHandler 来确定页面何时准备好进行交互

WebView2

webView.CoreWebView2.FrameCreated += (sender, args) =>
{
    CoreWebView2Frame frame = args.Frame;
};

DotNetBrowser

browser.FrameCreated += (sender, args) =>
{
    IFrame frame = args.Frame;
};

FrameNavigationCompleted 替换为 FrameLoadFinishedFrameLoadFailed 的组合:

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-TypeContent-Length 标头。对于多部分表单,它还将包含表单边界以及每个部分的 Content-DispositionContent-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 类型,而复杂值则包装为 IJsObjectIJsArrayIJsFunction

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;
};

默认情况下,这些过滤器适用于标准方案:httphttpsfile。如果您想拦截 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 扩展程序

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值