Ale*_*ker 70 data-binding wpf richtextbox
为了在WPF RichtextBox中对Document进行DataBinding,到目前为止,我看到了2个解决方案,它们来自RichtextBox并添加了DependencyProperty,以及带有"代理"的解决方案.第一次或第二次都不令人满意.有人知道另一个解决方案,或者是一个能够进行DataBinding的商业RTF控件吗?普通的Textbox不是替代品,因为我们需要文本格式化.
任何的想法?
Ray*_*rns 99
有一个更简单的方法!
您可以轻松创建一个附加DocumentXaml(或DocumentRTF)属性,该属性允许您绑定RichTextBox的文档.它是这样使用的,其中Autobiography是数据模型中的字符串属性:
<TextBox Text="{Binding FirstName}" />
<TextBox Text="{Binding LastName}" />
<RichTextBox local:RichTextBoxHelper.DocumentXaml="{Binding Autobiography}" />
Run Code Online (Sandbox Code Playgroud)
瞧!完全可绑定的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()));
}
};
}});
}
Run Code Online (Sandbox Code Playgroud)
相同的代码可用于TextFormats.RTF或TextFormats.XamlPackage.对于XamlPackage,您将拥有byte []类型的属性而不是string.
XamlPackage格式与普通XAML相比具有多个优势,尤其是包含图像等资源的能力,并且比RTF更灵活,更易于使用.
很难相信这个问题会持续15个月而没有人指出这么做的简单方法.
Bri*_*nas 22
我知道这是一篇旧帖子,但请查看Extended WPF Toolkit.它有一个RichTextBox,支持您尝试执行的操作.
Szy*_*zga 16
我可以给你一个好的解决方案,你可以使用它,但在我做之前,我将尝试解释为什么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); }
}
}
Run Code Online (Sandbox Code Playgroud)
在XAML中创建一个带有富文本框的窗口.
<Window x:Class="samples.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Flow Document Binding" Height="300" Width="300"
>
<Grid>
<RichTextBox Name="richTextBox" />
</Grid>
</Window>
Run Code Online (Sandbox Code Playgroud)
为窗口提供HasDocument类型的字段.
HasDocument hasDocument;
Run Code Online (Sandbox Code Playgroud)
Window构造函数应该创建绑定.
hasDocument = new HasDocument();
InitializeComponent();
Binding b = new Binding("Document");
b.Source = richTextBox;
b.Mode = BindingMode.OneWay;
BindingOperations.SetBinding(hasDocument, HasDocument.DocumentProperty, b);
Run Code Online (Sandbox Code Playgroud)
如果您希望能够在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;
Run Code Online (Sandbox Code Playgroud)
Krz*_*tof 15
我稍微调整了以前的代码.首先是范围.改变对我不起作用.在我更改range.Changed到richTextBox.TextChanged之后,事实证明TextChanged事件处理程序可以递归地调用SetDocumentXaml,所以我提供了保护它.我还使用了XamlReader/XamlWriter而不是TextRange.
public class RichTextBoxHelper : DependencyObject
{
private static HashSet<Thread> _recursionProtection = new HashSet<Thread>();
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));
}
};
}
)
);
}
Run Code Online (Sandbox Code Playgroud)
Fak*_*leb 11
<RichTextBox>
<FlowDocument PageHeight="180">
<Paragraph>
<Run Text="{Binding Text, Mode=TwoWay}"/>
</Paragraph>
</FlowDocument>
</RichTextBox>
Run Code Online (Sandbox Code Playgroud)
到目前为止,这似乎是最简单的方法,并没有在任何这些答案中显示.
在视图模型中只有Text变量.
创建一个具有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;
}
Run Code Online (Sandbox Code Playgroud)
这个解决方案可能就是你在某处找到的"代理"解决方案..但是...... RichTextBox根本没有Document作为DependencyProperty ......所以你必须以另一种方式做到这一点......
HTH
krzysztof的这个答案/sf/answers/209249421/满足了我的大部分需求。但该代码的一个问题(我面临的是),绑定不适用于多个控件。所以我改变了一个基础的实现。所以它也适用于同一窗口中的多个控件。_recursionProtectionGuid
public class RichTextBoxHelper : DependencyObject
{
private static List<Guid> _recursionProtection = new List<Guid>();
public static string GetDocumentXaml(DependencyObject obj)
{
return (string)obj.GetValue(DocumentXamlProperty);
}
public static void SetDocumentXaml(DependencyObject obj, string value)
{
var fw1 = (FrameworkElement)obj;
if (fw1.Tag == null || (Guid)fw1.Tag == Guid.Empty)
fw1.Tag = Guid.NewGuid();
_recursionProtection.Add((Guid)fw1.Tag);
obj.SetValue(DocumentXamlProperty, value);
_recursionProtection.Remove((Guid)fw1.Tag);
}
public static readonly DependencyProperty DocumentXamlProperty = DependencyProperty.RegisterAttached(
"DocumentXaml",
typeof(string),
typeof(RichTextBoxHelper),
new FrameworkPropertyMetadata(
"",
FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
(obj, e) =>
{
var richTextBox = (RichTextBox)obj;
if (richTextBox.Tag != null && _recursionProtection.Contains((Guid)richTextBox.Tag))
return;
// Parse the XAML to a document (or use XamlReader.Parse())
try
{
string docXaml = GetDocumentXaml(richTextBox);
var stream = new MemoryStream(Encoding.UTF8.GetBytes(docXaml));
FlowDocument doc;
if (!string.IsNullOrEmpty(docXaml))
{
doc = (FlowDocument)XamlReader.Load(stream);
}
else
{
doc = new FlowDocument();
}
// 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));
}
};
}
)
);
}
Run Code Online (Sandbox Code Playgroud)
为了完整起见,让我在ray-burns的原始答案/sf/answers/184924211/中添加几行。这就是如何使用助手。
<RichTextBox local:RichTextBoxHelper.DocumentXaml="{Binding Autobiography}" />
Run Code Online (Sandbox Code Playgroud)