使用WebView2在MFC中实现现代化HTML界面

使用WebView2在MFC中实现现代化HTML界面-可达鸭小栈
完整代码包含类和调用例子下载
此内容为付费资源,请付费后查看
500
积分资源免费
付费资源可享->
免费更新
协助部署
在线答疑
付费资源

智谱

AI 正在加载摘要

引言:

MFC(Microsoft Foundation Classes)虽然年代久远,但在许多企业级桌面应用中仍然占据重要地位。随着用户对界面美观度和交互体验的要求越来越高,传统的MFC控件已经难以满足需求。WebView2控件的出现,为MFC开发者提供了一条完美的现代化升级路径——用HTML/CSS/JS构建界面,用MFC处理业务逻辑。

本文将详细介绍如何在MFC项目中使用WebView2控件,并完整展示我封装的 CWebView2Helper 类的使用方法,帮助你快速实现MFC与前端页面的双向通信。

界面效果:

QQ20260409-102435

环境准备

1. 安装WebView2运行时

WebView2依赖Edge浏览器内核,用户机器需要安装WebView2 Runtime。你可以引导用户从微软官方下载,或者在安装包中嵌入引导程序。

如果系统已安装新版Edge浏览器,通常会自动包含WebView2 Runtime。

2. 配置MFC项目

添加WebView2 SDK引用(推荐使用NuGet):

在Visual Studio中打开项目,右键项目 → “管理NuGet程序包”,搜索 Microsoft.Web.WebView2 并安装。

添加WIL(Windows Implementation Libraries)

同样通过NuGet安装 Microsoft.Windows.ImplementationLibrary,该库提供了智能指针等辅助功能。

包含必要的头文件

#include "WebView2Helper.h"

CWebView2Helper 类核心接口

类概述

CWebView2Helper 封装了WebView2的完整生命周期管理,提供以下核心能力:

功能模块 主要接口
创建与初始化 Create()CreateWithHtml()IsInitialized()
导航控制 Navigate()NavigateToHtmlString()Refresh()GoBack()GoForward()
JS交互 ExecuteScript()CallJsFunction()PostWebMessage()
窗口布局 Resize()SetBounds()SetVisible()
调试工具 OpenDevTools()SetUserAgent()ClearCookies()

快速开始

第一步:创建WebView2控件

在对话框类中声明成员变量:

class CMyDlg : public CDialogEx
{
    // ...
    CWebView2Helper m_webView;
};

在 OnInitDialog() 中初始化:

BOOL CMFCApplication2Dlg::OnInitDialog()
{
    CDialogEx::OnInitDialog();

    // 将“关于...”菜单项添加到系统菜单中。
    // IDM_ABOUTBOX 必须在系统命令范围内。
    ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
    ASSERT(IDM_ABOUTBOX < 0xF000);

    CMenu* pSysMenu = GetSystemMenu(FALSE);
    if (pSysMenu != nullptr)
    {
        BOOL bNameValid;
        CString strAboutMenu;
        bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
        ASSERT(bNameValid);
        if (!strAboutMenu.IsEmpty())
        {
            pSysMenu->AppendMenu(MF_SEPARATOR);
            pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
        }
    }

    // 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动执行此操作
    SetIcon(m_hIcon, TRUE);         // 设置大图标
    SetIcon(m_hIcon, FALSE);        // 设置小图标
    thePtr = this;
    // ========== WebView2 初始化代码 ==========
    TRACE(_T("开始创建 WebView2...\n"));

    // 1. 注册鼠标事件回调
   
    // 获取 exe 所在目录
    TCHAR szExePath[MAX_PATH];
    GetModuleFileName(NULL, szExePath, MAX_PATH);
    CString strExeDir = szExePath;
    int nPos = strExeDir.ReverseFind(_T('\\'));
    if (nPos > 0) strExeDir = strExeDir.Left(nPos);

    // HTML 文件路径
    CString strHtmlPath = strExeDir + _T("\\index.html");
    CString strUrl = _T("file:///") + strHtmlPath;
    // 注册鼠标回调
  // 注册鼠标回调


    // 创建 WebView2
 /*   if (!m_webView.Create(GetSafeHwnd(),strUrl))
    {
        AfxMessageBox(_T("WebView2 创建失败,请检查是否安装了 WebView2 运行时"));
        return TRUE;
    }*/
    // 加载 HTML 资源(返回 UTF-8 字节)
    CStringA utf8Html = LoadHtmlFromResourceUtf8(IDR_TEXT1);

    // 直接创建并显示 HTML,无需等待
    m_webView.CreateWithHtml(GetSafeHwnd(), CString(utf8Html));

    // 注册消息回调(用于接收 JS 发来的消息)
    m_webView.RegisterMessageCallback(MyWebViewCallback);
  
    Sleep(500);
    m_webView.Resize();

    // 初始化键盘钩子,拦截F12
    if (!InitKeyboardHook())
    {
        TRACE(_T("键盘钩子安装失败\n"));
    }
    return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}

