Caliburn Micro+WinRT下RichEditBox双向绑定失效问题排查
解决WinRT RichEditBox与Caliburn Micro双向绑定失效问题
我之前也碰到过这个情况——RichEditBox没有现成的Text属性用来绑定,用附加属性做单向绑定没问题,但编辑后同步不回ViewModel,确实头疼。结合Caliburn Micro和WinRT的特性,给你一套可行的方案:
核心思路
RichEditBox的内容存在Document属性里,我们需要用附加属性作为中间层,同时处理两个方向的同步:
- 从ViewModel到View:把ViewModel的字符串属性转成RichEditBox的Document内容
- 从View到ViewModel:监听RichEditBox的内容变化,把Document转成字符串更新附加属性,进而同步到ViewModel
步骤1:实现附加属性辅助类
创建一个静态辅助类,封装附加属性和同步逻辑:
using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Documents; public static class RichEditBoxBindingHelper { // 定义附加属性:绑定的内容字符串(支持RTF或纯文本) public static readonly DependencyProperty BindableContentProperty = DependencyProperty.RegisterAttached( "BindableContent", typeof(string), typeof(RichEditBoxBindingHelper), new PropertyMetadata(null, OnBindableContentChanged)); public static string GetBindableContent(DependencyObject obj) { return (string)obj.GetValue(BindableContentProperty); } public static void SetBindableContent(DependencyObject obj, string value) { obj.SetValue(BindableContentProperty, value); } // 处理附加属性变化(ViewModel -> View) private static void OnBindableContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is not RichEditBox richEditBox) return; // 先移除事件监听,避免设置内容时触发反向同步导致循环 richEditBox.TextChanged -= RichEditBox_ContentChanged; var newContent = e.NewValue as string; if (!string.IsNullOrEmpty(newContent)) { // 这里用RTF格式,要纯文本就换成FormatPlainText richEditBox.Document.SetText(TextSetOptions.FormatRtf, newContent); } // 重新注册事件,监听用户编辑操作 richEditBox.TextChanged += RichEditBox_ContentChanged; } // 处理RichEditBox内容变化(View -> ViewModel) private static void RichEditBox_ContentChanged(object sender, RoutedEventArgs e) { if (sender is not RichEditBox richEditBox) return; // 把Document内容转成字符串(和上面的格式保持一致) richEditBox.Document.GetText(TextGetOptions.FormatRtf, out string currentContent); // 更新附加属性,触发ViewModel的绑定更新 SetBindableContent(richEditBox, currentContent); } }
步骤2:ViewModel配置
确保你的ViewModel继承自Caliburn Micro的PropertyChangedBase(或Screen),并定义绑定属性:
using Caliburn.Micro; public class YourViewModel : PropertyChangedBase { private string _editorContent; // 绑定的内容属性,初始值会同步到RichEditBox public string EditorContent { get => _editorContent; set { if (_editorContent != value) { _editorContent = value; NotifyOfPropertyChange(() => EditorContent); } } } // 可以在构造函数里设置初始值测试 public YourViewModel() { EditorContent = @"{\rtf1\ansi\deff0 {\colortbl ;\red255\green0\blue0;} This is \cf1 red text\cf0 !}"; } }
步骤3:XAML绑定
在XAML里引用辅助类,给RichEditBox设置附加属性的双向绑定:
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:YourNamespace" xmlns:cal="http://www.caliburnproject.org"> <RichEditBox local:RichEditBoxBindingHelper.BindableContent="{Binding EditorContent, Mode=TwoWay}" Width="400" Height="200"/> </Page>
关键注意点
- 避免循环更新:在更新RichEditBox内容前先移除
TextChanged监听,设置完成后再重新注册——不然ViewModel更新属性时,会触发RichEditBox的内容变化事件,又把值同步回去,造成循环。 - 格式一致性:
SetText和GetText的格式参数要保持一致(要么都用RTF,要么都用纯文本),不然内容会乱码或丢失格式。 - Caliburn Micro支持:Caliburn Micro对DependencyProperty的双向绑定完全兼容,只要你显式设置
Mode=TwoWay,并且ViewModel正确触发NotifyOfPropertyChange。
这样配置后,初始值会正常显示,用户编辑RichEditBox的内容后,也会实时同步回ViewModel的EditorContent属性。
内容的提问来源于stack exchange,提问作者M.kazem Akhgary
相关产品推荐
相关产品推荐

