做 .NET 业务系统开发这些年,我越来越确认一件事:越不起眼的需求,做起来越容易让人怀疑人生。就拿“输入和编辑多行文本”来说,需求方往往一句话——“给用户一个能编辑多段文字的区域,最好支持加粗、列表、调整格式”——可落到 .NET MAUI 里,自带控件根本不支持格式化编辑。Entry 是单行,Editor 虽然是多行,但也只能编辑纯文本。我最初也是想用原生 Editor 顶一顶,结果连最简单的加粗都给不了用户,更别提保存成结构化内容回显到 Web 端了。
后来我把方向转向 Telerik UI for .NET MAUI,重点折腾了 2026 Q1 版本里的 RadEditor 控件。这篇文章不打官腔,只讲我自己接入编辑器控件时的流程、配置细节、真实踩坑记录,以及从 WinForms / WPF 看过来时容易忽略的思维差异。不管你是刚开始学 MAUI 的新人,还是正给团队做技术选型的老开发,这篇文章应该能帮你少走不少弯路。
1. 原生 Editor 为什么撑不起“格式化编辑”场景
1.1 一个司空见惯却又极难满足的业务场景
先说我遇到的真实场景。今年我在做一套内部项目管理系统,移动端用 .NET MAUI。项目管理模块里有一块“审批意见”功能,用户需要在手机上输入长段意见,并且要支持加粗、下划线、有序列表、调整字号。听起来很稀松平常对吧?但你在 MAUI 自带控件里翻一圈就会发现:Entry 是单行输入,Editor 虽然是多行,但也只能做纯文本,没有任何格式概念。
我最初尝试的办法是“自绘工具栏 + 文本标记解析”:在文本框上方放一排按钮,点击时往文本里插入 Markdown 符号,比如把选中文字前后加上**,显示的时候再做解析。这个方案看着轻巧,实际体验却非常糟糕——用户在编辑过程中看到的是满屏星号、井号,保存后的预览效果和编辑态完全割裂。别说用户不买账,我自己演示到一半都不想继续了。
后来也考虑过用 WebView 加contenteditable来做富文本编辑器。这个思路技术上完全可行,毕竟 Web 端就是这么干的,但接入 .NET MAUI 的工作量远超预期:前端 JS 与 C# 要来回通信、Android 和 iOS 上 WebView 的键盘行为差异巨大、选中文本和光标位置的读取要一层层从 JS 传回托管层。折腾了整整一天,换来的还是一个“时不时失灵”的编辑框。
1.2 “输入多行”和“编辑文本”其实是两件事
很多初学者会把“多行文本输入”和“文本编辑”画等号,但真实业务里两者差别很大。“输入多行文本”只是最底层的能力,而“编辑”意味着用户能操作文本的结构和样式。一个能满足真实业务需要的文本编辑控件,至少要覆盖这几块能力:
- 选区控制:能获取当前选中的文字范围,并且能在任意位置插入内容、做局部替换。
- 格式化指令:加粗、斜体、下划线、删除线、字号、颜色这些基础操作要开箱即用。
- 结构操作:有序列表、无序列表、缩进、对齐、链接,这些东西在长文档里几乎天天用。
- 结构化持久化:内容不能只存纯文本,要能以 HTML 或 RTF 这类带标记的格式保存,这样 Web 端或其它平台才能原样展示。
按这个标准去对比,MAUI 自带的 Editor 只满足了第一项,剩下的全要靠自己造轮子。而在 .NET MAUI 生态里,Telerik UI for .NET MAUI 提供了一整套商业控件,其中 RadEditor 就是专门为了多行富文本编辑场景设计的。它直接把这些能力打包好,不需要我再逐个去实现光标定位、选中范围、HTML 序列化这些底层细节。
1.3 WinForms / WPF 开发者为什么对这个落差格外敏感
我做 WinForms 和 WPF 好几年,对这种“文本编辑能力退化”的感受特别强烈。WinForms 时代有 RichTextBox,自带加粗、斜体、颜色选择,RTF 格式一套就完事;WPF 时代更不用说了,RichTextBox 加 FlowDocument,格式能力和布局能力都非常强。习惯了这些控件的桌面开发者,第一次在 .NET MAUI 里需要“富文本编辑”时,第一反应往往是:不应该啊,这功能怎么连系统自带都没有?
这种落差正是 Telerik 这类商业控件能迅速被关注的原因。其实跨平台移动框架里的文本编辑能力普遍偏弱,这不是 MAUI 一家的问题,而是移动端轻量交互模型导致的。但业务系统不会因为平台变弱就降低需求,客户要的是“手机上也能像电脑上一样排一段漂亮的意见”。所以搞清楚 RadEditor 能做什么、不能什么,就成了关键。
2. RadEditor 的功能边界与 2026 Q1 的实际体验
2.1 核心能力盘点
我先把 RadEditor 在我项目里真正用到的能力列一下,方便你对号入座:
- 多行富文本编辑:输入、回退、文本选择、复制粘贴这些基础交互都做得比较完整。
- 格式化控制:加粗、斜体、下划线、删除线、上标、下标,以及字号、字体颜色和背景色的调整。
- 段落操作:有序列表、无序列表、缩进、左对齐、居中对齐、右对齐。
- 链接管理:可以插入链接、编辑链接地址,也能把已有文字取消链接。
- HTML 模式切换:在可视化编辑和 HTML 源码编辑之间切换,适合需要精细调整标记的场合。
- 双向绑定友好:
Text属性直接就是 HTML 字符串,和 ViewModel 绑定非常顺滑。 - 跨端统一渲染:在 Android、iOS、Windows 上呈现出的编辑交互保持一致,这对于产品体验一致性很重要。
顺便说一句,RadEditor 的Text属性本身就是 HTML 字符串。这个设计决定了它和后端系统的整合方式是“以 HTML 为数据契约”,这一点非常重要,后面的数据回显章节我会专门展开。
2.2 2026 Q1 版本里我实际感受到的变化
商业控件每个季度一更,2026 Q1 版本具体更新了什么,官方文档说得最清楚,我这里只分享几处自己实际使用中的直观感受:
第一,Android 新版本设备上的光标定位和文本选区命中更稳定了。之前在做 Android 自动化回归时,偶尔会出现光标拖到目标位置时选中区域跟着漂移的现象,2026 Q1 实测里这个概率明显下降。
第二,iOS 上键盘与焦点管理的处理有改善。2025 年底我在 iPad 上测试时遇到过点击编辑区后焦点丢失、需要再点一次才能唤起输入键盘的情况,换到 2026 Q1 后这个偶发问题没再复现。
第三,编辑器的无障碍信息组织更完整了。屏幕阅读器现在能按正确的顺序读出一段富文本的内容和结构,这对政企类项目是个隐性加分项,验收时常常会被提到。
第四,程序集结构有调整,安装包的体积和初始化耗时都有变化。具体数字我就不贴了,毕竟环境不同数据会有差异,但整体安装体验比之前清爽。
总的来说,2026 Q1 更像是“补短板”的一版,没有大开大合的新功能,但把移动端最容易影响体验的细节打磨了一遍。对于要投入生产环境使用的团队来说,这反而比堆新功能更让人放心。
2.3 和原生 Editor 放在一起看
| 对比维度 | MAUI 原生 Editor | Telerik RadEditor |
|---|---|---|
| 多行文本输入 | 支持 | 支持 |
| 富文本格式化 | 不支持 | 支持 |
| 工具栏 | 无 | 可配置 |
| 内容格式 | 纯文本 | HTML 字符串 |
| 双向绑定 | 支持Text | 支持Text,值为 HTML |
| 跨平台一致性 | 各平台原生风格 | 统一渲染风格 |
| 内存占用 | 低 | 相对较高 |
| 样式定制能力 | 基础属性 | 主题 + 控件模板 |
这个表最有价值的一行是“内容格式”。原生 Editor 存的是纯文本,而 RadEditor 存的是 HTML。这不仅仅是格式能力的差距,更是整个系统数据模型的差异——如果你要做的功能将来要在 Web 端、公众号、邮件里展示同一段内容,HTML 就是天然通用语言。
3. 从安装到绑定:一个可跑的 RadEditor 接入流程
3.1 NuGet 安装与授权准备
安装很简单,在项目里搜索Telerik.UI.for.Maui就行。用 dotnet CLI 的话:
dotnet add package Telerik.UI.for.Maui --version 2026.1.xxx这里有两个容易踩的坑,先提前说:
第一,Telerik 的商业控件需要授权。新用户可以在 Telerik 官网注册账户申请试用密钥,试用阶段控件会显示水印或者启动提示,不影响功能测试。确定要上生产环境之前,记得把商业授权和开发席位一起买好,别等项目上线了才想起来。
第二,Telerik.UI.for.Maui的版本号和 .NET 版本、平台版本是有关联的,不能盲目下最新版。装完包以后,最好去官网看兼容性矩阵,确认你用的 .NET 版本支持哪几个包版本。这一点在升级 .NET 版本时尤其容易出问题。
3.2 注册 Telerik 控件到 MAUI 应用
安装完包之后,下一步是在MauiProgram.cs里注册 Telerik。漏掉这一步的话,XAML 里引用控件通常不会直接报编译错误,但运行时页面加载起来往往直接抛异常,排查起来非常被动。
public static MauiApp CreateMauiApp() { var builder = MauiApp.CreateBuilder(); builder .UseMauiApp<App>() .UseTelerik() .ConfigureFonts(fonts => { fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular"); fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold"); }); return builder.Build(); }关键就是.UseTelerik()这一行。我见过有人忘记写,查了半天 XAML、查了半天绑定,最后发现只是少了这个注册,浪费了不少时间。
3.3 XAML 引入命名空间与 RadEditor 布局
在页面的根控件上声明 Telerik 命名空间:
xmlns:telerik="http://schemas.telerik.com/2022/xaml/maui"然后像这样使用 RadEditor:
<telerik:RadEditor x:Name="NoteEditor" Placeholder="请输入审批意见..." Text="{Binding NoteBody}" HeightRequest="220" />这里给一个小提示:Text绑定的是 HTML 字符串,所以 ViewModel 里的属性类型是string,不是“某个富文本对象”。比如你可以直接给NoteBody赋这个值:
NoteBody = "<p>已收到申请,<b>同意</b>本次预算调整。</p>";这个赋值方式在初始化、回显、测试时都非常方便,等于你第一次接触这个控件就能立刻控制它的内容。
3.4 双向绑定与内容变化监听
RadEditor 的双向绑定和 MAUI 原生Editor一样,靠Text属性加INotifyPropertyChanged就能跑起来:
public partial class NoteViewModel : INotifyPropertyChanged { private string _noteBody; public string NoteBody { get => _noteBody; set { _noteBody = value; OnPropertyChanged(); } } }如果你需要在用户输入过程中做实时校验,可以用TextChanged事件:
NoteEditor.TextChanged += (s, e) => { // 注意:这里不要做 UI 重布局、不要频繁弹出提示 viewModel.NoteBody = NoteEditor.Text; };我的建议是:既然已经用了双向绑定,TextChanged里就不要再手动赋值一遍,否则容易出现重复触发的问题。TextChanged更适合用于做字数统计、脏数据标记这类轻量逻辑。
3.5 内容回显:从服务端拿 HTML 直接赋值
回显是整个接入流程里最顺的部分,因为 RadEditor 的Text就是 HTML 字符串。从接口拿到数据后,直接赋给属性:
var note = await _api.GetNoteAsync(id); NoteBody = note.Content; // 这个 Content 本身是 "<p>...</p>" 结构的 HTML整个流程绕过了“把结构化文档转成显示文本”的麻烦。你甚至不需要在移动端引入任何 HTML 解析器,控件内部自己完成渲染。这也是我在做完第一个 Demo 后根本没犹豫就决定继续用它推进的原因。
4. 工具栏、主题与数据回显:把编辑体验拉满
4.1 工具栏的装配思路
RadEditor 的工具栏是可以按需配置的。和 WinForms/WPF 里那种“控件自带一条完整工具栏”的固定模式不同,MAUI 上的 RadEditor 更偏向让你把工具项组装到自己的工具栏容器里,这样在移动端有限的屏幕空间内只保留最常用的按钮。
我的实际配置原则是:只放核心五六个按钮,别照搬桌面端的一整排工具栏。手机上按钮做太多,用户点起来很吃力,边缘按钮的命中率也低。我在项目里只保留了加粗、斜体、下划线、无序列表、有序列表、链接这六项,屏幕宽度刚好放下,点击体验也舒适。
大致的 XAML 组织思路是这样(不同小版本的类名可能略有差异,以你安装后智能提示为准):
<telerik:RadEditor Text="{Binding NoteBody}"> <telerik:RadEditor.Toolbar> <telerik:EditorToolbar> <telerik:ToolbarItem Text="B" Tag="bold" /> <telerik:ToolbarItem Text="I" Tag="italic" /> <telerik:ToolbarItem Text="U" Tag="underline" /> <telerik:ToolbarItem Text="• List" Tag="bulletList" /> <telerik:ToolbarItem Text="1. List" Tag="numberList" /> <telerik:ToolbarItem Text="Link" Tag="link" /> </telerik:EditorToolbar> </telerik:RadEditor.Toolbar> </telerik:RadEditor>工具栏实际上会给编辑区域发格式化指令。你不需要自己去操作选区、不需要自己包<b>标签,控件内部处理好了。这里我特别想提一句:Telerik 的工具栏项和编辑器之间的绑定关系,一定要真机测一遍,因为模拟器上鼠标操作和手指触摸的选区模型完全不同,真机上才能验证“选中后点击加粗”这种主路径是否顺手。
4.2 主题与视觉定制
Telerik UI for .NET MAUI 自带一套主题机制,有 Light 和 Dark 两个基础主题,可以在 App 启动时统一指定,也可以针对单个控件做局部覆盖。我的项目因为要贴合公司设计规范,对 RadEditor 做了这些调整:
- 背景色改为浅灰
#F8F9FA,让编辑区与下方信息展示区形成视觉区分。 - 边框圆角调到 8,配合整体卡片式布局。
- 占位符文字颜色调整为次要文字色,避免默认灰色太深。
如果你发现直接设置某个属性不生效,那就需要去主题资源里找控件模板。RadEditor 派生自 Layout 类,视觉结构是“外层容器 + 内部可滚动编辑区”,外层样式和内层样式要分开控制。第一次调整外观时可以打开 Telerik 自带的主题资源文件,照着改属性名,比瞎试快得多。
4.3 为什么 HTML 字符串是最好用的数据格式
前面反复强调 RadEditor 的Text是 HTML,这个设计对业务系统有很实际的价值。
第一,后端存储简单。不需要引入专用文档格式的数据库类型,直接当字符串存进字段即可,查询、迁移、备份都方便。
第二,Web 端展示无缝。同一份 HTML 字符串,网页端直接插入 DOM 就能显示。移动端想预览也可以用 WebView 加载。一套数据,多端复用。
第三,与编辑器解耦。就算以后不用 RadEditor 了,数据仍然是一份标准 HTML,找个别的富文本编辑器也能读,不会被厂商格式锁死。
4.4 回显时的安全处理
用 HTML 字符串做数据格式,带来的安全成本也不低,这里必须敲黑板:外部传入的 HTML 不能直接信任。如果接口返回的字符串里带了<script>、onerror、javascript:这类危险内容,直接赋给 RadEditor 的Text,轻则样式异常,重则可能在 WebView 里触发脚本执行。
我在项目里的做法是,在服务端做一次 HTML 白名单清洗:只允许p、b、i、u、ul、ol、li、a、br、strong、em、span这些标签,其余全部过滤。移动端在赋值前再做一遍基础校验,双重保险。
5. 实测最容易翻车的四个地方
5.1 Android 软键盘遮挡编辑区
这是我在 Android 上遇到的第一个坑。布局底部固定了一块操作栏,编辑区在中间,键盘弹起来的时候直接把编辑区的下半截挡死了,用户完全看不到正在输入的内容。
处理方式有几层。第一是在 AndroidManifest.xml 主 Activity 上设置android:windowSoftInputMode="adjustResize",让系统在键盘弹出时主动压缩布局高度。第二是在页面外层用 ScrollView 包裹,给编辑区一个动态高度,这样键盘弹出时页面可以滚动。第三是更保险的做法:监听键盘高度,动态给编辑区加底部内边距。
实测下来,adjustResize加 ScrollView 的组合能解决 90% 的场景。剩下的 10% 出现在某些国产 ROM 上,键盘设置特殊时窗口调整会延迟,这种情况只能靠键盘高度监听兜底。
5.2 千万别把 RadEditor 直接塞进 CollectionView 行内
第一次做列表行内直接编辑时看着挺炫:用户在当前行就能改内容,不用跳转页面。但问题很快暴露——CollectionView 的视图回收机制和编辑器高频刷新天生冲突。输入一个字符,Text就变一次,列表项高度跟着变化,Item 尺寸重新计算,键盘频繁弹起收回,光标还经常跑到另一行去。
我的建议非常明确:列表项里的文本不要直接编辑,单独开一个编辑页。点击列表项后 Push 一个详情页,里面放 RadEditor,编辑完成把结果带回列表页刷新。这样既避开了视图回收问题,也降低了单一页面的渲染压力,用户操作路径反而更清晰。
5.3 iOS 上选区菜单与光标跳动
iOS 原生输入控件自带放大镜和选区菜单,整体体验其实不错,但 RadEditor 这类控件在 iOS 上封装的是原生编辑视图,偶尔还是会有选区错位的情况。常见症状是:想选中一个词,系统把前后两个词一起选进去了;或者快速双击选段时选中范围偏了一行。
这个问题的复现率不高,往往在长文本段落里才出现。我能给的经验是:让用户用长按 + 拖动手柄的方式精确定位,不要在代码层面做额外的选区自动化操作。另外从外部粘贴纯文本时,文本里偶尔会带上隐藏的 HTML 标签,我在TextChanged里做了一次清洗:
private string CleanPastedHtml(string raw) { // 移除空标签、重复的 div、不合规的嵌套,保留基础格式标签 return Regex.Replace(raw, @"<(script|style)[^>]*>.*?</\1>", "", RegexOptions.Singleline); }这个函数不复杂,但能挡住绝大多数粘贴带来的脏数据。
5.4 内存占用与页面释放
RadEditor 的功能比原生 Editor 多得多,内存占用更高是必然的。如果在 App 里连续打开好几个带编辑器的页面,并且这些页面实例没有及时释放,内存会肉眼可见地往上涨。我在项目里做了几件事来压内存:
- 页面
Unloaded事件里把绑定对象置空。 - 不在列表缓存页面中保留编辑器实例。
- 如果业务允许,编辑完立刻返回并手动把页面从导航栈里弹掉。
这些做法不一定每个团队都需要,但如果你正在做的 App 有严格的内存考核,或者目标设备是老机型,建议一开始就注意。顺手也提一句:不要同时在页面上放多个 RadEditor,比如“左边一个编辑器、右边一个编辑器”这种布局在移动端上从来都不合理。
6. WinForms / WPF 迁移者如何快速对齐 MAUI 文本编辑方案
6.1 三种框架的文本编辑基础能力对比
从 WinForms / WPF 迁移到 MAUI 的开发者,通常带着一套很长很完整的文本编辑经验。我把三方的典型控件和它们的底层数据结构放在一起看:
| 框架 | 常用控件 | 数据格式 | 跨平台能力 |
|---|---|---|---|
| WinForms | RichTextBox | RTF | 不支持 |
| WPF | RichTextBox + FlowDocument | XAML/FluidDocument | 不支持 |
| .NET MAUI | 无内置富文本控件,常用 Telerik RadEditor | HTML | Android / iOS / Windows |
这组对照最关键的差异是数据格式。WinForms 的 RTF 和 WPF 的 FlowDocument 都很强大,但它们都是 Windows 平台的语言。到了 MAUI,HTML 才是真正通用的格式化文本格式,既能被 Web 端直接消费,也能被移动端原生控件渲染。
6.2 迁移时最容易忽略的“数据契约”设计
我与不少从桌面端转过来的朋友交流过,大家最容易犯的错是:心里想着“我先把控件定下来,数据格式后面再说”。结果是 WPF 项目里习惯性地把内容存成 FlowDocument,到了 MAUI 端双端数据对着不上;或者一开始用了某个开源文本控件,它的专属序列化格式成了整个系统的技术债。
所以我的建议是:项目立项时,先把文本数据格式定成 HTML 子集。这个子集定义为:p、b、i、u、strong、em、ul、ol、li、a、span、br。编辑器可以用 Telerik RadEditor,也可以换成别的,但数据标准不变。这样各个端的展示才能完全统一,不会有“Android 上排版正常、Web 上错位”的奇怪情况。
6.3 授权、协作与选型落地建议
最后说点实际的选型问题。Telerik UI for .NET MAUI 是商业控件,试用版有水印和启动提示,购买前建议先确认开发席位和分发范围。MAUI 生态里开源的富文本控件不是没有,但在“跨平台一致性好、富文本编辑完整、能安全接入业务系统”这几个维度上,选择并不算充裕。
我的落地建议是:拿 2026 Q1 试用版,先写一个包含真实业务页面的 Demo——比如带审批意见、公告编辑、备注录入三个典型场景,在目标真机上跑一遍。关注三件事:编辑手感、内存占用、键盘交互。只要这三项过关,后面基本不会出大问题。
最后顺手分享一个小技巧。如果你也像我一样是从 WPF 迁过来的,第一次在 XAML 里写 RadEditor 的绑定,很容易下意识地去想“这玩意是不是也该存个 FlowDocument”。真不是——它的Text就是一段 HTML 字符串。把这个设定理解透,后面的数据存储、接口设计、多端展示,都会顺理成章地顺畅起来。想清楚这一层,你就已经在正确的路上了。