WebView2教程(基于C++)【二】第一个窗口

上一篇文章中,我们创建了一个窗口,但并没有介绍窗口类Win的代码,下面就是Win的头文件:

#pragma once
#include <rapidjson/document.h>
#include <Windows.h>
#include <wrl.h>
#include <wil/com.h>
#include <WebView2.h>
#include <vector>
#include "HostImpl.h"

class Win
{
public:
	Win(rapidjson::Value& config);
	~Win();
	int x, y, w, h;
	HWND hwnd;
private:
	static LRESULT CALLBACK RouteWindowMessage(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
	LRESULT CALLBACK wndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
	void initSizeAndPos();
	void initWindow();
	bool createPageController();
	HRESULT pageCtrlCallBack(HRESULT result, ICoreWebView2Controller* controller);
	rapidjson::Value& config;
	std::vector<wil::com_ptr<ICoreWebView2Controller>> ctrls;
	std::vector<wil::com_ptr<ICoreWebView2>> webviews;
	wil::com_ptr<Host> hostObj;
};

下面时窗口类的构造函数和第一个执行的方法:

#include "Win.h"
#include "Util.h"
#include <format>
#include <windowsx.h>
#include <dwmapi.h>
#include <WebView2.h>
#include "App.h"

using namespace Microsoft::WRL;

Win::Win(rapidjson::Value& config):config{config}
{
    initSizeAndPos();
    initWindow();
    createPageController();
}
void Win::initSizeAndPos()
{
    w = config["w"].GetInt();
    h = config["h"].GetInt();
    auto centerOfScreen = config["position"]["centerOfScreen"].GetBool();
    if (centerOfScreen) {
        RECT rect;
        SystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0);
        x = (rect.right - w) / 2;
        y = (rect.bottom - h) / 2;
    }
    else {
        x = config["position"]["x"].GetInt();
        y = config["position"]["y"].GetInt();
    }
}

构造函数的输入参数是配置文件config.json中windows数组的一项,你可以在上一篇中会看到config.json的代码

initSizeAndPos方法根据配置文件的信息确定窗口的位置和大小

initWindow方法负责创建窗口,代码如下:

void Win::initWindow()
{
    auto hinstance = GetModuleHandle(NULL);
    static int num = 0;
    WNDCLASSEX wcx{};
    wcx.cbSize = sizeof(wcx);
    wcx.style = CS_HREDRAW | CS_VREDRAW;
    wcx.lpfnWndProc = &Win::RouteWindowMessage;
    wcx.cbWndExtra = sizeof(Win*);
    wcx.hInstance = hinstance;
    wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    auto clsName = std::format(L"wv2js_{}", num++);
    wcx.lpszClassName = clsName.c_str();
    if (!RegisterClassEx(&wcx))
    {
        MessageBox(NULL, L"注册窗口类失败", L"系统提示", NULL);
        return;
    }
    auto title = convertToWideChar(config["title"].GetString());
    hwnd = CreateWindowEx(NULL, wcx.lpszClassName, title.c_str(), WS_VISIBLE | WS_OVERLAPPEDWINDOW| WS_CLIPCHILDREN,
        x, y, w, h, NULL, NULL, hinstance, static_cast<LPVOID>(this));
    if (!config["frame"].GetBool()) {
        if (config["shadow"].GetBool()) {
            DWMNCRENDERINGPOLICY policy = DWMNCRP_ENABLED;
            DwmSetWindowAttribute(hwnd, DWMWA_NCRENDERING_POLICY, &policy, sizeof(policy));
            MARGINS margins = { 1,1,1,1 };
            DwmExtendFrameIntoClientArea(hwnd, &margins);
        }
    }
}

这里需要注意的是,如果配置文件中frame设置成false,则窗口就是无边框窗口。

下面是窗口的消息处理方法:

LRESULT CALLBACK Win::RouteWindowMessage(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    if (msg == WM_NCCREATE)
    {
        CREATESTRUCT* pCS = reinterpret_cast<CREATESTRUCT*>(lParam);
        LPVOID pThis = pCS->lpCreateParams;
        SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pThis));
        auto that = static_cast<Win*>(pThis);
        that->hwnd = hWnd;
    }
    else if (auto obj = reinterpret_cast<Win*>(GetWindowLongPtr(hWnd, GWLP_USERDATA))) {
        return obj->wndProc(hWnd, msg, wParam, lParam);
    }
    return DefWindowProc(hWnd, msg, wParam, lParam);
}

LRESULT CALLBACK Win::wndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_NCCALCSIZE:
    {
        if (config["frame"].GetBool()) {
            return DefWindowProcW(hWnd, msg, wParam, lParam);
        }
        return 0;
    }
    case WM_GETMINMAXINFO:
    {
        if (config["frame"].GetBool()) {
            return DefWindowProcW(hWnd, msg, wParam, lParam);
        }
        else
        {
            MINMAXINFO* mminfo = (PMINMAXINFO)lParam;
            RECT workArea;
            SystemParametersInfo(SPI_GETWORKAREA, 0, &workArea, 0);
            mminfo->ptMinTrackSize.x = 1200;
            mminfo->ptMinTrackSize.y = 800;
            mminfo->ptMaxSize.x = workArea.right - workArea.left - 2;
            mminfo->ptMaxSize.y = workArea.bottom - workArea.top - 2;
            mminfo->ptMaxPosition.x = 1;
            mminfo->ptMaxPosition.y = 1;
        }
        return 0;
    }
    case WM_EXITSIZEMOVE: {
        RECT rect;
        GetWindowRect(hWnd, &rect);
        this->x = rect.left;
        this->y = rect.top;
        return 0;
    }
    case WM_SIZE: {
        this->w = LOWORD(lParam);
        this->h = HIWORD(lParam);
        rapidjson::Value& wvs = config["webviews"].GetArray();
        for (size_t i = 0; i < ctrls.size(); i++)
        {            
            auto rect = areaToRect(wvs[i]["area"], w, h);
            ctrls[i]->SetBoundsAndZoomFactor(rect, 1.0);
            
        }
        return 0;
    }
    case WM_DESTROY: {
        PostQuitMessage(0);
        return 0;
    }
    }
    return DefWindowProc(hWnd, msg, wParam, lParam);
}

