Richtexbox 如何在 Winforms 上附加来自事件侦听器的异步文本?
Posted
技术标签:
【中文标题】Richtexbox 如何在 Winforms 上附加来自事件侦听器的异步文本?【英文标题】:How richtexbox can append text async from event listener on winforms? 【发布时间】:2021-12-30 00:03:20 【问题描述】:如何异步处理richtextbox?例如,我有一个类和一个记录某种计算的事件
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ExampleProject
public partial class Form1 : Form
public Form1()
InitializeComponent();
private void button1_Click(object sender, EventArgs e)
//I subscribe to an event that often sends messages
//and I want to display
var c = new Core();
c.Notify += DisplayMessage;
c.ExampleMethod();
private void DisplayMessage(object sender, LogEventArgs e)
//When a event arrives, transfer to richTextBox1
richTextBox1.AppendText("\r\n" + e.Date.ToString("dd/MM/yyyy HH:mm:ss "), Color.SlateGray);
richTextBox1.AppendText(e.Message, Color.Blue);
richTextBox1.SelectionStart = richTextBox1.Text.Length;
richTextBox1.ScrollToCaret();
class Core
public delegate void LogHandler(object sender, LogEventArgs e);
public event LogHandler Notify;
//
public void ExampleMethod()
//generate messages with a pause in a random value
var rand = new Random();
for (int i = 1; i < 10000; i++)
var pause = rand.Next(50, 2000);
Thread.Sleep(pause);
Notify?.Invoke(this, new LogEventArgs($"logged i pause in miliseconds pause", DateTime.Now, MessageType.Notice));
//The class in which log messages are placed
class LogEventArgs
public string Message get;
public DateTime Date get;
public MessageType MessageType get;
public LogEventArgs(string mes, DateTime date, MessageType messageType)
Message = mes;
Date = date;
MessageType = messageType;
enum MessageType
Notice,
Warning,
Error,
//Extension method to set line colors in RichTextBox
public static class RichTextBoxExtensions
public static void AppendText(this RichTextBox box, string text, Color color)
box.SelectionStart = box.TextLength;
box.SelectionLength = 0;
box.SelectionColor = color;
box.AppendText(text);
box.SelectionColor = box.ForeColor;
尝试了 backgroundworker、task、synchronization 的选项......但没有得到任何结果。我很惊讶 WinForms 已经存在很多年了,而微软还没有向它们添加异步操作,例如AppendTextAsync()
。如何强制将文本异步添加到富文本框?
【问题讨论】:
有什么好处? @CaiusJard 我正在处理数据,它们并不大,有 10-20k 行,如果我在 Richtextbox 中显示日志,那么我的应用程序只会挂起 5-10 分钟 你是说在富文本框中添加 20000 行需要 10 分钟才能完成? 能否放个完整的代码复现?一个完整的小项目/完整的 Form1.cs 我们可以粘贴到一个新项目上,例如在表单中添加一个丰富的/文本框? 在竞争和死锁之后,第三个最常见的线程错误是 firehose 错误。当生产者线程生成数据的速度快于消费者线程处理数据的速度时,就会发生这种情况。当消费者是 Winforms 应用程序的 UI 线程时非常明显,它停止处理其正常职责(如绘制和响应用户输入),拼命地试图跟上 Begin/Invoke 请求并且无法赶上.需要通过减少调用频率来解决,每秒 25 次足够快,足以让人眼保持忙碌。 【参考方案1】:在表单上添加另一个文本框
将此代码粘贴到 Form1.cs 中所有内容的顶部:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ExampleProject
public partial class Form1 : Form
public Form1()
InitializeComponent();
private async void button1_Click(object sender, EventArgs e)
var c = new Core();
c.Notify += DisplayMessage;
await c.ExampleMethod();
private void DisplayMessage(object sender, LogEventArgs e)
richTextBox1.AppendText("\r\n" + e.Date.ToString("dd/MM/yyyy HH:mm:ss "), Color.SlateGray);
richTextBox1.AppendText(e.Message, Color.Blue);
richTextBox1.SelectionStart = richTextBox1.Text.Length;
richTextBox1.ScrollToCaret();
class Core
public delegate void LogHandler(object sender, LogEventArgs e);
public event LogHandler Notify;
public async Task ExampleMethod()
var rand = new Random();
for (int i = 1; i < 10000; i++)
var pause = rand.Next(50, 200);
await Task.Delay(pause);
Notify?.Invoke(this, new LogEventArgs($"logged i pause in miliseconds pause", DateTime.Now, MessageType.Notice));
class LogEventArgs
public string Message get;
public DateTime Date get;
public MessageType MessageType get;
public LogEventArgs(string mes, DateTime date, MessageType messageType)
Message = mes;
Date = date;
MessageType = messageType;
enum MessageType
Notice,
Warning,
Error,
public static class RichTextBoxExtensions
public static void AppendText(this RichTextBox box, string text, Color color)
box.SelectionStart = box.TextLength;
box.SelectionLength = 0;
box.SelectionColor = color;
box.AppendText(text);
box.SelectionColor = box.ForeColor;
运行它,单击按钮,然后继续在另一个文本框中输入 - 它保持响应等。我不知道您需要等待多长时间才能看到主要的减速,因为 RTB 中的文本变得巨大.. 但实际上您应该定期查看您的日志,这样您就不会在 RTB 中建立数兆字节的文本
winforms 中的异步并不是要使用某种异步方式更新控件;控件只能由创建它们的线程访问,因此与控件的任何交互我们通常要求该线程执行它(但我认为这是一个透视问题:如果后台线程正在要求 UI 线程更新控件,那么它是与后台线程正在执行的任何工作异步完成)-确保您对它们进行的访问是快速的,使用异步方式进行繁重的工作(API 调用?),然后调用以让您的 UI 线程更新控件。
【讨论】:
非常感谢您的帮助。在实际代码中,我的Task
中包含了其他几个同步方法。我认为将顶部方法包装在 Task
中就足够了,一切都会正常工作,但是 This async method lacks 'await' operators and will run synchronously
这取决于它们是什么,但该消息并非来自使用任务;它来自使用 async
修饰符标记方法,然后在方法主体中不执行任何 await
操作以上是关于Richtexbox 如何在 Winforms 上附加来自事件侦听器的异步文本?的主要内容,如果未能解决你的问题,请参考以下文章
以流方式读写文件:文件菜单打开一个文件,文件内容显示在RichTexBox中,执行复制剪切粘贴后,通过文件菜单可以保存修改后的文件。
如何将 BlockUIContainer 中 RichTextBox 的内容保存到 RTF 文件?