当前位置:  开发笔记 > 编程语言 > 正文

Richtextbox wpf绑定

如何解决《Richtextboxwpf绑定》经验,为你挑选了7个好方法。

为了在WPF RichtextBox中对Document进行DataBinding,到目前为止,我看到了2个解决方案,它们来自RichtextBox并添加了DependencyProperty,以及带有"代理"的解决方案.第一次或第二次都不令人满意.有人知道另一个解决方案,或者是一个能够进行DataBinding的商业RTF控件吗?普通的Textbox不是替代品,因为我们需要文本格式化.

任何的想法?



1> Ray Burns..:

有一个更简单的方法!

您可以轻松创建一个附加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个月而没有人指出这么做的简单方法.


双向对我不起作用(使用Rtf).`range.Changed`事件永远不会被调用.
@Kelly,使用DataFormats.Rtf,这可以解决多个richtextboxes问题.

2> Brian Laguna..:

我知道这是一篇旧帖子,但请查看Extended WPF Toolkit.它有一个RichTextBox,支持您尝试执行的操作.


Extended WPF Toolkit中的RichTextBox非常慢,我不推荐它.
@ViktorLaCroix你意识到这只是WPF RichTextBox上有一个额外的属性吗?

3> Szymon Rozga..:

我可以给你一个好的解决方案,你可以使用它,但在我做之前,我将尝试解释为什么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;


好的答案+1,但有一个狡辩:有理由使Document属性成为依赖属性 - 以便于将控件与MVVM模式一起使用.

4> Krzysztof..:

我稍微调整了以前的代码.首先是范围.改变对我不起作用.在我更改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));
                    }
                };
            }
        )
    );
}



5> FakeCaleb..:
 
     
         
             
          
     
 

到目前为止,这似乎是最简单的方法,并没有在任何这些答案中显示.

在视图模型中只有Text变量.



6> paparazzo..:

为什么不使用FlowDocumentScrollViewer?



7> Arcturus..:

创建一个具有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

推荐阅读
雨天是最美
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有