为了在WPF RichtextBox中对Document进行DataBinding,到目前为止,我看到了2个解决方案,它们来自RichtextBox并添加了DependencyProperty,以及带有"代理"的解决方案.第一次或第二次都不令人满意.有人知道另一个解决方案,或者是一个能够进行DataBinding的商业RTF控件吗?普通的Textbox不是替代品,因为我们需要文本格式化.
任何的想法?
有一个更简单的方法!
您可以轻松创建一个附加DocumentXaml
(或DocumentRTF
)属性,该属性允许您绑定RichTextBox的文档.它是这样使用的,其中Autobiography是数据模型中的字符串属性:
瞧!完全可绑定的RichTextBox数据!
此属性的实现非常简单:设置属性后,将XAML(或RTF)加载到新的FlowDocument中.当FlowDocument更改时,更新属性值.
这段代码可以解决这个问题:
using System.IO; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; public class RichTextBoxHelper : DependencyObject { public static string GetDocumentXaml(DependencyObject obj) { return (string)obj.GetValue(DocumentXamlProperty); } public static void SetDocumentXaml(DependencyObject obj, string value) { obj.SetValue(DocumentXamlProperty, value); } public static readonly DependencyProperty DocumentXamlProperty = DependencyProperty.RegisterAttached( "DocumentXaml", typeof(string), typeof(RichTextBoxHelper), new FrameworkPropertyMetadata { BindsTwoWayByDefault = true, PropertyChangedCallback = (obj, e) => { var richTextBox = (RichTextBox)obj; // Parse the XAML to a document (or use XamlReader.Parse()) var xaml = GetDocumentXaml(richTextBox); var doc = new FlowDocument(); var range = new TextRange(doc.ContentStart, doc.ContentEnd); range.Load(new MemoryStream(Encoding.UTF8.GetBytes(xaml)), DataFormats.Xaml); // Set the document richTextBox.Document = doc; // When the document changes update the source range.Changed += (obj2, e2) => { if(richTextBox.Document==doc) { MemoryStream buffer = new MemoryStream(); range.Save(buffer, DataFormats.Xaml); SetDocumentXaml(richTextBox, Encoding.UTF8.GetString(buffer.ToArray())); } }; }}); }
相同的代码可用于TextFormats.RTF或TextFormats.XamlPackage.对于XamlPackage,您将拥有byte []类型的属性而不是string.
XamlPackage格式与普通XAML相比具有多个优势,尤其是包含图像等资源的能力,并且比RTF更灵活,更易于使用.
很难相信这个问题会持续15个月而没有人指出这么做的简单方法.
我知道这是一篇旧帖子,但请查看Extended WPF Toolkit.它有一个RichTextBox,支持您尝试执行的操作.
我可以给你一个好的解决方案,你可以使用它,但在我做之前,我将尝试解释为什么Document 不是 DependencyProperty开始.
在RichTextBox控件的生命周期中,Document属性通常不会更改.RichTextBox使用FlowDocument初始化.该文档显示,可以通过多种方式进行编辑和修改,但Document属性的基础值仍然是FlowDocument的一个实例.因此,它确实没有理由成为依赖属性,即Bindable.如果您有多个引用此FlowDocument的位置,则只需要一次引用.由于它在任何地方都是相同的实例,因此每个人都可以访问这些更改.
我不认为FlowDocument支持文档更改通知,但我不确定.
话虽如此,这是一个解决方案.在开始之前,由于RichTextBox未实现INotifyPropertyChanged且Document不是依赖项属性,因此当RichTextBox的Document属性更改时,我们没有通知,因此绑定只能是OneWay.
创建一个将提供FlowDocument的类.绑定需要存在依赖属性,因此该类继承自DependencyObject.
class HasDocument : DependencyObject { public static readonly DependencyProperty DocumentProperty = DependencyProperty.Register("Document", typeof(FlowDocument), typeof(HasDocument), new PropertyMetadata(new PropertyChangedCallback(DocumentChanged))); private static void DocumentChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) { Debug.WriteLine("Document has changed"); } public FlowDocument Document { get { return GetValue(DocumentProperty) as FlowDocument; } set { SetValue(DocumentProperty, value); } } }
在XAML中创建一个带有富文本框的窗口.
为窗口提供HasDocument类型的字段.
HasDocument hasDocument;
Window构造函数应该创建绑定.
hasDocument = new HasDocument(); InitializeComponent(); Binding b = new Binding("Document"); b.Source = richTextBox; b.Mode = BindingMode.OneWay; BindingOperations.SetBinding(hasDocument, HasDocument.DocumentProperty, b);
如果您希望能够在XAML中声明绑定,则必须使您的HasDocument类派生自FrameworkElement,以便可以将其插入到逻辑树中.
现在,如果您要更改HasDocument上的Document属性,则富文本框的Document也将更改.
FlowDocument d = new FlowDocument(); Paragraph g = new Paragraph(); Run a = new Run(); a.Text = "I showed this using a binding"; g.Inlines.Add(a); d.Blocks.Add(g); hasDocument.Document = d;
我稍微调整了以前的代码.首先是范围.改变对我不起作用.在我更改range.Changed到richTextBox.TextChanged之后,事实证明TextChanged事件处理程序可以递归地调用SetDocumentXaml,所以我提供了保护它.我还使用了XamlReader/XamlWriter而不是TextRange.
public class RichTextBoxHelper : DependencyObject { private static HashSet_recursionProtection = new HashSet (); public static string GetDocumentXaml(DependencyObject obj) { return (string)obj.GetValue(DocumentXamlProperty); } public static void SetDocumentXaml(DependencyObject obj, string value) { _recursionProtection.Add(Thread.CurrentThread); obj.SetValue(DocumentXamlProperty, value); _recursionProtection.Remove(Thread.CurrentThread); } public static readonly DependencyProperty DocumentXamlProperty = DependencyProperty.RegisterAttached( "DocumentXaml", typeof(string), typeof(RichTextBoxHelper), new FrameworkPropertyMetadata( "", FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, (obj, e) => { if (_recursionProtection.Contains(Thread.CurrentThread)) return; var richTextBox = (RichTextBox)obj; // Parse the XAML to a document (or use XamlReader.Parse()) try { var stream = new MemoryStream(Encoding.UTF8.GetBytes(GetDocumentXaml(richTextBox))); var doc = (FlowDocument)XamlReader.Load(stream); // Set the document richTextBox.Document = doc; } catch (Exception) { richTextBox.Document = new FlowDocument(); } // When the document changes update the source richTextBox.TextChanged += (obj2, e2) => { RichTextBox richTextBox2 = obj2 as RichTextBox; if (richTextBox2 != null) { SetDocumentXaml(richTextBox, XamlWriter.Save(richTextBox2.Document)); } }; } ) ); }
到目前为止,这似乎是最简单的方法,并没有在任何这些答案中显示.
在视图模型中只有Text
变量.
为什么不使用FlowDocumentScrollViewer?
创建一个具有RichTextBox的UserControl.现在添加以下依赖项属性:
public FlowDocument Document { get { return (FlowDocument)GetValue(DocumentProperty); } set { SetValue(DocumentProperty, value); } } public static readonly DependencyProperty DocumentProperty = DependencyProperty.Register("Document", typeof(FlowDocument), typeof(RichTextBoxControl), new PropertyMetadata(OnDocumentChanged)); private static void OnDocumentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { RichTextBoxControl control = (RichTextBoxControl) d; if (e.NewValue == null) control.RTB.Document = new FlowDocument(); //Document is not amused by null :) control.RTB.Document = document; }
这个解决方案可能就是你在某处找到的"代理"解决方案..但是...... RichTextBox根本没有Document作为DependencyProperty ......所以你必须以另一种方式做到这一点......
HTH