Skip to content

从twinBASIC驱动Monaco

结合前面教程所有内容的案例研究:一个包含两个WebView2控件的窗体——左侧是Microsoft Monaco编辑器,右侧是实时HTML预览。用户输入时,Monaco将编辑的源代码发送给twinBASIC,后者将其镜像到预览面板。

完整项目以示例0——WebView2示例的形式在新项目对话框中提供(窗体示例3)。

架构

编辑器作为本地Web应用在虚拟主机名下运行;预览面板通过NavigateToString接收原始HTML。

设置编辑器资源

Monaco编辑器是一个约2MB的JavaScript、CSS和字体文件集合。将它们放入项目的 Resources 子文件夹——命名为 MONACO_DEMO——连同 index.html和一个小的引导 script.js托管本地Web资源教程描述了布局。

页面本身是一个 <div id='container'> 加上监听宿主初始内容消息的引导脚本:

html
<!DOCTYPE html>
<html>
    <head>
        <script src="/vs/loader.js"></script>
        <script src="/script.js"></script>
        <link rel="stylesheet" href="/styles.css">
    </head>
    <body>
        <div id="container"></div>
    </body>
</html>
js
window.chrome.webview.addEventListener('message', (event) => {
    let initialHTML = event.data;

    require.config({ paths: { 'vs': 'https://monaco.example/vs' } });
    require(["vs/editor/editor.main"], () => {
        let editor = monaco.editor.create(document.getElementById('container'), {
            value: initialHTML,
            language: 'html',
            theme: 'vs-dark',
            minimap: { enabled: false }
        });

        editor.onDidChangeModelContent(() => {
            // Inform the host of every edit.
            window.chrome.webview.postMessage(editor.getValue());
        });
    });
});

BASIC端

在窗体上放置两个 WebView2 控件——WebView(编辑器)和 WebViewPreview(渲染器)。Ready 处理程序部署资源、注册虚拟主机并导航:

vb
Private localPath As String

Private Sub WebView_Ready() Handles WebView.Ready
    localPath = Environ$("USERPROFILE") & "\Documents\tbMonacoDemo"
    CopyResourcesFolderContentsToLocalPath "MONACO_DEMO", localPath

    WebView.SetVirtualHostNameToFolderMapping _
        "monaco.example", localPath & "\", wv2ResourceAllow
    WebView.Navigate "https://monaco.example/index.html"
End Sub

CopyResourcesFolderContentsToLocalPath托管本地Web资源中的辅助过程。)

推送初始内容

Monaco加载完成后,引导脚本监听包含用于填充编辑器的HTML的 message 事件。在编辑器的NavigationComplete后发送该消息:

vb
Private Sub WebView_NavigationComplete( _
        ByVal IsSuccess As Boolean, ByVal WebErrorStatus As Long) _
        Handles WebView.NavigationComplete

    Dim initialHTML As String = _
        StrConv(LoadResData("initial-editor-html.html", "MONACO_DEMO"), vbFromUTF8)

    WebView.PostWebMessage(initialHTML)
    WebViewPreview.NavigateToString(initialHTML)
End Sub

LoadResData返回资源字节;StrConv(..., vbFromUTF8) 解码它们。PostWebMessage将字符串传递给Monaco的 message 监听器;NavigateToString用相同文本渲染HTML来填充预览面板。

实时预览

Monaco中的每次按键触发其 onDidChangeModelContent 回调,该回调将新内容 postMessage 回BASIC。这以JsMessage事件到达——直接送入预览:

vb
Private Sub WebView_JsMessage(ByVal Message As Variant) Handles WebView.JsMessage
    WebViewPreview.NavigateToString(Message)
End Sub

就是这样——预览面板在每次编辑时重新渲染。

检测缺失的Edge运行时

相当一部分用户将在未安装WebView2 Evergreen运行时的机器上运行应用程序。Error事件将此情况报告为Win32错误代码 &H80070002ERROR_FILE_NOT_FOUND):

vb
Private Sub WebView_Error(ByVal code As Long, ByVal msg As String) _
        Handles WebView.Error
    Const ERROR_FILE_NOT_FOUND As Long = &H80070002
    If code = ERROR_FILE_NOT_FOUND Then
        MsgBox "Failed to initialize the WebView2 control." & vbCrLf & _
               "Please install the WebView2 (Evergreen) runtime.", _
               vbExclamation, "WebView2"
    Else
        MsgBox "WebView2 error " & Hex$(code) & ": " & msg, _
               vbExclamation, "WebView2"
    End If
End Sub

即使在单WebView应用中也值得处理此情况——你在此显示的消息是"什么都没发生"和"哦,我需要安装什么"之间的区别。

下一步

twinBASIC及其LOGO版权为作者"韦恩"所有