Trace listener to write to a text box (WPF applica

2019-01-08 16:23发布

问题:

For my WPF application I do logging to a text file using a TextWriterTraceListener. How can I also display the Trace output to a textbox?

回答1:

I use this for C# winforms, should be easily adjustable to wpf

public class MyTraceListener : TraceListener
{
    private TextBoxBase output;

    public MyTraceListener(TextBoxBase output) {
        this.Name = "Trace";
        this.output = output;
    }


    public override void Write(string message) {

        Action append = delegate() {
            output.AppendText(string.Format("[{0}] ", DateTime.Now.ToString()));
            output.AppendText(message); 
        };
        if (output.InvokeRequired) {
            output.BeginInvoke(append);
        } else {
            append();
        }

    }

    public override void WriteLine(string message) {
        Write(message + Environment.NewLine);
    }
}

Use it like

TraceListener debugListener = new MyTraceListener (theTextBox);
Debug.Listeners.Add(debugListener);
Trace.Listeners.Add(debugListener);

Remember to Trace/Debug.Listeners.Remove(debugListener); when you don't need it anymore.



回答2:

How about implementing a custom TraceListener that simply appends trace messages to a string? You then expose that string as a property, implement INotifyPropertyChanged and databind a TextBox control to that property.

Something like this:

public class MyTraceListener : TraceListener, INotifyPropertyChanged
{
    private readonly StringBuilder builder;

    public MyTraceListener()
    {
        this.builder = new StringBuilder();
    }

    public string Trace
    {
        get { return this.builder.ToString(); }
    }

    public override void Write(string message)
    {
        this.builder.Append(message);
        this.OnPropertyChanged(new PropertyChangedEventArgs("Trace"));
    }

    public override void WriteLine(string message)
    {
        this.builder.AppendLine(message);
        this.OnPropertyChanged(new PropertyChangedEventArgs("Trace"));
    }

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    #endregion

    protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        PropertyChangedEventHandler handler = this.PropertyChanged;
        if (handler != null)
        {
            handler(this, e);
        }
    }
}

You would need to add this TraceListener to the list of active listeners:

Trace.Listeners.Add(new MyTraceListener());


回答3:

Below code is C#6.0 style of @Mark Seemann's code.

public class MyTraceListener : TraceListener, INotifyPropertyChanged
{
    private readonly StringBuilder _builder;

    public MyTraceListener()
    {
        _builder = new StringBuilder();
    }

    public string Trace => _builder.ToString();

    public override void Write(string message)
    {
        _builder.Append(message);
        OnPropertyChanged(new PropertyChangedEventArgs("Trace"));
    }

    public override void WriteLine(string message)
    {
        _builder.AppendLine(message);
        OnPropertyChanged(new PropertyChangedEventArgs("Trace"));
    }

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    #endregion

    protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        PropertyChanged?.Invoke(this, e);
    }
}

Assume that MainViewModel is root DataContext of MainWindow.xaml file. To apply MyTraceListener in MVVM manner, write below code in MainViewModel.cs.

private string _traceOutput;
private readonly MyTraceListener _trace = new MyTraceListener();

// Constructor
public MainViewModel() {

    // ...your viewmodel initialization code.

    // Add event handler in order to expose logs to MainViewModel.TraceOutput property.
    WeakEventManager<INotifyPropertyChanged, PropertyChangedEventArgs>.AddHandler(_trace, "PropertyChanged", traceOnPropertyChanged);
    Trace.Listeners.Add(_trace);
}

private void traceOnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
    if (e.PropertyName == "Trace")
        TraceOutput = _trace.Trace;
}

public string TraceOutput
{
    get { return _traceOutput; }
    set {
        _traceOutput = value;
        RaisePropertyChanged(); // This method is from Mvvm-light.
    }
}

In MainWindow.xaml, bind TraceOutput property to TextBox. If you want the TextBox to scroll to bottom along with accumulated logs, apply TextChanged event.

<TextBox x:Name="TextBoxLog" TextWrapping="Wrap" Text="{Binding TraceOutput}" VerticalScrollBarVisibility="Auto" AcceptsReturn="True" TextChanged="TextBoxLog_OnTextChanged" />

in code-behind of XAML file (MainWindow.xaml.cs), event handler is simply as below.

    private void TextBoxLog_OnTextChanged(object sender, TextChangedEventArgs e)
    {
        TextBoxLog.ScrollToEnd();
    }


回答4:

you could append a custom Listener which updates the Textbox.Text property. You therefore need to inherit from the abstract base class TraceListener and override one of the TraaceData, TraceEvent, TraceTransfer methods.