You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WPF .NET Core中SelectionTextBrush失效,求高亮文本变色解决方案

.NET Core 7下WPF RichTextBox选中文本变色的替代方案

由于.NET Core 7中WPF的RichTextBox无法通过原有.NET Framework的样式方案修改选中文本颜色,这里提供一种基于事件监听和FlowDocument文本元素操作的替代实现。

核心思路

通过监听SelectionChanged事件,手动遍历选中范围内的文本元素,修改其前景色;同时记录元素的原始颜色,在取消选中时恢复样式。针对部分选中单个Run的场景,还可以拆分Run实现精确的片段变色。

基础实现(全Run选中变色)

XAML代码

<RichTextBox x:Name="MyRichTextBox" SelectionChanged="MyRichTextBox_SelectionChanged" Background="#2D2D2D">
    <FlowDocument>
        <Paragraph Foreground="#E0E0E0">
            测试文本:选中这段文字查看颜色变化,切换选中范围会自动恢复原始样式。
        </Paragraph>
    </FlowDocument>
</RichTextBox>

后台代码

using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;

namespace WpfApp
{
    public partial class MainWindow : Window
    {
        // 记录被修改的Run及其原始前景色
        private readonly Dictionary<Run, Brush> _originalBrushes = new Dictionary<Run, Brush>();

        public MainWindow()
        {
            InitializeComponent();
        }

        private void MyRichTextBox_SelectionChanged(object sender, RoutedEventArgs e)
        {
            var rtb = sender as RichTextBox;
            if (rtb == null) return;

            // 恢复之前选中元素的原始颜色
            foreach (var item in _originalBrushes)
            {
                item.Key.Foreground = item.Value;
            }
            _originalBrushes.Clear();

            if (rtb.Selection.IsEmpty) return;

            var start = rtb.Selection.Start;
            var end = rtb.Selection.End;

            // 遍历选中区域内的所有Run元素
            while (start.CompareTo(end) <= 0)
            {
                if (start.Parent is Run run)
                {
                    if (!_originalBrushes.ContainsKey(run))
                    {
                        _originalBrushes[run] = run.Foreground;
                    }
                    // 设置选中文本的颜色,这里用白色示例
                    run.Foreground = Brushes.White;
                }

                // 移动到下一个文本节点
                if (!start.MoveToNextContextPosition(LogicalDirection.Forward))
                {
                    break;
                }
            }
        }
    }
}

进阶实现(支持部分Run选中变色)

如果需要精确到选中的文本片段(而非整个Run),可以拆分Run元素,仅修改选中的部分:

补充后台代码

private void MyRichTextBox_SelectionChanged(object sender, RoutedEventArgs e)
{
    var rtb = sender as RichTextBox;
    if (rtb == null) return;

    // 恢复之前选中元素的原始颜色
    foreach (var item in _originalBrushes)
    {
        item.Key.Foreground = item.Value;
    }
    // 合并相邻同样式的Run,避免过多元素影响性能
    MergeAdjacentRuns(rtb.Document);
    _originalBrushes.Clear();

    if (rtb.Selection.IsEmpty) return;

    ProcessSelectionRange(rtb.Selection.Start, rtb.Selection.End);
}

// 处理选中范围的文本元素
private void ProcessSelectionRange(TextPointer start, TextPointer end)
{
    while (start.CompareTo(end) < 0)
    {
        if (start.Parent is Run run)
        {
            var runStart = run.ContentStart;
            var runEnd = run.ContentEnd;

            // 场景1:Run完全在选中范围内
            if (runStart.CompareTo(start) >= 0 && runEnd.CompareTo(end) <= 0)
            {
                RecordAndChangeRunColor(run);
                start = runEnd;
            }
            // 场景2:Run的后半部分被选中
            else if (runStart.CompareTo(start) < 0 && runEnd.CompareTo(start) > 0 && runEnd.CompareTo(end) <= 0)
            {
                var splitRun = run.Split(start);
                RecordAndChangeRunColor(splitRun);
                start = runEnd;
            }
            // 场景3:Run的前半部分被选中
            else if (runStart.CompareTo(start) >= 0 && runStart.CompareTo(end) < 0 && runEnd.CompareTo(end) > 0)
            {
                var splitRun = run.Split(end);
                RecordAndChangeRunColor(run);
                start = end;
            }
            // 场景4:Run的中间部分被选中
            else if (runStart.CompareTo(start) < 0 && runEnd.CompareTo(end) > 0)
            {
                var middleRun = run.Split(start);
                middleRun.Split(end);
                RecordAndChangeRunColor(middleRun);
                start = end;
            }
            else
            {
                start = runEnd;
            }
        }
        else
        {
            if (!start.MoveToNextContextPosition(LogicalDirection.Forward))
            {
                break;
            }
        }
    }
}

// 记录Run原始颜色并修改选中颜色
private void RecordAndChangeRunColor(Run run)
{
    if (!_originalBrushes.ContainsKey(run))
    {
        _originalBrushes[run] = run.Foreground;
    }
    run.Foreground = Brushes.White;
}

// 合并相邻同样式的Run,优化文档性能
private void MergeAdjacentRuns(FlowDocument doc)
{
    foreach (var para in doc.Blocks.OfType<Paragraph>())
    {
        var runs = para.Inlines.OfType<Run>().ToList();
        for (int i = runs.Count - 1; i > 0; i--)
        {
            var current = runs[i];
            var previous = runs[i - 1];

            // 判断样式是否一致(可根据需求添加更多样式判断)
            if (current.Foreground == previous.Foreground &&
                current.FontFamily == previous.FontFamily &&
                current.FontSize == previous.FontSize &&
                current.FontStyle == previous.FontStyle &&
                current.FontWeight == previous.FontWeight)
            {
                previous.Text += current.Text;
                para.Inlines.Remove(current);
            }
        }
    }
}

方案说明

  • 该实现完全基于WPF原生API,兼容.NET Core 7及更高版本。
  • 基础实现适合对选中精度要求不高的场景,代码简洁;进阶实现支持精确到文本片段的变色,但逻辑稍复杂。
  • 通过合并相邻Run的操作,可以避免频繁拆分导致文档元素过多,优化性能。

内容的提问来源于stack exchange,提问作者user21301374

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 10:52:51