Skip to content

数据绑定专题 - BindUI / BindData / SetData

📖 目录


概述

cWebView2Host 的数据绑定系统是一种声明式的 VB6-Web 双向通信机制,无需手动编写 JSON 消息解析、DOM 操作或事件监听胶水代码。

✨ 核心特点

  • 🔗 双向绑定 - BindUI (UI->宿主) + BindData (宿主->UI) 覆盖双向通信
  • 📝 声明式 - 用一行绑定声明替代大量胶水代码
  • 🔄 自动重建 - 页面导航后自动重新注入绑定逻辑
  • 🎯 CSS 选择器 - 使用标准 CSS 选择器定位 DOM 元素
  • 📦 批量操作 - SetDataBatch 一次推送多个数据

架构原理

系统组成

cWebView2Host.BindUI() / BindData() / SetData()


WebView2BindUIProxy (COM 对象,暴露为 JS 全局对象 bindUIProxy)

        ├── BindUI: JS addEventListener → bindUIProxy.onEvent(bindId, eventName, detail)
        │   └── CallByName(HostInst, MethodName, vbMethod, EventName, Detail)

        ├── BindData: SetData → JS 循环遍历绑定列表 → element[attr] = value

        └── 导航重建: DOMContentLoaded → 注入重新绑定 JS → 恢复所有 BindUI + BindData + DataValue

工作流程

1. VB6 调用 BindUI/BindData → 注册绑定到 WebView2BindUIProxy 内部集合
2. GetJsCode() → 从嵌入资源加载 ProxyBindUI.js / ProxyMouseEvents.js
3. GetBindAddJs() → 生成 addEventListener 的 JS 代码
4. ExecuteScript() → 在 WebView2 中注入并执行绑定代码
5. DOM 事件触发 → JS 调用 bindUIProxy.onEvent() → CallByName → VB6 方法回调
6. SetData 调用 → 生成 JS 代码 → 遍历绑定列表 → 更新 DOM 属性

BindUI - 事件绑定

基本用法

vb
' 将 #submit-btn 的 click 事件绑定到 Me.OnButtonClick
wv.BindUI Me, "OnButtonClick", "#submit-btn"

声明后,当用户在 WebView2 中点击 #submit-btn 元素时,VB6 自动调用 OnButtonClick 方法。

宿主方法签名

所有 BindUI 回调方法必须遵循以下签名:

vb
Public Sub OnButtonClick(ByVal EventName As String, ByVal Detail As String)
    ' EventName: 触发的 DOM 事件名(如 "click", "input", "change")
    ' Detail: JSON 格式的事件详情
End Sub

Detail 参数解析

Detail 是 JSON 字符串,内容取决于 DOM 事件类型。常见事件的 Detail 结构:

事件Detail 示例
click{"type":"click","target":"#btn"}
input{"type":"input","value":"用户输入的文本"}
change{"type":"change","value":"新值","checked":true}

由于 VB6 没有内置 JSON 解析,通常使用简单的字符串处理:

vb
Public Sub OnNameInput(ByVal EventName As String, ByVal Detail As String)
    ' 简单提取 value 字段
    Dim val As String
    val = JsonValue(Detail, "value")
    wv.SetData "name", val
End Sub

