从单独的线程将彩色文本附加到富文本框

本文关键字:文本 彩色 单独 线程 | 更新日期: 2023-09-27 18:26:03

不久前,我在代码项目(链接)上发现了以下代码,它可以帮助我从另一个线程向富文本框添加一些文本:

delegate void UpdateMessageLog(Control ctrl, string text);
public static void UpdateText(Control ctrl, string text)
{
    if (ctrl.InvokeRequired)
    {
        UpdateMessageLog delUpdate = new UpdateMessageLog(UpdateText);
        ctrl.Invoke(delUpdate, ctrl, text);
    }
    else
    {
            ctrl.Text += text;
        }
    }

这是必要的,因为我正在WinForms中使用c#开发一个简单的基于UDP的聊天应用程序,并且我的侦听服务器方法正在另一个线程上运行,因此如果收到消息,它的文本将使用上面的代码更新到RTB。这是我的服务器方法:

private void serverThread()
{
    UdpClient udpReceiveClient = new UdpClient(8082);
    while (true)
    {
        IPEndPoint ConnectingIPEndPoint = new IPEndPoint(IPAddress.Any, 0);
        byte[] receivedBytes = udpReceiveClient.Receive(ref ConnectingIPEndPoint);
        string sMessage = Encoding.ASCII.GetString(receivedBytes);
        UpdateText(rtbTextWindow, Environment.NewLine + "Sound Desk: " + sMessage);
    }
}

后来,我发现了一些代码,可以将彩色文本添加到RTB中,并帮助我用不同的颜色为不同的文本着色:

private void AppendText(RichTextBox box, Color color, string text)
{
    int start = box.TextLength;
    box.AppendText(text);
    int end = box.TextLength;
    // Textbox may transform chars, so (end-start) != text.Length
    box.Select(start, end - start);
    {
        box.SelectionColor = color;
        // could set box.SelectionBackColor, box.SelectionFont too.
    }
    box.SelectionLength = 0; // clear
}

下面是我使用它的一个例子——当当前客户端发送消息时,它自己的消息也会添加到RTB:

private void btnSend_Click(object sender, EventArgs e)
{
    AppendText(this.rtbTextWindow, Color.GreenYellow, Environment.NewLine + "Front Stage: ");
    AppendText(this.rtbTextWindow, Color.Black, txtMsg.Text);
    SendOverUDP(txtMsg.Text);
    //Clear Text
    txtMsg.Clear();
    txtMsg.Focus();
}

现在,如果我使用它将彩色文本附加到当前客户端正在发送的RTB,但从不同节点接收到的文本(从服务器线程接收到)需要不同的解决方案,这将非常有效,因为AppendText方法不检查或处理RTB的"InvokeRequired"方面。

从单独的线程将彩色文本附加到富文本框

您需要了解的第一件事是,任何用户界面(UI)对象都只能在UI线程上更新。其中包括您对txtMsg的引用。

你可能在一个单独的后台线程上与聊天的另一端进行通信。因此,为了更新您的UI,您必须跳转或调用到UI线程中进行更新。

您的"UpdateText()"方法为您提供了如何为AppendText执行此操作的所有线索。首先,您甚至不能从后台线程引用UI控件。

此外,作为附带说明,还有一个称为Action的通用委托,它消除了定义委托的需要,例如UpdateMessageLog委托。

因此,无论何时您希望更新任何控件,都要遵循一个简单的模式,如下所示:

void UpdateControl(object dataToApply)
{
    if (myControl.InvokeRequired)
    {
        myControl.Invoke(new Action<object>(UpdateControl), dataToApply);
    }
    else
    {
         //Code goes here to apply the update.  This will run on the UI thread, 
         //such as your call to update your RichTextBox:
         AppendText(this.rtbTextWindow, Color.Black, dataToApply);
    }
}

有关如何传递不同类型和数量的参数的详细信息,可以查阅Action<>泛型委托。如果需要从UI控件返回值,则可以使用Func<>。请注意,我上面使用的类型(object)只是向您展示一个示例——在您的情况下,您可能希望将其设置为string

如果我理解正确的话,您只想让AppendTextUpdateText一样具有"线程意识"。模式总是一样的——检查InvokeRequired,如果是,则创建并调用该方法的委托,否则只执行正常的方法代码。像这个

private void AppendText(RichTextBox box, Color color, string text)
{
    if (box.InvokeRequired)
    {
        box.Invoke(new Action<RichTextBox, Color, string>(AppendText), box, color, text);
        return;
    }
    // The existing code ...
}