简体   繁体   中英

C#: Thread safe richtextbox event logging method?

I have a method that is meant to display output on a RichTextBox in a form.

    public void LogTextEvent(RichTextBox TextEventLog, Color TextColor, string EventText)
    {
        string nDateTime = DateTime.Now.ToString("hh:mm:ss tt") + " - ";

        // color text.
        TextEventLog.SelectionStart = TextEventLog.Text.Length;
        TextEventLog.SelectionColor = TextColor;

        // newline if first line, append if else.
        if (TextEventLog.Lines.Length == 0)
        {
            TextEventLog.AppendText(nDateTime + EventText);
            TextEventLog.ScrollToCaret();
            TextEventLog.AppendText(System.Environment.NewLine);
        }
        else
        {
            TextEventLog.AppendText(nDateTime + EventText + System.Environment.NewLine);
            TextEventLog.ScrollToCaret();
        }
    }

The problem arises when I call LogEventText() from another method running inside a thread:

            Thread thListening = new Thread(new ThreadStart(StartListening));
            thListening.Start();

Inside the StartListening method (it is a thread created that handles new tcp sockets to be created for clients on a main listening socket for a small http server), I make use of LogTextEvent to log some data, but I receive an InvalidOperationException was unhandled error in the LogTextEvent method at the 2nd line, TextEventLog.SelectionStart = TextEventLog.Text.Length;

The error reads, Cross-thread operation not valid: Control 'rchEventLog' accessed from a thread other than the thread it was created on.

Can anyone help break down what's going on, why, and how the error could be fixed?

Try something like this:

public void LogTextEvent(RichTextBox TextEventLog, Color TextColor, string EventText)
{
    if (TextEventLog.InvokeRequired)
    {
        TextEventLog.BeginInvoke(new Action(delegate {
            LogTextEvent(TextEventLog, TextColor, EventText);
        }));
        return;
    }

    string nDateTime = DateTime.Now.ToString("hh:mm:ss tt") + " - ";

    // color text.
    TextEventLog.SelectionStart = TextEventLog.Text.Length;
    TextEventLog.SelectionColor = TextColor;

    // newline if first line, append if else.
    if (TextEventLog.Lines.Length == 0)
    {
        TextEventLog.AppendText(nDateTime + EventText);
        TextEventLog.ScrollToCaret();
        TextEventLog.AppendText(System.Environment.NewLine);
    }
    else
    {
        TextEventLog.AppendText(nDateTime + EventText + System.Environment.NewLine);
        TextEventLog.ScrollToCaret();
    }
}

If the LogTextEvent method is called from a thread other than the rich text box's UI thread, it will post a message to the UI thread which will cause the LogTextEvent method to be called in that thread.

By using BeginInvoke instead of Invoke, it is posted asynchronously which means that it returns immediately to the caller instead of waiting for the method to be executed on the other thread. For a logging scenario like this, this is probably what you want. If you needed to get a value from a control or something you would probably need to use Invoke instead.

new Thread(new ThreadStart(delegate {
    //do some worker-thread processing here
    ...
    //now update the gui from within the gui thread
    MyRichTextBox.Invoke(new Action(delegate {
        MyRichTextBox.SelectionStart = CalculateTheSelectionStart();
    }));
})).Start();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM