当前位置: 首页 > news >正文

如何在wpf项目里插入Quill 编辑器

第一步骤,要安装Node.js,下载地址:https://nodejs.org/zh-cn/download

第二步骤,生成quill。

不需要懂前端构建工具链,只是借用 npm 当一个"下载器",比手动去 CDN 上一个个右键另存为更可靠(能保证版本一致、文件完整)。
在任意临时目录执行,我这里直接在桌面执行cmd,不需要在 WPF 项目里

QQ截图20260617122801

 


mkdir quill-temp && cd quill-temp
npm init -y
npm install quill

QQ截图20260617111930
安装完成后你需要的文件就在:

QQ截图20260617122916

 


quill-temp/node_modules/quill/dist/
├── quill.js          ← 主库(未压缩,方便调试)
├── quill.js.map
├── quill.snow.css    ← snow 主题样式(你用的工具栏样式)
├── quill.bubble.css
├── quill.core.css
└── ...

QQ截图20260617112018

在项目中新建QuillEditor 文件夹,并且把 quill.js 和 quill.snow.css 这两个文件复制到里面。这三个文件都要选中 生成操作:内容。

 

QQ截图20260617122321

 

第三步骤,手动新建 editor.html 直接配套使用。

<!DOCTYPE html>
<html lang="zh">
<head><meta charset="UTF-8" /><title>Editor</title><link rel="stylesheet" href="quill.snow.css"><style>html, body {margin: 0;padding: 0;height: 100%;}#editor-container {height: 100%;display: flex;flex-direction: column;}#toolbar {flex-shrink: 0;}#editor {flex: 1;overflow-y: auto;}</style>
</head>
<body><div id="editor-container"><div id="toolbar"></div><div id="editor"></div></div><script src="quill.js"></script><script>// 图片插入交给 C# 端处理,而不是用 Quill 默认的 base64 内嵌function imageHandler() {postToHost({ type: 'requestImage' });}const toolbarOptions = [['bold', 'italic', 'underline', 'strike'],[{ 'script': 'sub' }, { 'script': 'super' }],['clean'],['image']];const quill = new Quill('#editor', {theme: 'snow',modules: {toolbar: {container: toolbarOptions,handlers: {image: imageHandler}}}});// 统一的发送消息函数,脱离 WebView2 环境时不报错function postToHost(msg) {if (window.chrome && window.chrome.webview) {window.chrome.webview.postMessage(msg);}}// C# 端选完图片后调用这个函数把路径插进编辑器function insertImageAtCursor(path) {const range = quill.getSelection(true);quill.insertEmbed(range.index, 'image', path);}// 内容变化时通知 C#(用于双向绑定)
        quill.on('text-change', function () {postToHost({type: 'contentChanged',html: quill.root.innerHTML});});// 提供给 C# 主动调用的取值/赋值方法function getContent() {return quill.root.innerHTML;}function setContent(html) {quill.root.innerHTML = html;}</script>
</body>
</html>

 

第四步骤,在nuget 中添加 WebViewe2 ,WebViewe2 运行基于本机电脑已经按住edge浏览器。


 

用户电脑很可能没有按住WebViewe2,最好采用Fixed Version模式。

考虑到你的目标客户是 B 端机构(学校、培训机构),这些环境往往网络管控严格、不允许随意联网下载组件,Fixed Version 更稳妥。具体做法:

  1. 去 Microsoft 官方 WebView2 下载页面,下载对应你目标系统架构(x64/ARM64)的 Fixed Version 运行时压缩包
  2. 把这个运行时文件夹整个放进你的安装包里
  3. 程序启动时指定运行时路径,而不是用系统全局安装的版本:

 

第五步骤,在nuget 中添加 WebViewe2 

QQ截图20260617123449

 第六步骤,在项目中加入该控件

xaml

