尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

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

如何在wpf项目里插入Quill 编辑器
📅 发布时间:2026/7/9 0:47:58

第一步骤,要安装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

 

编程是个人爱好

相关新闻

  • 深圳黄金回收哪家靠谱?2026全市星级测评榜单,本地人变现放心选 - zzlzzl6688
  • 官方最新发布2026年:安徽六安电大中专计算机应用专业,衔接大专升学跳板 - 我叫小周
  • 合肥个人闲置大牌包出手完整教程,避开压价、快速拿到合理市价 - 禹竞

最新新闻

  • USB 3.0 xHCI 主机控制器报错代码 10/39/43:5步精准排查与修复流程(含驱动/BIOS/注册表)
  • 露易丝·海的诗歌2
  • 高精度ADC与STM32L4在工业测量中的优化设计
  • 学生管理信息系统:高效、便捷的学生管理工具
  • 计算机毕业设计之民办高校二手物品交易网的设计与实现
  • python findwindowex Python findwindowex抓不到窗格?这招让隐藏文本瞬间裸奔

日新闻

  • SQL 查询语句的标准逻辑执行顺序(即语义处理顺序),它与实际书写顺序不同,但决定了数据库如何解析和执行查询
  • ORB-SLAM2 重定位模块深度解析:从 BoW 候选帧到 PnP 优化的 6 步流程
  • 罗技鼠标宏压枪脚本终极指南:从原理到实战的完整解析

周新闻

  • 基于YOLOv12的番茄成熟度智能检测系统开发
  • 终极RimWorld模组管理指南:用RimSort告别模组冲突烦恼
  • AI Agent框架开发:从理论到实践的完整指南

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号