如何解决我们可以在 WPF 的 RichTextBox 中以编程方式搜索突出显示的文本吗
如下图所示,使用Microsoft WORD VBA,我们可以搜索MS WORD
文档中所有突出显示的文本。在旧版 MS Office VSTO add-in 中使用 C# 也可以实现相同的效果。 问题:我们如何以编程方式获取 highlighted
中所有 WPF RichTextBox
文本的 TexRanges?
WPF RichTextBox 显示:
获取 rtf 的代码:
string sRTF = "";
TextRange tr = new TextRange(rtbTest.Document.ContentStart,rtbTest.Document.ContentEnd);
using (MemoryStream ms = new MemoryStream())
{
tr.Save(ms,DataFormats.Rtf);
sRTF = ASCIIEncoding.Default.GetString(ms.ToArray());
}
Debug.Write(sRTF);
RTF 输出:
在以下输出中,我们可以看到突出显示的文本 test
的 rtf 是 {\lang9\highlight2\ltrch test}
。我们如何以编程方式在此处获取突出显示的文本(即 test
)。这只是一个例子。我的意思是要以编程方式获取所有突出显示的文本?
{\rtf1\ansi\ansicpg1252\uc1\htmautsp\deff2{\fonttbl{\f0\fcharset0 Times New Roman;}{\f2\fcharset0 Calibri;}}{\colortbl\red0\green0\blue0;\red255\green255\blue255;\red255\green255\blue0;}\loch\hich\dbch\pard\plain\ltrpar\itap0{\lang1033\fs18\f2\cf0 \cf0\ql{\fs22\f2 {\lang9\ltrch This is a }{\lang9\highlight2\ltrch test}{\lang9\ltrch for a WPF RichTextBox}\li0\ri0\sa200\sb0\fi0\ql\par}
{\f2 {\ltrch }\li0\ri0\sa0\sb0\fi0\ql\par}
}
}
我们能否在 WPF RichTextBox 中通过如下所示的 VBA 宏或通过遗留 VSTO 加载项中的 c# 以编程方式实现与我们在 WORD 文档中所做的一样(例如):
Selection.Find.ClearFormatting
Selection.Find.Highlight = True
With Selection.Find
.Text = ""
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindContinue
.Format = True
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
End With
Selection.Find.Execute
Selection.Find.Execute
解决方法
要了解什么是算法,可用于分析 FlowDocument
的内容,这是阅读 Flexible Content Display With Flow Documents 帖子的最佳方式,或者,例如,阅读 书中的“文档”一章MacDonald M. - C# 中的 Pro WPF 4.5。 .NET 4.5 中的 Windows Presentation Foundation(.NET 中的专家之声),2012 年。
因此,FlowDocument
包含 BlockCollection Blocks
属性。这是整个 FlowDocument
内容的顶级块。
下面的代码使用此属性递归解析和分析 FlowDocument
中的所有元素,包括搜索具有指定背景颜色的文本片段。
出于测试目的,应用程序窗口包含 Color ComboBox
,它允许使用 Set Color 按钮选择某种颜色并绘制选定的文本片段. 搜索颜色文本按钮开始扫描文档,找到指定颜色的文本并使用创建的TextRange
列表将文本重新绘制为粉红色。
MainWindow.xaml
<Window ...
Title="MainWindow" Height="350" Width="500" >
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<RichTextBox Name="rtb" BorderBrush="LightGreen"
Padding="5" Margin="10" VerticalScrollBarVisibility="Auto">
<FlowDocument>
<Paragraph>
<Run>?</Run>
</Paragraph>
</FlowDocument>
</RichTextBox>
<Grid Grid.Row="1" Margin="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto" MinWidth="60"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Label Content="Color: " />
<ComboBox x:Name="ComboColor" Grid.Column="1" Width="150" Margin="3" SelectedValuePath="Name" >
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Rectangle Fill="{Binding Name}" Width="16" Height="16" Margin="0,5,0" />
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Button Content="Set color" Grid.Row="1" Grid.Column="1" Padding="3" Margin="3" Click="SetColor_Click"/>
<Button Content="Search colored text" Grid.Row="2" Grid.Column="1" Padding="3" Margin="3" Click="Search_Click"/>
</Grid>
</Grid>
</Window>
MainWindow.xaml.cs
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
namespace WpfApp17
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ComboColor.ItemsSource = typeof(Colors).GetProperties();
ComboColor.SelectedValue = "Brown";
}
private void Search_Click(object sender,RoutedEventArgs e)
{
Parsing(rtb);
}
public void Parsing(RichTextBox rtb)
{
// Get selected color
var c = System.Drawing.Color.FromName(ComboColor.SelectedValue.ToString());
var parser = new RtfDocumentParser();
// Initialization with selected color
parser.Init(Color.FromArgb(c.A,c.R,c.G,c.B));
// Processing
parser.Analyze(rtb);
// Color found TextRanges to pink
foreach (var tr in parser.TextRanges)
{
tr.ApplyPropertyValue(TextElement.BackgroundProperty,new SolidColorBrush(Colors.Pink));
}
}
private void SetColor_Click(object sender,RoutedEventArgs e)
{
var selection = rtb.Selection;
if (!selection.IsEmpty)
{
var c = System.Drawing.Color.FromName(ComboColor.SelectedValue.ToString());
selection.ApplyPropertyValue(TextElement.BackgroundProperty,new SolidColorBrush(Color.FromArgb(c.A,c.B)));
}
}
}
}
RtfDocumentParser.cs
using System;
using System.Collections.Generic;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
namespace WpfApp17
{
public class RtfDocumentParser
{
#region Public properties
public Color SearchColor { get; private set; }
public IList<TextRange> TextRanges { get; private set; }
#endregion
#region Private properties
private TextPointer Start { get; set; }
private TextPointer End { get; set; }
#endregion
#region ctor
public RtfDocumentParser()
{
Init(Colors.Yellow);
}
#endregion
#region Public methods
public void Init(Color color)
{
SearchColor = color;
TextRanges = new List<TextRange>();
}
public void Analyze(RichTextBox rtb)
{
ParseBlockCollection(rtb.Document.Blocks);
CloseRange();
}
#endregion
#region Private methods
private void ParseBlockCollection(BlockCollection blocks)
{
foreach (var block in blocks)
{
CloseRange();
if (block is Paragraph para) { ParseInlineCollection(para.Inlines); }
else if (block is List list)
{
foreach (var litem in list.ListItems)
{
CloseRange();
TextRange range = new TextRange(litem.ElementStart,litem.ElementEnd);
ParseBlockCollection(litem.Blocks);
}
}
else if (block is Table table)
{
foreach (TableRowGroup rowGroup in table.RowGroups)
{
foreach (TableRow row in rowGroup.Rows)
{
foreach (var cell in row.Cells)
{
ParseBlockCollection(cell.Blocks);
}
}
}
}
else if (block is BlockUIContainer blockui) { /* blockui.Child */ }
else if (block is Section section) { ParseBlockCollection(section.Blocks); }
else { throw new NotImplementedException(); }
}
}
public void ParseInlineCollection(InlineCollection inlines)
{
foreach (var inline in inlines)
{
if (inline is Run r)
{
Analyze(r);
}
else if (inline is InlineUIContainer || inline is LineBreak lbreak)
{
CloseRange();
}
else if (inline is Span span)
{
ParseInlineCollection(span.Inlines);
}
}
}
private void Analyze(Run run)
{
if (run.Background is SolidColorBrush rBrush)
{
CheckPositions(rBrush.Color,run.ElementStart,run.ElementEnd);
}
else if (run.Parent is Span span && span.Background is SolidColorBrush sBrush)
{
CheckPositions(sBrush.Color,run.ElementEnd);
}
else if (End != null)
{
CloseRange();
}
}
private void CheckPositions(Color color,TextPointer start,TextPointer end)
{
if (color == SearchColor)
{
if (Start == null)
{
Start = start;
}
else if (!IsMatch(start,End))
{
TextRanges.Add(new TextRange(Start,End));
Start = start;
}
End = end;
}
else if (End != null)
{
CloseRange();
}
}
private bool IsMatch(TextPointer start,TextPointer position)
{
for (; position != null; position = position.GetNextContextPosition(LogicalDirection.Forward))
{
//var context = position.GetPointerContext(LogicalDirection.Forward);
if (start.CompareTo(position) == 0)
return true; // Match
}
return false;
}
private void CloseRange()
{
if (End is TextPointer)
{
TextRanges.Add(new TextRange(Start,End));
}
Start = End = null;
}
#endregion
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。