How to work with richtextbox asynchronously? For example, I have a class and an event that logs some kind of calculation
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;
}
}
}
Tried options with backgroundworker, task , synchronization... and got no result. I'm surprised that WinForms have been around for many years and Microsoft hasn't added asynchronous actions to them, for example AppendTextAsync()
. How can I force text to be added asynchronously to a richtextbox?
CodePudding user response:
Put another textbox on your form
Paste this code over ther top of everything in your 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;
}
}
}
Run it, click the button, then carry on typing in the other textbox - it stays responsive etc.. I don't know how long you'll have to wait to see major slowdowns as the text in the RTB gets huge.. but really you should look at periodically clipping your log so you arent building up megabytes of text in the RTB
Async in winforms is not about getting the controls to update using some async fashion; control must only be accessed by the thread that created them so any interaction with a control we generally ask that thread to do it (but I suppose it's a matter of perspective: if a background thread is asking the UI thread to update a control then it's being done asyncronous to whatever work the background thread is doing) - make sure the access you do with them is quick, do your heavy lifting (API calls?) using async way and then Invoke to get your UI thread to update the controls.