Don’t know how many decades ago I got the idea, and there are probably dozens of unfinished samples like these, but below is complete one.
bo.Forms.TextBoxTraceListener.
But first the feature list:
- Allows Trace statements to be catched in a TextBox (you probably guessed this from the name TextBoxTraceListener)
- Works in both the UI and non-UI threads (so it does not fail when calling from a non UI-thread)
- Supports Write and WriteLine (no need to override the others as they all come down to this)
- Does not do indentation (hey, it’s quick and dirty!)
Note that TextBox.Text is not meant for large amount of data, or frequently appending to the data, but again: it’s meant as quick and dirty, not as your tracing ‘silver bullet’.
First how you use it:
public partial class MyClass { public MyMethod() { System.Diagnostics.Trace.WriteLine("trace value", "trace category"); } // ... }
Then how you instantiate it:
public partial class MyForm : Form { private TextboxTraceListener textboxTraceListener; public MyForm() { InitializeComponent(); textboxTraceListener = new TextboxTraceListener(traceTextBox); } // ... }
The trick to get it working in both the UI and non-UI threads is to route all calls to the textBox.Invoke (actually System.Windows.Forms.Invoke) method: that one sorts out if it needs to switch to the UI thread or not.
This works best when you have a central method, in this case WriteInUiThread that does all the logic for you.
Invoke either expects a plain delegate, or a delegate with parameters. We need the latter, which usually results in a some fiddling with temporary storage, and/or passing the parameters as object arrays that the compiler will not verify for you.
So I opted for another route: an dispatching method (RunOnUiThread), with anonymous method calling it, together with a parameterless Delegate type (the protected delegate void Delegate();). You need that Delegate, because you cannot use the plain delegate keyword here (as the plain delegate does not know its’ signature yet).
Now the compiler will do all the fiddling for you without any need to worry about the parameters: behind the scene it magically creates a hidden class for you, instantiates an instances, calls the right method, etc.
So finally here is the source code:
using System; using System.Diagnostics; using System.Threading; using System.Windows.Forms; namespace bo.Windows.Forms { public class TextboxTraceListener : TraceListener { private TextBox textBox; public TextboxTraceListener(TextBox textBox) { this.textBox = textBox; TraceListenerCollection traceListeners = Trace.Listeners; if (!traceListeners.Contains(this)) traceListeners.Add(this); } protected delegate void Delegate(); protected virtual void RunOnUiThread(Delegate method) { textBox.Invoke(method); } public override void Write(string message) { RunOnUiThread(delegate { WriteInUiThread(message); } ); } private void WriteInUiThread(string message) { // accessing textBox.Text is not fast but works for small trace logs textBox.Text = textBox.Text + message; } public override void WriteLine(string message) { Write(message + Environment.NewLine); } } }
Posted in Debugging, Development
