You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

串口秤量应用显示闪烁问题求助(附代码及效果示例)

解决串口秤量应用TextBox显示闪烁问题

我开发了一款从串口读取数据并在文本框显示的秤量应用,但串口传入的数值显示出现闪烁问题。以下是程序代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO.Ports;
using System.Text.RegularExpressions;

namespace WindowsFormsApp22
{
    public partial class Form1 : Form
    {
        SerialPort port;

        public Form1()
        {
            InitializeComponent();
            // Initialize the serial port.
            port = new SerialPort("COM13", 9600, Parity.None, 8, StopBits.One);
            port.Open();

            // Add an event handler for the DataReceived event.
            port.DataReceived += Port_DataReceived;
        }

        private void Port_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            // Get the data that was received.
            string data = port.ReadExisting();
            string[] parts = data.Split(' ');
            string weight = parts.Last();

            // Remove non-numeric characters from the weight.
            weight = Regex.Replace(weight, @"[^0-9]", "");

            // Trim the leading zeros from the weight.
            weight = weight.TrimStart('0');

            // Update the text box on the main thread.
            BeginInvoke((Action)(() => {
                textBox1.Text = weight;
                textBox1.SelectionStart = textBox1.Text.Length;
            }));
        }
    }
}

当前问题:文本框显示的数值频繁闪烁,期望实现稳定显示的效果。


问题根源

闪烁主要来自两个原因:

  1. 串口DataReceived事件可能分段返回数据,导致多次触发更新操作,即使数值没有变化也会重复设置文本框内容,引发不必要的重绘
  2. WinForms的TextBox默认重绘机制在频繁更新时会出现闪烁

解决方案

以下是修改后的代码,从数据读取、UI更新优化、控件重绘三个层面解决问题:

using System;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports;
using System.Text.RegularExpressions;

namespace WindowsFormsApp22
{
    public partial class Form1 : Form
    {
        SerialPort port;
        // 缓存串口数据,确保获取完整的称重帧
        private readonly StringBuilder _serialBuffer = new StringBuilder();
        // 记录当前显示的数值,避免重复更新UI
        private string _currentDisplayValue = "";

        public Form1()
        {
            InitializeComponent();
            // 启用TextBox双缓冲,减少重绘闪烁
            EnableDoubleBuffering(textBox1);
            
            // 初始化串口(注意根据设备实际参数调整)
            port = new SerialPort("COM13", 9600, Parity.None, 8, StopBits.One);
            // 设置帧结束符,需匹配秤的输出格式(常见为\r\n)
            port.NewLine = "\r\n";
            port.Open();

            port.DataReceived += Port_DataReceived;
        }

        /// <summary>
        /// 启用控件双缓冲的辅助方法
        /// </summary>
        private void EnableDoubleBuffering(Control control)
        {
            if (SystemInformation.TerminalServerSession)
                return;

            typeof(Control).InvokeMember(
                "DoubleBuffered",
                System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.SetProperty,
                null,
                control,
                new object[] { true });
        }

        private void Port_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            // 读取本次收到的所有数据并加入缓存
            string receivedData = port.ReadExisting();
            _serialBuffer.Append(receivedData);

            // 循环处理所有完整的帧
            while (_serialBuffer.ToString().Contains(port.NewLine))
            {
                // 提取第一帧完整数据
                int frameEndIndex = _serialBuffer.ToString().IndexOf(port.NewLine);
                string fullFrame = _serialBuffer.ToString(0, frameEndIndex);
                // 从缓存中移除已处理的帧
                _serialBuffer.Remove(0, frameEndIndex + port.NewLine.Length);

                // 解析重量数值
                string[] parts = fullFrame.Split(' ');
                string weight = parts.Last();
                // 过滤非数字字符
                weight = Regex.Replace(weight, @"[^0-9]", "");
                // 去除前导零,空值则显示0
                weight = string.IsNullOrEmpty(weight.TrimStart('0')) ? "0" : weight.TrimStart('0');

                // 仅当数值变化时更新UI,减少重绘次数
                if (weight != _currentDisplayValue)
                {
                    _currentDisplayValue = weight;
                    BeginInvoke((Action)(() => {
                        textBox1.Text = _currentDisplayValue;
                        textBox1.SelectionStart = textBox1.Text.Length;
                    }));
                }
            }
        }
    }
}

修改说明

  1. 串口数据缓冲:通过StringBuilder缓存串口数据,直到收到完整的一帧(以换行符为结束标志,需根据秤的实际输出调整),避免分段数据导致的频繁更新
  2. 数值变化检测:用_currentDisplayValue记录当前显示的数值,只有当新数值与当前值不同时才更新UI,彻底避免无意义的重绘
  3. 双缓冲优化:通过反射启用TextBox的双缓冲特性,减少控件重绘时的闪烁现象
  4. 空值处理:当解析后重量为空时,自动显示"0",避免文本框内容为空的异常情况

内容的提问来源于stack exchange,提问作者Siamak

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 14:11:58