' 简易 JSON 值提取辅助函数
Private Function JsonValue(ByVal Json As String, ByVal Key As String) As String
    Dim pos As Long
    pos = InStr(Json, """" & Key & """:""")
    If pos > 0 Then
        pos = pos + Len(Key) + 3
        Dim endPos As Long
        endPos = InStr(pos, Json, """")
        If endPos > 0 Then
            JsonValue = Mid(Json, pos, endPos - pos)
        End If
    End If
End Function

自定义事件名

默认绑定 click 事件,可通过 EventName 参数指定其他事件:

vb
' 绑定 input 事件(实时响应文本输入)
wv.BindUI Me, "OnNameInput", "#name-input", EventName:="input"

' 绑定 change 事件(选择框、复选框值变化)
wv.BindUI Me, "OnToggle", "#toggle-enabled", EventName:="change"

' 绑定 submit 事件
wv.BindUI Me, "OnFormSubmit", "#my-form", EventName:="submit"

多元素绑定

vb
' 多个按钮绑定到不同方法
wv.BindUI Me, "OnSave", "#btn-save"
wv.BindUI Me, "OnCancel", "#btn-cancel"
wv.BindUI Me, "OnReset", "#btn-reset"

' 同一元素的不同事件绑定到不同方法
wv.BindUI Me, "OnFocus", "#input", EventName:="focus"
wv.BindUI Me, "OnBlur", "#input", EventName:="blur"

移除绑定

vb
' 移除指定元素的所有事件绑定
wv.UnbindUI "#submit-btn"

' 仅移除指定元素的特定事件绑定
wv.UnbindUI "#name-input", "input"

BindData - 数据绑定

原理

BindData 将一个数据键名绑定到DOM 元素特定属性。当 SetData 更新该键名时,所有绑定了该键名的 DOM 元素属性会自动更新。

支持的 DOM 属性

属性说明示例
textContent元素文本内容(默认)标签、段落
value表单元素值input、textarea、select
innerHTML元素内部 HTML动态渲染 HTML 片段
src资源来源img、video、iframe
classCSS 类名状态样式切换
checked选中状态checkbox、radio
visible显示/隐藏(自定义)任意元素
其他属性直接设置 DOM 属性href, disabled, style 等

基本用法

vb
' 绑定数据键到元素属性
wv.BindData "name", "#name-input", "value"
wv.BindData "message", "#msg-display", "textContent"

' 同一键绑定到多个元素/属性
wv.BindData "name", "#name-input", "value"
wv.BindData "name", "#name-preview", "textContent"    ' 同一数据,不同属性

' 绑定到特殊属性
wv.BindData "enabled", "#toggle", "checked"            ' checkbox
wv.BindData "enabled", "#panel", "visible"             ' 显示/隐藏
wv.BindData "avatar", "#avatar-img", "src"             ' 图片地址
wv.BindData "status", "#status-dot", "class"           ' CSS 类名
wv.BindData "items", "#list", "innerHTML"              ' HTML 片段

visible 属性

visible 是 cWebView2Host 自定义的虚拟属性,控制 DOM 元素的显示/隐藏:

vb
wv.BindData "showPanel", "#settings-panel", "visible"

' 当 showPanel = True 时,JS 设置 element.style.display = ''
' 当 showPanel = False 时,JS 设置 element.style.display = 'none'

class 属性

class 属性替换元素的全部 CSS 类名:

vb
wv.BindData "statusOnline", "#status-dot", "class"

wv.SetData "statusOnline", "dot online"     ' 绿色在线状态
wv.SetData "statusOnline", "dot offline"    ' 灰色离线状态

SetData - 数据推送

SetData

推送单个数据值。支持 String、Boolean、Number 类型:

vb
wv.SetData "name", "张伟"                    ' 字符串
wv.SetData "enabled", True                   ' 布尔值
wv.SetData "count", 42                       ' 数值
wv.SetData "avatar", "https://example.com/a.png"  ' URL 字符串

类型转换规则:

  • VB6 String → JS 字符串(自动加引号)
  • VB6 Boolean → JS boolean(true/false)
  • VB6 数值类型 → JS number(直接传递)

SetDataBatch

批量推送多个数据值,参数为 JSON 对象字符串:

vb
wv.SetDataBatch "{""name"":""张伟"",""enabled"":true,""count"":42}"

适用于页面初始化时一次性推送所有数据。


导航后自动重建

当 WebView2 导航到新页面时,之前注入的 JavaScript 绑定代码会被销毁。cWebView2Host 自动处理此问题:

重建机制

  1. 每次导航导致 DOMContentLoaded 后,自动触发重建
  2. GetReattachJs() 生成重建所有绑定的 JS 代码
  3. 重建包括:BindUI 事件监听 + BindData 声明 + DataValue 当前值

DataValue 持久化

SetData 推送的值会自动存储在 m_DataValues 集合中。页面导航后,自动将这些值重新应用到 DOM:

vb
' 首次导航
wv.SetData "name", "张伟"

' 用户点击链接导航到新页面
' → DOMContentLoaded 触发自动重建
' → "name" 键的值 "张伟" 自动重新应用

完整示例

以下示例展示 BindUI/BindData/SetData 的完整协作:

VB6 代码

vb
Dim WithEvents wv As cWebView2Host

Private Sub Form_Load()
    Set wv = New cWebView2Host
    wv.Initialize Me.hWnd, App.Path & "\www"
End Sub

Private Sub wv_Ready()
    ' === 宿主 -> UI 数据绑定 ===
    wv.BindData "name", "#name-input", "value"
    wv.BindData "name", "#name-preview", "textContent"
    wv.BindData "enabled", "#toggle-enabled", "checked"
    wv.BindData "enabled", "#settings-panel", "visible"
    wv.BindData "avatar", "#avatar", "src"
    wv.BindData "count", "#msg-count", "textContent"
    wv.BindData "statusOnline", "#status-dot", "class"
    
    ' === UI -> 宿主 事件绑定 ===
    wv.BindUI Me, "OnNameInput", "#name-input", EventName:="input"
    wv.BindUI Me, "OnToggle", "#toggle-enabled", EventName:="change"
    wv.BindUI Me, "OnIncMsg", "#btn-inc"
    wv.BindUI Me, "OnResetMsg", "#btn-reset"
    
    ' === 推送初始数据 ===
    wv.SetData "name", "张伟"
    wv.SetData "enabled", True
    wv.SetData "count", 0
    
    Me.Caption = wv.DocumentTitle
End Sub

' === UI 回调方法 ===

Public Sub OnNameInput(ByVal EventName As String, ByVal Detail As String)
    Dim val As String
    val = JsonValue(Detail, "value")
    wv.SetData "name", val                ' 回推到 UI
End Sub

Public Sub OnToggle(ByVal EventName As String, ByVal Detail As String)
    ' Detail 示例: {"type":"change","checked":true}
    Dim checked As Boolean
    checked = (InStr(Detail, """checked"":true") > 0)
    wv.SetData "enabled", checked
End Sub

Public Sub OnIncMsg(ByVal EventName As String, ByVal Detail As String)
    Dim count As Long
    count = Val(wv.JsProp("document.querySelector('#msg-count').textContent"))
    wv.SetData "count", count + 1
End Sub

Public Sub OnResetMsg(ByVal EventName As String, ByVal Detail As String)
    wv.SetData "count", 0
End Sub

HTML 页面

html
<div>
    <input id="name-input" type="text" />
    <p>Hello, <span id="name-preview">-</span>!</p>
</div>
<div>
    <label><input id="toggle-enabled" type="checkbox" /> Enable</label>
    <div id="settings-panel">
        <p>Settings content here</p>
    </div>
</div>
<div>
    <span id="msg-count">0</span> messages
    <button id="btn-inc">+</button>
    <button id="btn-reset">Reset</button>
</div>

常见问题

❓ Q1: BindUI 回调没有被触发?

现象: 绑定了事件但 VB6 方法没有被调用。

原因:

  1. 宿主方法必须声明为 Public(不能用 Private)
  2. 宿主方法签名必须完全匹配 (ByVal EventName As String, ByVal Detail As String)
  3. 绑定必须在 wv_Ready 事件或之后执行,不能在 Form_Load

解决方案:

vb
' 正确
Public Sub OnClick(ByVal EventName As String, ByVal Detail As String)

' 错误
Private Sub OnClick(ByVal EventName As String, ByVal Detail As String)  ' 不能是 Private
Public Sub OnClick()  ' 参数签名不匹配

❓ Q2: BindData 设置 class 属性时原有样式丢失?

现象: 使用 BindData "key", "#element", "class" 后元素原有的 CSS 类被覆盖。

原因: class 属性直接替换元素的全部 className,不是追加。

解决方案: 将完整的类名组合后 SetData:

vb
wv.SetData "statusClass", "card " & IIf(isActive, "active", "inactive")

❓ Q3: 页面导航后绑定失效?

现象: 点击链接导航到新页面后,绑定不再工作。

原因: cWebView2Host 在 DOMContentLoaded 时自动重建绑定,但如果新页面的 DOM 结构不同(选择器匹配不到元素),绑定将静默失败。

解决方案:

  1. 确保所有页面的 DOM ID/选择器一致
  2. 或在导航后重新执行绑定

❓ Q4: SetData 如何传递复杂对象?

现象: 需要将数组或对象传给 JS。

原因: SetData 的 Value 是 Variant,内部转换为 JS 字面量。复杂类型需要序列化为 JSON 字符串并使用 innerHTML 或自定义 JS 处理。

解决方案:

vb
' 方式1:使用 innerHTML 渲染列表
wv.BindData "items", "#item-list", "innerHTML"
wv.SetData "items", "<li>Item 1</li><li>Item 2</li>"

' 方式2:直接调用 JS 函数处理 JSON
wv.JsRun "renderItems(" & jsonString & ")"

最后更新: 2026-06-24

VB6及其LOGO版权为微软公司所有