一种winform实时刷新日志内容的方法

做上位机,经常会有实时刷新日志内容的需求,比如接收串口消息、MQTT接收消息,常用的方法是Invoke函数 + TextBox.AppendText,比如

C# 复制代码
Invoke((new Action(() =>
{
	String msg = "串口消息" + Environment.NewLine;
    txtReceiveMessage.AppendText(string.Format("{0} - {1}",DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss"),msg));
})));

但是这种方式有两个弊端,一是日志长度靠TextBox.MaxLength属性控制,不能准确到行;二来消息只能逐个向后添加,不好调整顺序。

想要更灵活地刷新日志内容,可以使用AsyncOperation类与数据绑定功能。
AsyncOperation :C#中用来跟踪异步操作的生存期的类,其中的Post函数,可以从异步操作中向主进程发送消息。

数据绑定:C#中一种非常强大的功能,它允许开发者将控件属性与数据源连接起来,实现自动更新和同步。

默认情况下,绑定在TextBox、Label上的数据,只有在主进程上改动才能实时反应到界面上,跨线程操作会报错,这时则需要通过AsyncOperation类把"数据改动"这个消息发送到主界面上,才能正常展示,示例代码如下:

  • 先创建一个AsyncProperty类
C# 复制代码
public class AsyncProperty : INotifyPropertyChanged
{
    protected AsyncOperation operation;
    public event PropertyChangedEventHandler PropertyChanged;

    public AsyncProperty()
    {
        operation = AsyncOperationManager.CreateOperation(null);
    }

	// 通过Post函数,将属性更改消息发送到主进程
    public void PostProperty(string propertyName)
    {
        operation.Post(new System.Threading.SendOrPostCallback(_ =>
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }), null);
    }
}
  • 继承AsyncProperty类,创建一个日志类LogContent
C# 复制代码
public class LogContent : AsyncProperty
{
    // 设置日志最大行数
    private int maxLine = 200;
    private int line = 0;
    private string _content = string.Empty;

    public string Content
    {
        get { return _content; }
    }

    public void InsertLog(string content)
	{
	    line++;
	    StringBuilder sb = new StringBuilder();
	    sb.Append(content);
	    sb.Append("\r\n");
	    if (line > maxLine)
	    {
	        sb.Append(_content.Substring(0, indexOfNth(_content, maxLine, '\n', 1)));
	    }
	    else
	    {
	        sb.Append(_content);
	    }
	
	    _content = sb.ToString();
	    PostProperty(nameof(Content));
	}

    public void ClearLog()
    {
        line = 0;
        _content = string.Empty;
        PostProperty(nameof(Content));
    }
}
  • 把LogContent.Content绑定到TextBox.Text上即可
C# 复制代码
public partial class Form1 : Form
{
    LogContent logContent;
    public Form1()
    {
        InitializeComponent();

        logContent = new LogContent();
        textBox1.DataBindings.Add("Text", logContent, nameof(logContent.Content));
    }

    private void btnLog_Click(object sender, EventArgs e)
    {
        Thread thread = new Thread(WriteLog);
        thread.IsBackground = true;
        thread.Start();
    }

    private void WriteLog()
    {
        for (int i = 0; i < 500; i++)
        {
            Thread.Sleep(100);
            logContent.InsertLog(DateTime.Now.ToString("yyyyMMddHHmmssfff"));
        }
    }
}

此时,跨线程调用的WriteLog函数,会将日志倒序实时刷新到界面上。

相关推荐
泛凡(Linyongui)10 小时前
基于 CW32L010 的智能厨卫调温旋钮方案--低成本 EC21 编码旋钮全解析
单片机·嵌入式硬件·c#
暴走约伯11 小时前
【VFX工具链源码拆解 02】告别机械劳动(上):一键统改百个子粒子颜色与批量重命名
3d·unity·c#·材质
信息安全失业大专人员13 小时前
每日了解一个智能体-WorkBuddy
开发语言·ai·c#·智能体
呆呆敲代码的小Y13 小时前
【实战精选面试题】C# 面试题详细解答,按难易程度排序
android·java·游戏·c#·游戏开发·unity面试题·c#面试题
淡海水14 小时前
09-01-内存-Unity资源生命周期全景
unity·c#·游戏引擎·.netcore·gc
椒颜皮皮虾྅17 小时前
让 C# 读懂整张图:DeploySharp 驱动 PaddleOCR v4/v5/v6,打通 OCR 全流程
开发语言·人工智能·深度学习·学习·c#·ocr·cuda
昂好的day17 小时前
C# 同步异步与多线程:Thread / Task / 线程池一次讲透(上篇)
开发语言·windows·c#
吃一碗粉只付一碗钱1 天前
12-给SPA友好的401和403响应
c#·状态模式
caimouse1 天前
MonoDevelop 编译用户应用程序时调用的是哪个编译器
c#
will_net1 天前
apollo 站点部署&C#接入
开发语言·c#·apollo