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

如何为WPF RichTextBox中的不同类型日志段落设置颜色

问题

我有一个WPF RichTextBox控件,用于读取日志文件并在界面展示。日志文件中的内容会作为单个段落追加到RichTextBox中,日志包含“Info”“Warning”“Error”类型的段落,段落之间用---分隔,示例如下:

8/23 17:40:54:761  6948 - Warning - Logging: Set ABC
-----------------------------------------
08/23 17:40:54:772  6948 - Info - Logging: FullFileName:/XYZ/
                           Message:Some Message 
                           Type : Information                          
---------------------------------------------
08/23 17:40:54:844  6948 - Error - Logging: End of Control_Loaded  

我希望将其中的“Error”段落设置为红色,“Info”段落设置为绿色,请问该如何实现?

以下是我的WPF RichTextBox代码:

<RichTextBox Background='Black' Foreground="White" IsReadOnly="True" 
             AcceptsReturn="True" UndoLimit="0" 
             IsUndoEnabled="False"
             TextChanged="TextBoxBase_OnTextChanged" >
             <FlowDocument>
                 <Paragraph>
                     <Run Text="{Binding Contents, Mode=TwoWay}"/>
                 </Paragraph>
             </FlowDocument>
</RichTextBox>
private void TextBoxBase_OnTextChanged(object sender, TextChangedEventArgs e)
{
    var textBox = (RichTextBox)(sender);
    var fileMonitorViewModel = ((FileMonitorViewModel)textBox.DataContext);            
    //textBox.ColorParagraphs();
}
解决方案

你当前用单个Run绑定全部日志的方式无法实现分段着色,因为所有内容都在同一个Paragraph里。需要改为按日志分段构建FlowDocument的方式,具体步骤如下:

1. 修改XAML结构

移除预先定义的Paragraph和Run,让RichTextBox的FlowDocument为空:

<RichTextBox x:Name="LogRichTextBox" Background='Black' Foreground="White" 
             IsReadOnly="True" AcceptsReturn="True" UndoLimit="0" 
             IsUndoEnabled="False"/>

2. 实现日志分段与着色逻辑

添加一个方法解析日志文本,按分隔符拆分后为不同类型的段落设置对应颜色:

private void UpdateLogDisplay(string fullLogText)
{
    LogRichTextBox.Document.Blocks.Clear();
    // 按分隔符拆分日志,过滤空段落
    var logSegments = fullLogText.Split(new[] { "-----------------------------------------" }, 
        StringSplitOptions.RemoveEmptyEntries);
    
    foreach (var segment in logSegments)
    {
        var trimmedSegment = segment.Trim();
        if (string.IsNullOrWhiteSpace(trimmedSegment)) continue;
        
        var paragraph = new Paragraph { Margin = new Thickness(0) }; // 取消段落默认边距,让日志更紧凑
        
        // 根据日志类型设置文字颜色
        if (trimmedSegment.Contains("- Error -"))
        {
            paragraph.Foreground = Brushes.Red;
        }
        else if (trimmedSegment.Contains("- Info -"))
        {
            paragraph.Foreground = Brushes.Green;
        }
        else
        {
            paragraph.Foreground = Brushes.White; // Warning保持默认白色
        }
        
        paragraph.Inlines.Add(new Run(trimmedSegment + Environment.NewLine + Environment.NewLine));
        LogRichTextBox.Document.Blocks.Add(paragraph);
    }
    
    // 自动滚动到最新日志位置
    LogRichTextBox.ScrollToEnd();
}

3. 触发日志更新

当日志内容变化时调用上述方法。如果采用MVVM架构,可以通过属性变更通知触发:

在ViewModel中添加属性变更事件:

private string _contents;
public string Contents
{
    get => _contents;
    set
    {
        if (_contents != value)
        {
            _contents = value;
            OnPropertyChanged(nameof(Contents));
            LogUpdated?.Invoke(this, _contents);
        }
    }
}

public event EventHandler<string> LogUpdated;

// 实现INotifyPropertyChanged接口的OnPropertyChanged方法
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

在窗口构造函数中订阅事件并更新UI:

public YourWindow()
{
    InitializeComponent();
    var vm = (FileMonitorViewModel)DataContext;
    vm.LogUpdated += Vm_LogUpdated;
}

private void Vm_LogUpdated(object sender, string logText)
{
    // 确保在UI线程执行更新
    Dispatcher.Invoke(() => UpdateLogDisplay(logText));
}

4. 绑定式替代方案

如果需要保留数据绑定,可以自定义值转换器,直接将日志文本转换为带颜色段落的FlowDocument:

public class LogToFlowDocumentConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var logText = value as string;
        if (string.IsNullOrEmpty(logText)) return new FlowDocument();
        
        var doc = new FlowDocument();
        var segments = logText.Split(new[] { "-----------------------------------------" }, 
            StringSplitOptions.RemoveEmptyEntries);
        
        foreach (var segment in segments)
        {
            var trimmed = segment.Trim();
            if (string.IsNullOrWhiteSpace(trimmed)) continue;
            
            var para = new Paragraph { Margin = new Thickness(0) };
            if (trimmed.Contains("- Error -"))
                para.Foreground = Brushes.Red;
            else if (trimmed.Contains("- Info -"))
                para.Foreground = Brushes.Green;
            else
                para.Foreground = Brushes.White;
            
            para.Inlines.Add(new Run(trimmed + Environment.NewLine + Environment.NewLine));
            doc.Blocks.Add(para);
        }
        
        return doc;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

在XAML中引用转换器并绑定Document属性:

<Window.Resources>
    <local:LogToFlowDocumentConverter x:Key="LogConverter"/>
</Window.Resources>

<RichTextBox Background='Black' IsReadOnly="True" 
             AcceptsReturn="True" UndoLimit="0" 
             IsUndoEnabled="False"
             Document="{Binding Contents, Converter={StaticResource LogConverter}}"/>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 14:57:22