Click here to Skip to main content
14,773,758 members
Articles » Platforms, Frameworks & Libraries » Windows Presentation Foundation » Controls
Posted 14 Feb 2010

Tagged as


68 bookmarked

Diagnostic Trace Display Using WPF

Rate me:
Please Sign up or sign in to vote.
4.93/5 (20 votes)
25 Aug 2012CPOL
A WPF control and FlowDocument to display Trace output in a running application.



Often times, it becomes necessary in an application to display ongoing and detailed status messages to the user. Capturing System.Diagnostics.Trace output by subclassing the TraceListener and redirecting the Trace output to someplace in the UI is a very simple way to accomplish that. This article presents a mechanism to capture Trace output and display it in a WPF user interface.


I've had a WinForms version of this forever that has been quite handy. Replicating that in WPF took a little more doing than I thought it would, but that's mostly due to the steep WPF learning curve. In the end, the WPF FlowDocument made it a breeze to apply formatting to Trace output, especially compared to the old Win32 RichTextBox.

Using the Code

The attached code includes two mechanisms for redirecting Trace output to the UI.


This is a simple UserControl that can be placed in the UI like any other control. When it is loaded in the visual tree, it creates a TraceListener and will immediately begin displaying Trace output in simple black and white text.


This is the topmost textbox in the screenshot above.


If you want formatting above black and white, there is the TraceDocument. This class inherits from FlowDocument and can be used anywhere that a FlowDocument can be displayed.

In a RichTextBox (the middle window):

<RichTextBox IsReadOnly="True" AllowDrop="False" 
      IsUndoEnabled="False"> <local:TraceDocument/>

Or in a FlowDocumentReader (the bottom window):

<FlowDocumentReader ViewingMode="Scroll">

The FlowDocumentReader includes built-in search and zoom.

The nice thing about using a FlowDocument is that different types of trace messages can be styled in the XAML and the TraceDocument will bind TraceEventTypes to similarly named styles.

public enum TraceEventType
    Critical = 1,
    Error = 2,
    Warning = 4,
    Information = 8,
    Verbose = 16,
    Start = 256,
    Stop = 512,
    Suspend = 1024,
    Resume = 2048,
    Transfer = 4096,
<FlowDocument  x:Class="TraceTextBox.TraceDocument"
               FontSize="12" FontFamily="Courier New" PageWidth="10000"  >
        <Style TargetType="{x:Type Paragraph}" x:Key="Information">
            <Setter Property="Margin" Value="0"/>
            <Setter Property="KeepTogether" Value="True"/>
        <Style TargetType="{x:Type Paragraph}" x:Key="Error">
            <Setter Property="Margin" Value="0"/>
            <Setter Property="Foreground" Value="Red"/>
            <Setter Property="FontWeight" Value="Bold"/>
            <Setter Property="KeepTogether" Value="True"/>
        <Style TargetType="{x:Type Paragraph}" x:Key="Warning">
            <Setter Property="Margin" Value="0"/>
            <Setter Property="Foreground" Value="Red"/>
            <Setter Property="KeepTogether" Value="True"/>
        <Style TargetType="{x:Type Paragraph}" x:Key="Fail">
            <Setter Property="Margin" Value="0"/>
            <Setter Property="Foreground" Value="Fuchsia"/>
            <Setter Property="FontWeight" Value="Bold"/>
            <Setter Property="KeepTogether" Value="True"/>

Understanding the Code

TraceTextSource is the TraceListener derived class that captures Trace output and proxies it to an ITraceTextSink.

interface ITraceTextSink
    void Fail(string msg);
    void Event(string msg, TraceEventType eventType);
class TraceTextSource : TraceListener
    public ITraceTextSink Sink { get; private set; }
    private bool _fail;
    private TraceEventType _eventType = TraceEventType.Information;

    public TraceTextSource(ITraceTextSink sink)
        Debug.Assert(sink != null);
        Sink = sink;

    public override void Fail(string message)
        _fail = true;

    public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id, string message)
        _eventType = eventType;
        base.TraceEvent(eventCache, source, eventType, id, message);

    public override void Write(string message)
        if (IndentLevel > 0)
            message = message.PadLeft(IndentLevel + message.Length, '\t');

        if (_fail)

            Sink.Event(message, _eventType);

        _fail = false;
        _eventType = TraceEventType.Information;

    public override void WriteLine(string message)
        Write(message + "\n");

