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 行显示原始文本,搜索突出显示为 TextBlocks。

第 4 行显示原始文本,搜索突出显示为 RichTextBox