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

时间:2015-12-07 15:23:05

标签: c# multithreading winforms

前段时间我在代码项目(Link)上找到了以下代码,它帮助我从另一个帖子向我的Rich Text Box添加了一些文本:

<%= f.input :cases, collection: 0..500, label: "Number of new cases of ILD per month (approx)", class: 'form-control' %>

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

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;
        }
    }

然后我发现代码可以让我向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(); } 方法不会检查或处理&#39; InvokeRequired&#39; RTB的方面。

2 个答案:

答案 0 :(得分:1)

您首先需要了解的是,任何用户界面(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

答案 1 :(得分:1)

如果我理解正确,您只需要AppendText“线程感知”,如UpdateText。并且模式始终是一样的 - 检查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 ...
}