C#:线程安全的richtextbox事件记录方法?

时间:2022-11-16 11:45:57

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

我有一个方法,用于在表单中的RichTextBox上显示输出。

    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:

当我从线程内运行的另一个方法调用LogEventText()时出现问题:

            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;

在StartListening方法内部(它是一个创建的线程,用于处理为小型http服务器的主侦听套接字上的客户端创建的新tcp套接字),我使用LogTextEvent来记录一些数据,但是我收到InvalidOperationException是未处理的错误在第二行的LogTextEvent方法中,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.

错误读取,跨线程操作无效:控制'rchEventLog'从其创建的线程以外的线程访问。

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

任何人都可以帮助分解正在发生的事情,为什么以及如何修复错误?

2 个解决方案

#1


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.

如果从富文本框的UI线程以外的线程调用LogTextEvent方法,它将向UI线程发布消息,这将导致在该线程中调用LogTextEvent方法。

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.

通过使用BeginInvoke而不是Invoke,它是异步发布的,这意味着它会立即返回给调用者,而不是等待在另一个线程上执行该方法。对于像这样的日志记录方案,这可能是您想要的。如果你需要从控件或某些东西获取一个值,你可能需要使用Invoke。

#2


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();

#1


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.

如果从富文本框的UI线程以外的线程调用LogTextEvent方法,它将向UI线程发布消息,这将导致在该线程中调用LogTextEvent方法。

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.

通过使用BeginInvoke而不是Invoke,它是异步发布的,这意味着它会立即返回给调用者,而不是等待在另一个线程上执行该方法。对于像这样的日志记录方案,这可能是您想要的。如果你需要从控件或某些东西获取一个值,你可能需要使用Invoke。

#2


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();