其中值得注意的就是WM_SIZE消息,在窗口大小改变的时候,我们也改变了WebView2控件的大小。(一个窗口中可能有多个WebView2控件)

这里用到了一个areaToRect方法,用于把配置文件中的配置信息转化成RECT。如下所示:

RECT areaToRect(rapidjson::Value& area, const int& w, const int& h)
{
    auto left{ area["left"].GetInt() };
    auto right{ area["right"].GetInt() };
    auto top{ area["top"].GetInt() };
    auto bottom{ area["bottom"].GetInt() };
    auto width{ area["width"].GetInt() };
    auto height{ area["height"].GetInt() };
    if (right < 0) {
        right = left + width;
    }
    else {
        right = w - right;
    }
    if (bottom < 0) {
        bottom = top + height;
    }
    else
    {
        bottom = h - bottom;
    }
    if (left < 0) {
        left = right - width;
    }
    if (top < 0) {
        top = bottom - height;
    }
    return RECT{ left,top,right,bottom };
}

接下来看以下构造函数调用的第三个方法的代码:

bool Win::createPageController()
{
    rapidjson::Value& wvs = config["webviews"].GetArray();
    auto env = App::getWebViewEnv();
    auto callBackInstance = Callback<ICoreWebView2CreateCoreWebView2ControllerCompletedHandler>(this, &Win::pageCtrlCallBack);
    for (size_t i = 0; i < wvs.Size(); i++)
    {
        auto result = env->CreateCoreWebView2Controller(hwnd, callBackInstance.Get());
        if (FAILED(result)) {
            return false;
        }
    }
    return true;
}

在这个方法里,我们异步创建了WebView2控件,异步方法的回调函数是:pageCtrlCallBack,这个方法的代码为:

HRESULT Win::pageCtrlCallBack(HRESULT result, ICoreWebView2Controller* controller)
{
    HRESULT hr;
    wil::com_ptr<ICoreWebView2> webview;
    hr = controller->get_CoreWebView2(&webview);
    webviews.push_back(webview);
    wil::com_ptr<ICoreWebView2Settings> settings;
    webview->get_Settings(&settings);
    settings->put_IsScriptEnabled(TRUE);
    settings->put_AreDefaultScriptDialogsEnabled(TRUE);
    settings->put_IsWebMessageEnabled(TRUE);
    rapidjson::Value& wvs = config["webviews"].GetArray();
    auto index = ctrls.size();
    auto rect = areaToRect(wvs[index]["area"], w, h);
    hr = controller->put_Bounds(rect);
    ctrls.push_back(controller);
    hr = webview->Navigate(L"https://www.baidu.com");
    return hr;    
}

析构函数回收资源:

Win::~Win()
{
    for (size_t i = 0; i < webviews.size(); i++)
    {
        webviews[i]->Release();
    }
    for (size_t i = 0; i < ctrls.size(); i++)
    {
        ctrls[i]->Release();
    }
}

运行程序得到如下界面。 

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
WebView2 是一个基于 Chromium 的 Web 视图控件,它可以嵌入到 Windows 应用程序中,方便开发者在应用程序中加载和显示 Web 内容。以下是关于 WebView2 的简要教程。 1. 下载和安装:首先,需要从 Microsoft 的官方网站下载 WebView2 运行时和开发工具包。安装完毕后,可以在 Visual Studio 中创建一个新的项目,并在项目中添加 WebView2 控件。 2. 初始化 WebView2 控件:在加载 WebView2 控件之前,需要进行初始化。可以通过在 MainForm 的代码中添加以下代码行来实现初始化: ```csharp private async void Form1_Load(object sender, EventArgs e) { await WebView.EnsureCoreWebView2Async(null); } ``` 这段代码会使应用程序等待 WebView2 控件初始化完成,然后可以继续加载其他内容。 3. 加载网页内容:通过 WebView2 控件的 `Source` 属性可以加载指定 URL 的网页内容,也可以直接加载 HTML 字符串。示例如下: ```csharp private void LoadWebPage() { WebView.Source = new Uri("https://www.example.com"); // 加载指定 URL 的网页 // 或者: WebView.NavigateToString("<html><body><h1>Hello, World!</h1></body></html>"); // 加载 HTML 字符串 } ``` 4. 响应网页事件:WebView2 允许开发者对网页中的事件进行响应,例如点击链接、提交表单等。可以通过订阅 WebView2 控件的 `NavigationStarting`、`ContentLoading` 和 `NavigationCompleted` 等事件来实现。示例如下: ```csharp private void WebView_NavigationStarting(object sender, CoreWebView2NavigationStartingEventArgs e) { // 处理网页导航开始事件 } private void WebView_ContentLoading(object sender, CoreWebView2ContentLoadingEventArgs e) { // 处理网页内容加载事件 } private void WebView_NavigationCompleted(object sender, CoreWebView2NavigationCompletedEventArgs e) { // 处理网页导航完成事件 } ``` 通过这些事件,可以在应用程序中做出相应的处理,例如更新 UI、获取表单数据等操作。 总而言之,WebView2 是一个强大的工具,可以帮助开发者在 Windows 应用程序中加载和显示 Web 内容。通过以上简要教程,可以了解如何使用 WebView2 控件来加载网页内容,并对网页中的事件进行响应。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值