<UserControl x:Class="IndividualQAlibrary.Theme.Controls.RichTextBoxMVVMEditorView"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:i="http://schemas.microsoft.com/xaml/behaviors"xmlns:wv2="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf"mc:Ignorable="d" x:Name="Editor"d:DesignHeight="450" d:DesignWidth="800"><Grid><wv2:WebView2 x:Name="QuillWebView" HorizontalAlignment="Stretch" /></Grid>
</UserControl>

C# 

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.Json;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Microsoft.Web.WebView2.Core;
using Microsoft.Win32;
using System.IO;
using System.Text.Json;
using Microsoft.Web.WebView2.Core;namespace IndividualQAlibrary.Theme.Controls
{public class BridgeMessage{public string Type { get; set; }public string Html { get; set; }}public partial class RichTextBoxMVVMEditorView : UserControl {public RichTextBoxMVVMEditorView(){InitializeComponent();Loaded += OnLoaded;}private async void OnLoaded(object sender, RoutedEventArgs e){// 触发 CoreWebView2 初始化,必须 await 完成await QuillWebView.EnsureCoreWebView2Async(null);QuillWebView.CoreWebView2.Settings.AreDevToolsEnabled = true;// 此时 CoreWebView2 才不为 null
            SetupVirtualHost();SetupMessageBridge();// 用伪域名加载页面,而不是 file:// 或本地路径QuillWebView.Source = new Uri("https://quill.localapp/editor.html");}private void SetupVirtualHost(){var assetsPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory,"QuillEditor");  // 改成实际的文件夹名
QuillWebView.CoreWebView2.SetVirtualHostNameToFolderMapping("quill.localapp",assetsPath,CoreWebView2HostResourceAccessKind.Allow);}private void SetupMessageBridge(){QuillWebView.CoreWebView2.WebMessageReceived += OnWebMessageReceived;}private string PickAndSaveImageFile(){// TODO: 实现弹出文件选择框、保存图片到本地目录的逻辑return null;}private async void OnWebMessageReceived(object sender, CoreWebView2WebMessageReceivedEventArgs e){string json = e.WebMessageAsJson;var msg = JsonSerializer.Deserialize<BridgeMessage>(json,new JsonSerializerOptions { PropertyNameCaseInsensitive = true });switch (msg.Type){case "requestImage":var localPath = PickAndSaveImageFile();if (localPath != null){var safePath = JsonSerializer.Serialize(localPath);await QuillWebView.CoreWebView2.ExecuteScriptAsync($"insertImageAtCursor({safePath})");}break;case "contentChanged":// TODO: 确认 ViewModel 类型后再启用这段同步逻辑// if (DataContext is XxxViewModel vm)// {//     vm.RichContent = msg.Html;// }
                    UpdateContentFromJs(msg.Html);break;}}public static readonly DependencyProperty RichContentProperty =DependencyProperty.Register(nameof(RichContent),typeof(string),typeof(RichTextBoxMVVMEditorView),new FrameworkPropertyMetadata(string.Empty,FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,OnRichContentChanged));public string RichContent{get => (string)GetValue(RichContentProperty);set => SetValue(RichContentProperty, value);}private bool _isUpdatingFromJs; // 防止 JS→C#→JS 的循环回写private static async void OnRichContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e){var control = (RichTextBoxMVVMEditorView)d;if (control._isUpdatingFromJs) return; // ViewModel 改值是因为 JS 推过来的,不要再推回去if (control.QuillWebView.CoreWebView2 != null){await control.SetEditorContentAsync((string)e.NewValue);}}// JS 端内容变化时调用这个,更新依赖属性,但要标记来源避免死循环private void UpdateContentFromJs(string html){_isUpdatingFromJs = true;RichContent = html;_isUpdatingFromJs = false;}public async Task SetEditorContentAsync(string html){if (QuillWebView.CoreWebView2 == null) return;var safeHtml = JsonSerializer.Serialize(html ?? string.Empty);await QuillWebView.CoreWebView2.ExecuteScriptAsync($"setContent({safeHtml})");}}}

引用

   xmlns:controls="clr-namespace:IndividualQAlibrary.Theme.Controls"<controls:RichTextBoxMVVMEditorView   
Grid.Row="2" Margin="20,0,20,0" Grid.ColumnSpan="2"/><TextBlock  Grid.Row="3"  Margin="20,0,0,0"  VerticalAlignment="Center" FontSize="16"  Foreground="#3E4A58" FontWeight="Bold" Grid.ColumnSpan="2">正确答案·选项 :</TextBlock><controls:RichTextBoxMVVMEditorView  

效果

QQ截图20260617133724

 

http://www.rkmt.cn/news/1541887.html

相关文章:

  • 深圳黄金回收哪家靠谱?2026全市星级测评榜单,本地人变现放心选 - zzlzzl6688
  • 官方最新发布2026年:安徽六安电大中专计算机应用专业,衔接大专升学跳板 - 我叫小周
  • 合肥个人闲置大牌包出手完整教程,避开压价、快速拿到合理市价 - 禹竞
  • 红河哈尼族彝族自治州黄金回收实体店怎么选?这份清单帮你货比三家 - 奢金汇
  • Java毕设选题推荐:基于 SpringBoot+Vue 的数码产品展示与购物结算系统设计 移动端适配型数码电商购物平台设计与实现【附源码、mysql、文档、调试+代码讲解+全bao等】
  • 从零搭建微信小程序后端:域名、备案、HTTPS 证书与 Nginx(以 perfecttools.top 为例)
  • 2026年6月最新江诗丹顿中国官方售后电话热线客服地址服务网点 - 江诗丹顿服务中心
  • 2026武汉黄金回收避坑实录:哪家靠谱?收收金到底值不值得选? - 热点速览
  • 2026年长沙高端系统门窗定制怎么选|断桥铝隔音窗品牌深度测评与避坑指南 - 优质企业观察收录
  • 泸州汽车灯光升级行业实力门店解析:欧特车灯深耕行业二十年,以专业工艺树立行业标准 - 热点速览
  • 2026长沙高端系统门窗定制:断桥铝隔音方案与全屋定制品牌深度横评 - 优质企业观察收录
  • 2015+充电协议解析
  • 福州防腐木地板生产厂家哪家售后好:2026年匠心精选 - 品牌推广大师
  • 成都保险被拒赔怎么办?李晓伟律师教你三步维权法 - 行路心安
  • 借条丢了怎么办?专业律所帮你补救证据链 - 品牌2026
  • 2026深圳黄金回收探店报告,筛选11家口碑门店,新手变现不踩雷 - 奢侈品回收测评
  • 福建菠萝格生产厂商推荐:匠心精选 - 品牌推广大师
  • 2026年五轴CNC精密加工TOP厂家推荐:如何筛选靠谱供应商 - 余文22
  • 银川保险理赔律师推荐:李晓伟律师团队综合实力全解析 - 行路心安
  • 2026哈尔滨实体店铺推广运营怎么选?行业避坑总结 - 最新行业资讯
  • OpenClaw 数据库灾备全方案:定时备份、异地灾备、故障自动切换
  • UI-TARS Desktop终极指南:用自然语言控制你的电脑
  • 2026 福州黄金回收全流程指南:实时金价、门店梯队、变现干货,轻松高价出手闲置黄金 - 薛定谔的梨花猫
  • Microsoft Office LTSC 2024 for Mac 16.110 发布 - 文档、电子表格、演示文稿和电子邮件
  • 2026长沙库存积压设备回收公司 实测 - LYL仔仔
  • 2026深圳黄金回收门店盘点,11家合规老店,本地居民实测推荐 - 奢侈品回收测评
  • 25.条件构造器 分页插件
  • 基于LCU API的英雄联盟客户端工具包架构设计与技术实现
  • 厦门专业中职学校排行:适配本地产业的5家实力院校 - 奔跑123
  • 2026年安徽普高落榜择校指南,适合低分孩子的学校推荐 - 小张zc