至此已完成html界面创建 接下来只需要编写html代码以及JS代码和MFC通信即可

MFC回调处理响应事件例子
void MyWebViewCallback(const CString& msg)
{
    OutputDebugString(msg + "\r\n");

    if (msg.Find("新增用户")!=-1)
    {
        // 发送 JSON 格式的消息
        CString jsonMessage = msg;
        thePtr->m_webView.PostWebMessage(jsonMessage);
    }
    if (msg == "window_close")
    {
        ExitProcess(0);
    }
    else if (msg == "click_kaodayalog")
    {
        ShellExecute(NULL, "open", "https://www.ikdya.com", NULL, NULL, SW_SHOWNORMAL);
    }
    else if (msg == "按下鼠标")
    {
        // 获取主窗口句柄(需要你提供,假设是 AfxGetMainWnd() 或者全局变量)
        if (g_hWnd == NULL)
        {
            g_hWnd = AfxGetMainWnd()->GetSafeHwnd();  // 或者你的主窗口句柄
        }

        POINT ptCursor;
        GetCursorPos(&ptCursor);

        CRect rcWnd;
        GetWindowRect(g_hWnd, &rcWnd);

        g_ptDragOffset.x = ptCursor.x - rcWnd.left;
        g_ptDragOffset.y = ptCursor.y - rcWnd.top;
        g_bDragging = TRUE;

        //OutputDebugString("开始拖动, 偏移量=" + std::to_string(g_ptDragOffset.x) + "," + std::to_string(g_ptDragOffset.y) + "\r\n");
    }
    else if (msg == "鼠标移动")
    {
        if (g_bDragging && g_hWnd)
        {
            POINT ptCursor;
            GetCursorPos(&ptCursor);

            int newX = ptCursor.x - g_ptDragOffset.x;
            int newY = ptCursor.y - g_ptDragOffset.y;

            SetWindowPos(g_hWnd, NULL, newX, newY, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
        }
    }
    else if (msg == "释放鼠标")
    {
        g_bDragging = FALSE;
        OutputDebugString("结束拖动\r\n");
    }
}

html代码发送消息监听消息示例

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <script src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script>
    <style>
        * { margin: 0; padding: 0; }
        body { background: #f5f5f5; }
        #chart { width: 100%; height: 100%; }
    </style>
</head>
<body>
    <div id="chart"></div>
    <script>
        let myChart;
        
        // 初始化图表
        function initChart() {
            myChart = echarts.init(document.getElementById('chart'));
            // 可选:监听窗口大小变化
            window.addEventListener('resize', () => myChart.resize());
        }
        
        // 渲染图表(供C++调用)
        function renderChart(optionJson) {
            if (!myChart) initChart();
            myChart.setOption(JSON.parse(optionJson));
        }
        
        // 通知C++页面已就绪
        window.chrome.webview.postMessage(JSON.stringify({
            type: 'ready',
            timestamp: Date.now()
        }));
        
        // 监听C++发来的消息
        window.chrome.webview.addEventListener('message', function(event) {
            const msg = JSON.parse(event.data);
            if (msg.type === 'updateChart') {
                renderChart(JSON.stringify(msg.option));
            }
        });
    </script>
</body>
</html>

注意事项

1. 线程模型

WebView2的回调默认在UI线程执行,不要在回调中执行耗时操作。如需处理,使用PostMessage转到其他线程或使用异步方式。

2. 初始化是异步的

Create() 和 CreateWithHtml() 是异步操作,调用后需要等待 IsInitialized() 返回TRUE才能执行导航、JS调用等操作。建议在注册消息回调后再创建控件。

3. 内存HTML字符串的编码

使用 CreateWithHtml() 或 NavigateToHtmlString() 时,请确保字符串是UTF-8编码。如果从资源加载,可能需要先转换编码。

4. 临时文件清理

使用 NavigateToHtmlWithTempFile() 时,临时文件会在对象析构或下次调用时自动删除,无需手动清理。

5. 部署依赖

发布程序时,确保目标机器已安装WebView2 Runtime。可以在安装程序中加入检测逻辑:

------本页内容已结束,喜欢请分享------

感谢您的来访,获取更多精彩文章请收藏本站。

© 版权声明
THE END
看完了?看完了愣着啊点赞干什么
点赞65 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容