TraceTextSource also wires to the UI (using a TraceDocument, for instance):

TraceListener listener = new TraceTextSource(new TraceDocument());

And it's up to the implementer of ITraceTextSink to display the Trace output stream appropriately in the UI.

public partial class TraceDocument : FlowDocument, ITraceTextSink
    private delegate void AppendTextDelegate(string msg, string style);

    private TraceListener _listener;

    public TraceDocument()
        AutoAttach = true;

    public bool AutoAttach { get; set; }

    public void Event(string msg, TraceEventType eventType)
        Append(msg, eventType.ToString());

    public void Fail(string msg)
        Append(msg, "Fail");

    private void Append(string msg, string style)
        if (Dispatcher.CheckAccess())
            Debug.Assert(Blocks.LastBlock != null);
            Debug.Assert(Blocks.LastBlock is Paragraph);
            var run = new Run(msg);

            run.Style = (Style)(Resources[style]);
            if (run.Style == null)
                run.Style = (Style)(Resources["Information"]);


            Dispatcher.Invoke(new AppendTextDelegate(Append), msg, style);

    private static void ScrollParent(FrameworkContentElement element)
        if (element != null)
            if (element.Parent is TextBoxBase)

            else if (element.Parent is ScrollViewer)

                ScrollParent(element.Parent as FrameworkContentElement);

    private void Document_Loaded(object sender, RoutedEventArgs e)
        if (AutoAttach && _listener == null)
            _listener = new TraceTextSource(this);

    private void Document_Unloaded(object sender, RoutedEventArgs e)
        if (_listener != null)
            _listener = null;


  • 2/14/2010 - Initial upload.
  • 8/25/2012 - support multithreading


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

Don Kackman
Team Leader Starkey Laboratories
United States United States
The first computer program I ever wrote was in BASIC on a TRS-80 Model I and it looked something like:
10 PRINT "Don is cool"
20 GOTO 10

It only went downhill from there.

Hey look, I've got a blog

Comments and Discussions

BugMessage is not colored Pin
Member 82833785-Jan-18 1:49
MemberMember 82833785-Jan-18 1:49 
QuestionMulti-threaded Applications? Pin
Nigel Stuke12-Jul-12 19:21
professionalNigel Stuke12-Jul-12 19:21 
AnswerRe: Multi-threaded Applications? Pin
Don Kackman15-Jul-12 15:05
MemberDon Kackman15-Jul-12 15:05 
AnswerRe: Multi-threaded Applications? Pin
Don Kackman17-Jul-12 8:02
MemberDon Kackman17-Jul-12 8:02 
AnswerRe: Multi-threaded Applications? Pin
Don Kackman27-Aug-12 6:09
MemberDon Kackman27-Aug-12 6:09 
QuestionVS 2008? Pin
User 5924123-Feb-10 20:38
MemberUser 5924123-Feb-10 20:38 
AnswerRe: VS 2008? Pin
Don Kackman24-Feb-10 2:43
MemberDon Kackman24-Feb-10 2:43 
GeneralGreate Pin
Marcelo Ricardo de Oliveira19-Feb-10 9:31
MemberMarcelo Ricardo de Oliveira19-Feb-10 9:31 
GeneralRe: Greate Pin
Don Kackman19-Feb-10 10:53
MemberDon Kackman19-Feb-10 10:53 
GeneralPretty useful Don Pin
Sacha Barber14-Feb-10 22:40
mvaSacha Barber14-Feb-10 22:40 
GeneralRe: Pretty useful Don Pin
Don Kackman15-Feb-10 4:54
MemberDon Kackman15-Feb-10 4:54 
GeneralThanks Pin
gpgemini14-Feb-10 19:16
Membergpgemini14-Feb-10 19:16 
GeneralRe: Thanks Pin
Don Kackman15-Feb-10 4:55
MemberDon Kackman15-Feb-10 4:55 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.