PS:本文使用RichTextBox仅基于MVVM模式。
- RichTextBox富文本框,主要应用场景考虑需要多行内容以及其他类型的内容如图片等,WPF提供的富文本框是基于FlowDocument(流文档),接下来会先解释一下什么是流文档。
- FlowDocument,先给大家看看官方的定义:
流文档旨在根据窗口大小、设备分辨率和其他环境变量来“重排内容”。 此外,流文档还具有很多内置功能,包括搜索、能够优化可读性的查看模式以及更改字体大小和外观的功能。 当易读性是文档的主要使用要求时,最适合使用流文档。
我个人理解的流文档主要是为了解决那些需要对文本内容排列布局、添加图片、放大缩小等使用场景。因此需要先了解流文档的内容格式。先来看看官方给出的模型:
- 蓝色背景为Block(块级元素)就是FlowDocument内的主要元素,一篇文章的主要构成为一个个的段落,我个人的理解是Block级别元素就是段落,FlowDocument主要包括Paragarph(章节)、Section(区域)、List(列表)、Table(表格)、BolckUIContainer(块级别UI容器)。
- 绿色背景的Inline(内联元素)是Block里的主力,用于承载需要输入的内容。这里就不再展开了,大家可以自己去动手试试。
- 黄色背景的UIElement对应的是具体的UI控件。
FlowDocument结构应该清晰了吧,FlowDocment可以包含多个Block,Block里面可以包含多个Inline。这很显然是一个的盒子结构(一层套着一层)。
- 介绍完流文档之后,我们重新回到RichTextBox,内部是由FlowDocument构成的,当我们使用MVVM模式时,我们需要将流文档的内容与具体的变量做绑定,以达到实时读取和修改的能力。因为RichTextBox控件中Document属性是不支持绑定的,所以需要对控件重写。通过添加附加属性的方式为Document添加绑定。
后端代码:
public partial class BindableRichTextBox : RichTextBox
{
public new FlowDocument Document
{
get { return (FlowDocument)GetValue(DocumentProperty); }
set { SetValue(DocumentProperty, value); }
}
// Using a DependencyProperty as the backing store for Document. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DocumentProperty =
DependencyProperty.Register("Document", typeof(FlowDocument), typeof(BindableRichTextBox), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnDucumentChanged)));
private static void OnDucumentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
RichTextBox rtb = (RichTextBox)d;
rtb.Document = (FlowDocument)e.NewValue;
}
}
前端代码只要把根标签改为RichTextBox即可
<RichTextBox
x:Class="CutScreenPictureApp.BindableRichTextBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:CutScreenPictureApp"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d" />
这样就可以使用绑定了,绑定的类型要求为FlowDocument。一定要初始化!!!
可以利用绑定后的FlowDocument对象动态添加图片,这里展示我的代码:
_CheckFullTextBoxCommand = new RelayCommand<string>((data) =>
{
TextRange textRange = new TextRange(
// TextPointer to the start of content in the RichTextBox.
_FlowDocument.ContentStart,
// TextPointer to the end of content in the RichTextBox.
_FlowDocument.ContentEnd
);
try
{
if (!string.IsNullOrEmpty(fileName) && File.Exists(fileName))
{
// 添加图片控件
System.Windows.Controls.Image image = new System.Windows.Controls.Image()
{
Source = new BitmapImage(new Uri(fileName)),
Width = 60,
Height = 60,
};
InlineUIContainer inlineUIContainer = new InlineUIContainer(image);
Paragraph paragraph = _FlowDocument.Blocks.Last() as Paragraph;
paragraph.Inlines.Add(inlineUIContainer);
}
}
catch ( Exception ex )
{
Debug.WriteLine( ex );
}
System.Windows.MessageBox.Show(_FlowDocument == null ? "" : textRange.Text);
});
TextRange是用来展示内容的,主要是通过InlineUIContainer绑定控件。
接下来讲讲InlineUIContainer,我写到这里也很好奇,InlineUIContainer是干嘛的?于是我去查阅了官方文档,先给出官方的定义:
提供一个内联内容元素,该元素使 UIElement 类型能够嵌入 到 RichTextBlock 的内容中。
[Microsoft.UI.Xaml.Markup.ContentProperty(Name="Child")]
[Windows.Foundation.Metadata.Activatable(65536, "Microsoft.UI.Xaml.WinUIContract")]
[Windows.Foundation.Metadata.ContractVersion(typeof(Microsoft.UI.Xaml.WinUIContract), 65536)]
[Windows.Foundation.Metadata.MarshalingBehavior(Windows.Foundation.Metadata.MarshalingType.Agile)]
[Windows.Foundation.Metadata.Threading(Windows.Foundation.Metadata.ThreadingModel.Both)]
public sealed class InlineUIContainer : Inline
显然可以看到它是继承自Inline的,Inline(内联)是流文档的主要内容区域,通俗来说这玩意主要用来展示图片的,给出官方的举例代码:
<RichTextBlock>
<Paragraph>
<Italic>This is an inline image.</Italic>
<InlineUIContainer>
<Image Source="Assets/SmallLogo.png" Height="30" Width="30"/>
</InlineUIContainer>
Mauris auctor tincidunt auctor.
</Paragraph>
</RichTextBlock>
使用 InlineUIContainer 对象的最常见方案是将图像引入文本内容。 使用图像的新 Image 对象作为 InlineUIContainer 的子内容。 如果不希望图像缩放到自然图像大小,请设置图像的 “高度 ”和“ 宽度 ”。