WPF、RichTextBox、SelectionBrush,而不是 SelectionBackColor
WPF, RichTextBox, SelectionBrush, not SelectionBackColor
在 WPF 应用程序 RichTextBox 中,我试图找到一种方法来为框中的各种单词提供背景颜色。在 System.Windows.Forms 版本的 RichTextBox 中,有一种非常简单的方法可以做到这一点:
richTextBox1.SelectionBackColor = color;
richTextBox1.AppendText(word);
但是System.Windows.Controls版本的RichTextBox只有SelectionBrush,同样的方法不行
是否可以为 RichTextBox 中的不同单词设置背景颜色?
您可以在 RichTextBox
中使用 FlowDocument
<RichTextBox>
<RichTextBox.Document>
<FlowDocument>
<Paragraph>
<Run Background="Red">Hello World</Run>
<LineBreak/>
<Run Background="Green">This is a colored</Run>
<Run>text.</Run>
</Paragraph>
</FlowDocument>
</RichTextBox.Document>
</RichTextBox>
编辑您的评论:一个(几乎)完整的示例,展示了两种不同的方法。
无论您最终如何在 UI 中显示您的文本,您都应该有某种逻辑来为突出显示的文本创建合适的数据模型。以下示例使用 TextFragment
的集合,其中每个片段都可选择标记为突出显示。
public class TextFragment
{
public TextFragment(string text, bool isHighlighted)
{
this.Text = text;
this.IsHighlighted = isHighlighted;
}
public string Text { get; private set; }
public bool IsHighlighted { get; private set; }
}
此外,对于示例,我使用 class TextEntry
来管理原始文本、搜索文本和生成的文本片段。请注意,我继承自 BaseViewModel
class,它为 INotifyPropertyChanged
相关事物实现了一些辅助函数。辅助函数 bool SetProperty<T>(ref T store, T value, [CallerMemberName]string propertyName = null)
将检查 value 和 store 是否相等,可能会用 value 更新 store 并引发 属性 changed 通知。 return 值表示该值是否真的是 different/changed.
public class TextEntry : BaseViewModel
{
public TextEntry()
{
TextParts = new ObservableCollection<TextFragment>();
}
private void UpdateTextParts()
{
TextParts.Clear();
if (string.IsNullOrEmpty(SearchText))
{
TextParts.Add(new TextFragment(OriginalText, false));
return;
}
int startAt = 0;
do
{
int next = OriginalText.IndexOf(SearchText, startAt, StringComparison.CurrentCultureIgnoreCase);
if (next == -1)
{
TextParts.Add(new TextFragment(OriginalText.Substring(startAt), false));
return;
}
else
{
if (next != startAt)
{
TextParts.Add(new TextFragment(OriginalText.Substring(startAt, next - startAt), false));
}
// add highlighted part
TextParts.Add(new TextFragment(OriginalText.Substring(next, SearchText.Length), true));
startAt = next + SearchText.Length;
}
} while (startAt < OriginalText.Length);
}
private string _OriginalText;
public string OriginalText
{
get { return _OriginalText; }
set
{
if (SetProperty(ref _OriginalText, value))
{
UpdateTextParts();
}
}
}
private string _SearchText;
public string SearchText
{
get { return _SearchText; }
set
{
if (SetProperty(ref _SearchText, value))
{
UpdateTextParts();
}
}
}
public ObservableCollection<TextFragment> TextParts { get; private set; }
}
您可以在 UI 中创建多部分文本,方法是在水平 StackPanel
中附加多个具有不同文本设置的文本块。这样,文本部分可以由 ItemsControl
管理。或者,您可以将 RichTextBox
与其 Document
属性 一起使用,但这需要在后面的代码中进行更多处理。
主要 window 中的一些初始化代码和更新 RichTextBox
示例文档的方法:
public MainWindow()
{
InitializeComponent();
var vm = new TextEntry();
grid1.DataContext = vm;
// this trigger works, but don't ask about efficiency for a bigger application
vm.TextParts.CollectionChanged += TextParts_CollectionChanged;
}
void TextParts_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
ObservableCollection<TextFragment> data = sender as ObservableCollection<TextFragment>;
var doc = richTextBox1.Document;
var paragraph = new Paragraph();
paragraph.Inlines.AddRange(data.Select(x =>
{
var run = new Run(x.Text);
if (x.IsHighlighted)
{
run.Background = Brushes.LightCoral;
}
return run;
}));
doc.Blocks.Clear();
doc.Blocks.Add(paragraph);
}
和window的XAML内容:
<Grid x:Name="grid1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="Original Text: " Margin="3"/>
<TextBlock Grid.Row="1" Grid.Column="0" Text="Searched Word: " Margin="3"/>
<TextBlock Grid.Row="2" Grid.Column="0" Text="Highlighted Text: " Margin="3"/>
<TextBlock Grid.Row="3" Grid.Column="0" Text="Highlighted Text2: " Margin="3"/>
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding OriginalText, UpdateSourceTrigger=PropertyChanged}" Margin="3"/>
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding SearchText,UpdateSourceTrigger=PropertyChanged}" Margin="3"/>
<ItemsControl Grid.Row="2" Grid.Column="1" ItemsSource="{Binding TextParts}" Margin="3" IsTabStop="False">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" VerticalAlignment="Top"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Text}">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding IsHighlighted}" Value="True">
<Setter Property="Background" Value="LightCoral"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<RichTextBox Grid.Row="3" Grid.Column="1" x:Name="richTextBox1" Margin="3" VerticalAlignment="Top" IsReadOnly="True"/>
</Grid>
预期计划:
两行文本输入。一篇为原文,一篇为搜索文。
第 3 行显示原始文本,搜索突出显示为 TextBlock
s。
第 4 行显示原始文本,搜索突出显示为 RichTextBox
。
在 WPF 应用程序 RichTextBox 中,我试图找到一种方法来为框中的各种单词提供背景颜色。在 System.Windows.Forms 版本的 RichTextBox 中,有一种非常简单的方法可以做到这一点:
richTextBox1.SelectionBackColor = color;
richTextBox1.AppendText(word);
但是System.Windows.Controls版本的RichTextBox只有SelectionBrush,同样的方法不行
是否可以为 RichTextBox 中的不同单词设置背景颜色?
您可以在 RichTextBox
FlowDocument
<RichTextBox>
<RichTextBox.Document>
<FlowDocument>
<Paragraph>
<Run Background="Red">Hello World</Run>
<LineBreak/>
<Run Background="Green">This is a colored</Run>
<Run>text.</Run>
</Paragraph>
</FlowDocument>
</RichTextBox.Document>
</RichTextBox>
编辑您的评论:一个(几乎)完整的示例,展示了两种不同的方法。
无论您最终如何在 UI 中显示您的文本,您都应该有某种逻辑来为突出显示的文本创建合适的数据模型。以下示例使用 TextFragment
的集合,其中每个片段都可选择标记为突出显示。
public class TextFragment
{
public TextFragment(string text, bool isHighlighted)
{
this.Text = text;
this.IsHighlighted = isHighlighted;
}
public string Text { get; private set; }
public bool IsHighlighted { get; private set; }
}
此外,对于示例,我使用 class TextEntry
来管理原始文本、搜索文本和生成的文本片段。请注意,我继承自 BaseViewModel
class,它为 INotifyPropertyChanged
相关事物实现了一些辅助函数。辅助函数 bool SetProperty<T>(ref T store, T value, [CallerMemberName]string propertyName = null)
将检查 value 和 store 是否相等,可能会用 value 更新 store 并引发 属性 changed 通知。 return 值表示该值是否真的是 different/changed.
public class TextEntry : BaseViewModel
{
public TextEntry()
{
TextParts = new ObservableCollection<TextFragment>();
}
private void UpdateTextParts()
{
TextParts.Clear();
if (string.IsNullOrEmpty(SearchText))
{
TextParts.Add(new TextFragment(OriginalText, false));
return;
}
int startAt = 0;
do
{
int next = OriginalText.IndexOf(SearchText, startAt, StringComparison.CurrentCultureIgnoreCase);
if (next == -1)
{
TextParts.Add(new TextFragment(OriginalText.Substring(startAt), false));
return;
}
else
{
if (next != startAt)
{
TextParts.Add(new TextFragment(OriginalText.Substring(startAt, next - startAt), false));
}
// add highlighted part
TextParts.Add(new TextFragment(OriginalText.Substring(next, SearchText.Length), true));
startAt = next + SearchText.Length;
}
} while (startAt < OriginalText.Length);
}
private string _OriginalText;
public string OriginalText
{
get { return _OriginalText; }
set
{
if (SetProperty(ref _OriginalText, value))
{
UpdateTextParts();
}
}
}
private string _SearchText;
public string SearchText
{
get { return _SearchText; }
set
{
if (SetProperty(ref _SearchText, value))
{
UpdateTextParts();
}
}
}
public ObservableCollection<TextFragment> TextParts { get; private set; }
}
您可以在 UI 中创建多部分文本,方法是在水平 StackPanel
中附加多个具有不同文本设置的文本块。这样,文本部分可以由 ItemsControl
管理。或者,您可以将 RichTextBox
与其 Document
属性 一起使用,但这需要在后面的代码中进行更多处理。
主要 window 中的一些初始化代码和更新 RichTextBox
示例文档的方法:
public MainWindow()
{
InitializeComponent();
var vm = new TextEntry();
grid1.DataContext = vm;
// this trigger works, but don't ask about efficiency for a bigger application
vm.TextParts.CollectionChanged += TextParts_CollectionChanged;
}
void TextParts_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
ObservableCollection<TextFragment> data = sender as ObservableCollection<TextFragment>;
var doc = richTextBox1.Document;
var paragraph = new Paragraph();
paragraph.Inlines.AddRange(data.Select(x =>
{
var run = new Run(x.Text);
if (x.IsHighlighted)
{
run.Background = Brushes.LightCoral;
}
return run;
}));
doc.Blocks.Clear();
doc.Blocks.Add(paragraph);
}
和window的XAML内容:
<Grid x:Name="grid1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="Original Text: " Margin="3"/>
<TextBlock Grid.Row="1" Grid.Column="0" Text="Searched Word: " Margin="3"/>
<TextBlock Grid.Row="2" Grid.Column="0" Text="Highlighted Text: " Margin="3"/>
<TextBlock Grid.Row="3" Grid.Column="0" Text="Highlighted Text2: " Margin="3"/>
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding OriginalText, UpdateSourceTrigger=PropertyChanged}" Margin="3"/>
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding SearchText,UpdateSourceTrigger=PropertyChanged}" Margin="3"/>
<ItemsControl Grid.Row="2" Grid.Column="1" ItemsSource="{Binding TextParts}" Margin="3" IsTabStop="False">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" VerticalAlignment="Top"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Text}">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding IsHighlighted}" Value="True">
<Setter Property="Background" Value="LightCoral"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<RichTextBox Grid.Row="3" Grid.Column="1" x:Name="richTextBox1" Margin="3" VerticalAlignment="Top" IsReadOnly="True"/>
</Grid>
预期计划:
两行文本输入。一篇为原文,一篇为搜索文。
第 3 行显示原始文本,搜索突出显示为 TextBlock
s。
第 4 行显示原始文本,搜索突出显示为 RichTextBox
。