C# 学习笔记17:上位机助手_页面生成多控件滚动效果_保存与加载控件文本到文件_多字符串发送界面

news2024/11/24 16:49:29

今日继续完善更新我的上位机助手,这次完善多字符串发送的部分:

目前上位机助手支持以下功能:

1、  普通的16进制\ASCLL显示收发

2、  全页更新HEX显示(会自动断串口)

3、  日志辅助显示报错

4、  必要的清除日志区、接收区的逻辑

5、  后台线程刷新查找串口,自动选择CH340串口

6、  串口数据绘图,单位1ms,精度0.0001ms

7、  多字符串发送(单击编辑,双击发送),发送计数成功次数

8、  多字符串文本可以通过写入\读取文件进行加载

文章提供完整代码解释、设计点解释、测试效果图、测试工程下载

目录

开发学习目标:

动态添加TextBox到Panel:

打开Panel控件的AutoScroll属性:

编写代码:

效果如下:

TextBox的双击事件生成:

效果如下:

保存所有TextBox到文件:

加载所有TextBox到进程:

完整测试效果图如下:

1、最初打开状态:

2、初始化加载多字符串:

3、加载之前保存的条目状态:

4、修改后保存目前条目状态:

 5、先全删,再加载上次条目状态:

测试工程下载:

 

遇到的问题记录:

解决后的源码如下:


开发学习目标:

完善之前写的上位机助手,可以在页面中生成众多TextBox控件,双击即可发送数据

动态添加TextBox到Panel:

打开Panel控件的AutoScroll属性:

AutoScroll属性被设置为true,允许Panel在内容超出其可视范围时显示滚动条。

这里我设置了一个加载按键,在它按下后开始加载界面的Textbox组件:

编写代码:

在点击事件中添加如下代码:

​

        //开始加载多字符串发送助手
        private void Load_multi_textbox_Click(object sender, EventArgs e)
        {
            int yPosition = 0; // 初始Y位置
            int textBoxHeight = 25; // 假设每个TextBox的高度为20
            for (int i = 0; i < 36; i++) // 假设我们要添加10个TextBox
            {
                System.Windows.Forms.TextBox textBox = new System.Windows.Forms.TextBox();
                textBox.Text = $"TextBox {i + 1}"; // 为TextBox设置文本  
                textBox.Location = new Point(10, yPosition); // 设置TextBox的位置  
                textBox.Width = 1144; // 设置TextBox的宽度  
                textBox.Height = textBoxHeight; // 设置TextBox的高度  
                panel1.Controls.Add(textBox); // 将TextBox添加到Panel中 
                yPosition += textBoxHeight + 5; // 更新Y位置,为下一个TextBox留出空间  
            }
            // 如需要可调整Panel的大小确保所有TextBox都可见  
            // 由于设置了AutoScroll,这一步通常是可选的  
            // 例如:panel1.Height = yPosition;  
        }
​

效果如下:

点击按钮后:

TextBox的双击事件生成:

只需添加在循环中为每个 TextBox 控件添加一个事件处理程序。由于事件处理程序需要引用到具体的 TextBox 控件,可以使用匿名方法或 lambda 表达式来捕获当前的 TextBox 实例

这个代码我并未将双击事件的调用写成串口发送,而是打印哪个编号的TextBox被双击来测试代码是否正常运行:

        //开始加载多字符串发送助手
        private void Load_multi_textbox_Click(object sender, EventArgs e)
        {
            int yPosition = 0; // 初始Y位置
            int textBoxHeight = 25; // 假设每个TextBox的高度为20
            for (int i = 0; i < 36; i++) // 假设我们要添加10个TextBox
            {
                System.Windows.Forms.TextBox textBox = new System.Windows.Forms.TextBox();
                textBox.Text = $"TextBox {i + 1}"; // 为TextBox设置文本  
                textBox.Location = new Point(10, yPosition); // 设置TextBox的位置  
                textBox.Width = 1144; // 设置TextBox的宽度  
                textBox.Height = textBoxHeight; // 设置TextBox的高度  

                // 添加双击事件处理程序  
                textBox.DoubleClick += (senderTextBox, eArgs) =>
                {
                    // 但由于lambda表达式的作用域,它实际上捕获了当前迭代的textBox实例  
                    MessageBox.Show($"Double-clicked: {textBox.Text}"); // 显示被双击的TextBox的文本  


                };


                panel1.Controls.Add(textBox); // 将TextBox添加到Panel中 
                yPosition += textBoxHeight + 5; // 更新Y位置,为下一个TextBox留出空间  
            }
            // 如需要可调整Panel的大小确保所有TextBox都可见
            // 由于设置了AutoScroll,这一步通常是可选的  
            // 例如:panel1.Height = yPosition; 

        }

效果如下:

双击不同的Textbox,打印它们各自的编号:

保存所有TextBox到文件:

由于我是在循环中动态创建 TextBox 控件,并且每个 TextBox 都没有被单独存储为一个变量(除了循环中的临时变量 textBox),因此直接通过变量名来访问每个 TextBox 的内容是不可行的

我选择遍历panel控件中的所有textbox来进行保存:

这里如果发现没有这个文本文件,那就会自己创建一个进行保存

        //保存条目输入状态
        private void Save_multi_textbox_Click(object sender, EventArgs e)
        {
            // 定义文件路径  
            string filePath = "Multi_String.txt";
            // 可选:显式地清空文件内容(实际上不需要,因为接下来会用StreamWriter覆盖)  
            // File.WriteAllText(filePath, string.Empty);  
            // 使用StringWriter来构建要写入文件的字符串(可选,但更灵活)  
            StringWriter stringWriter = new StringWriter();
            foreach (Control control in panel1.Controls)
            {
                // 检查控件是否是TextBox  
                if (control is System.Windows.Forms.TextBox textBox)
                {
                    // 将TextBox的内容写入StringWriter  
                    // stringWriter.WriteLine(textBox.Text);
                    // 或者,如果您想要一些前缀或格式  
                    stringWriter.WriteLine($"TextBox Content:{textBox.Text}");
                }
            }
            // 现在,将StringWriter的内容一次性写入文件  
            // 注意:这实际上会覆盖文件,所以不需要先清空文件  
            File.WriteAllText(filePath, stringWriter.ToString());
            // 可选:显示消息框通知用户  
            MessageBox.Show("所有TextBox内容已成功保存到Multi_String.txt文件中!");
            // 释放StringWriter资源(虽然在这个简单的例子中,它会在GC时被自动处理)
            stringWriter.Dispose();
        }

加载所有TextBox到进程:

依旧是遍历所有Panel进行遍历并用文本中内容进行覆盖,这里我之前的保存文件代码中添加了标识头,需要去除:

                // 去除前缀(如果存在)  

                if (line.StartsWith("TextBox Content:"))  

                {  

                    line = line.Substring("TextBox Content:".Length).Trim();  

                }  

完整代码如下:

        //加载条目状态:
        private void Load_Muiti_string_Click(object sender, EventArgs e)
        {
            // 定义文件路径  
            string filePath = "Multi_String.txt";
            // 尝试读取文件  
            if (File.Exists(filePath))
            {
                int textBoxIndex = 0; // 用于跟踪当前应该填充哪个TextBox的索引  
                using (StreamReader reader = new StreamReader(filePath))
               {
                    string line;
                    while ((line = reader.ReadLine()) != null)
                    {
                        // 去除前缀(如果存在)  
                        if (line.StartsWith("TextBox Content:"))
                        {
                            line = line.Substring("TextBox Content:".Length).Trim();
                        }
                        // 尝试将读取的文本设置回TextBox(如果索引在范围内)  
                        if (textBoxIndex < panel1.Controls.Count && panel1.Controls[textBoxIndex] is System.Windows.Forms.TextBox textBox)
                        {
                            textBox.Text = line;
                        }
                        // 移动到下一个TextBox  
                        textBoxIndex++;
                        // 如果TextBox的数量少于文件中的行数,则停止读取(可选)  
                       // if (textBoxIndex >= panel1.Controls.OfType<TextBox>().Count()) break;  
                    }
                }
            }
            else
            {
                MessageBox.Show("文件Multi_String.txt不存在!");
            }
        }

完整测试效果图如下:

1、最初打开状态:

2、初始化加载多字符串:

3、加载之前保存的条目状态:

4、修改后保存目前条目状态:

 5、先全删,再加载上次条目状态:

测试工程下载:

https://download.csdn.net/download/qq_64257614/89642319

 

遇到的问题记录:

这是我想在串口接收中断进行一系列数据处理分流遇到的问题,

我想在串口接收中断中进行以下操作:

1、在串口缓冲区按格式处理数据,处理好的存在一个字典列表中,供表格曲线控件打印

2、原始数据根据勾选框来转化为ASCLL或HEX形式打印在接收区文本框中

起初,我直接想在串口接收中断这个线程直接更新文本框UI的内容,但不行,因为串口接收中断线程与文本框UI线程不属于一个线程,不能直接互相传递函数变量等:

因此需要使用一些方法来确保我对UI的更新,是在UI线程上进行:

serialPort1_DataReceived 事件处理程序中,使用 InvokeBeginInvoke来确保 UI 更新在正确的线程(UI 线程)上执行

起初我使用了Invoke方法,因为比较简单,是同步更新的逻辑,不需要额外写函数委托,可以正常更新UI的文本内容

但是后来,在我频繁开关串口时发生了以下报错:

这导致了程序的卡死!

System.ObjectDisposedException:“无法访问已释放的对象。
ObjectDisposed_ObjectName_Name”
                Invoke((MethodInvoker)delegate {Serial_Receiving_area.AppendText(displayText); });发生在我频繁开关串口时

在串口接收逻辑中,当串口被关闭时,如果仍然有数据在serialPort1_DataReceived事件处理程序中处理,或者程序试图从已经关闭的串口读取数据,这可能会导致程序挂起或卡死。

System.ObjectDisposedException 异常通常发生在尝试访问一个已经被释放(或销毁)的对象时。在串口通信的上下文中,这通常意味着在 serialPort1_DataReceived 事件处理程序中,SerialPort 对象 sp 或其他相关对象(如 UI 控件)在事件处理过程中被关闭了

这里的底层逻辑是:

使用 Control.InvokeControl.BeginInvoke 方法来在 UI 线程上执行对控件的更新。这两个方法都用于在控件的拥有线程(即 UI 线程)上执行代码委托。

Invoke 是同步的,它会等待委托执行完毕才继续执行后面的代码;

BeginInvoke 是异步的,它会立即返回,让委托在后台执行。

因此在这个接收中断里更新UI文本显示的逻辑里,明显是BeginInvoke更适合这个委托调用

解决后的源码如下:

       //串口接收逻辑
       private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
       {
           SerialPort sp = (SerialPort)sender;
           string indata = sp.ReadExisting(); // 读取所有可用的数据  
           string[] lines = indata.Split('\n');
           bool autoLinefeed = Auto_linefeed.Checked; // 获取Auto_linefeed的选中状态  
                                                      // 这是你想要显示的文本  

          // string displayText = "Hello, World!";

           if (!isSerialPortOpen) { return; }// 如果串口已关闭,则直接返回

           //Receiving_Area_Write();
           foreach (string line in lines)
           {
               userCurve.Curve_Main(line); 
           }
           // 直接在UI线程上更新文本区(假设这个方法在UI线程上被调用)  
            或者使用Invoke/BeginInvoke来确保在UI线程上执行  
           // 遍历每一行数据  
           foreach (string line in lines)
           {
               userCurve.Curve_Main(line); // 处理每行数据(假设这个方法不需要UI线程) 
               if (HEX_radio.Checked)
               {
                   // 将字符串转换为字节数组,然后转换为十六进制字符串  
                   // 这里对每行数据单独进行转换  
                   byte[] bytes = Encoding.Default.GetBytes(line);
                   string hexLine = UserInterface.byteToHexStr(bytes);
                   if (Showtime_check.Checked)
                   { displayText = "[" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "]收→" + hexLine; }
                   else
                   {displayText = hexLine; }
               }
               else if (ASCII_radio.Checked)
               {
                   if (Showtime_check.Checked)
                   {displayText = "[" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "]收→" + line;}
                   else
                   { displayText = line; }
               }
               // 如果启用了自动换行,则在文本末尾添加换行符  
               if (autoLinefeed){ displayText += "\r\n"; }
               //使用Invoke来确保在UI线程上更新文本区
               //Invoke((MethodInvoker)delegate { Serial_Receiving_area.AppendText(displayText); });

               UpdateTextBox(displayText);
           }
       }

       // Serial_Receiving_area 是一个 TextBox 控件  
       // 定义一个方法,用于在 UI 线程上安全地更新文本框  
       private void UpdateTextBox(string text)
       {
           if (Serial_Receiving_area.InvokeRequired)
           {
               // 如果需要跨线程操作,则使用 Invoke 来在 UI 线程上执行  
               Serial_Receiving_area.BeginInvoke(new Action<string>(UpdateTextBox), text);
           }
           else
           {
               // 直接在 UI 线程上更新文本框  
               Serial_Receiving_area.AppendText(text);
           }
       }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2045900.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Hbase图形化界面

分享一个好用的hbase图形化界面 安装包&#xff1a;链接: https://pan.baidu.com/s/11Y2cDlme-P2xe--pYqy6MQ?pwdguag 提取码: guag 1、上传项目到linux 2、修改数据库配置信息 application-druid.yml 修改url、username、password为数据库连接信息 3、创建数据库(注意字符集…

display:flex布局,最简单的案例

1. 左右贴边 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><style>#parent{width: 800px;background: red;height: 200px;display: flex;justify-content: space-between…

vscode 远程免密登录

Windows R 输入 cmd在命令行终端中输入 ssh-keygen 一直回车、确定 生成秘钥 3. C:\用户\xxx.ssh 拷贝公钥内容 id_rsa.pub 4. 在虚拟机~/.ssh/ 下创建文件touch authorized_keys,拷贝公钥内容 id_rsa.pub粘贴到authorized_keys里即可。

某市-2024【网安·理论】初赛-web1-扫雷-wp

进来是个简单的扫雷 看源码是纯js写的 看了下主要格子之类的生成逻辑在jms.js里 其中flag的输出条件也包含在jms.js 格式化了一下 看特征是base64了&#xff0c;然后又经过了别的操作&#xff0c;不过他混淆了一下就懒得看了。 知道的是每过一个难度的都可以拿到1/3个fl…

【网络】UDP回显服务器和客户端的构造,以及连接流程

回显服务器&#xff08;Echo Server&#xff09; 最简单的客户端服务器程序&#xff0c;不涉及到业务流程&#xff0c;只是对与 API 的用法做演示 客户端发送什么样的请求&#xff0c;服务器就返回什么样的响应&#xff0c;没有任何业务逻辑&#xff0c;没有进行任何计算或者…

【操作系统】什么是进程?什么是线程?两者有什么区别(面试常考!!!)

什么是进程/任务&#xff08;Process/Task&#xff09; 当我们打开我们的电脑的任务管理器就可以看到我们的电脑正在执行的进程。 每个应用程序运行于现代操作系统之上时&#xff0c;操作系统会提供一种抽象&#xff0c;好像系统上只有这个程序在运行&#xff0c;所有的硬件资…

基于Springboot 和Vue 的高校宿舍管理系统源码

网络上很多宿舍管理系统都不完整&#xff0c;大多数缺少数据库文件&#xff0c;所在使用极其不方便&#xff0c;由于本人程序员&#xff0c;根据代码&#xff0c;自己花时间不全了数据库文件&#xff0c;并且可以完美运行&#xff01;&#xff01;&#xff01;&#xff01;&…

使用MQ的考量:系统可用性与复杂性

使用MQ的考量&#xff1a;系统可用性与复杂性 一、降低系统可用性二、增加系统复杂性 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 消息队列&#xff08;MQ&#xff09;在软件开发中扮演重要角色&#xff0c;但其使用也带来一些问题。本文…

原型与原型链与继承

原型、原型链与继承 构造函数 构造函数创建实例的过程 1.创建一个新对象 2.将空对象的__proto__指向构造函数的原型 3.修改构造函数中this指向&#xff0c;将构造函数中的this指向实例对象&#xff0c;执行构造函数中的代码&#xff0c;给这个新对象添加属性和方法&#x…

机器学习笔记:注意力机制中多头注意力的实现

目录 介绍 模型 代码实现 引入库 单个注意力头 多个注意力头的实现 测试 思考 介绍 在注意力机制中&#xff0c;单个注意力学到的东西有限&#xff0c;可以通过对不同的注意力进行组合&#xff0c;学到不同的知识&#xff0c;以达到想要的目的。因此采用”多头注意力…

windows 安装 Mysql

一、安装Mysql 下载完成后直接双击进行安装 安装一路默认 如下图所示&#xff0c;在MySQL Servers/MySQL Server/MySQL Server 5.7的下方找到MySQL Server 5.7.41 - X64&#xff0c;然后选中它&#xff0c;点击两框之间的第一个箭头&#xff0c;将其移到右边的框中 点击Exe…

接口基础知识8_详解response header(响应头)

课程大纲 一、定义 HTTP响应头&#xff08;HTTP Response Header&#xff09;&#xff1a;在HTTP协议中用于描述服务器响应的元数据。 它是服务器在响应客户端请求时&#xff0c;发送给客户端的一部分响应信息&#xff0c;包含了服务器的相关配置和响应内容的描述。 二、常见…

[机器学习]--KNN算法(K邻近算法)

KNN (K-Nearest Neihbor,KNN)K近邻是机器学习算法中理论最简单,最好理解的算法,是一个 非常适合入门的算法,拥有如下特性: 思想极度简单,应用数学知识少(近乎为零),对于很多不擅长数学的小伙伴十分友好虽然算法简单,但效果也不错 KNN算法原理 上图是每一个点都是一个肿瘤病例…

【C++深度探索】unordered_set、unordered_map封装

&#x1f525; 个人主页&#xff1a;大耳朵土土垚 &#x1f525; 所属专栏&#xff1a;C从入门至进阶 这里将会不定期更新有关C/C的内容&#xff0c;欢迎大家点赞&#xff0c;收藏&#xff0c;评论&#x1f973;&#x1f973;&#x1f389;&#x1f389;&#x1f389; 文章目录…

CSS继承、盒子模型、float浮动、定位、diaplay

一、CSS继承 1.文字相关的样式会被子元素继承。 2.布局样式相关的不会被子元素继承。&#xff08;用inherit可以强行继承&#xff09; 实现效果&#xff1a; 二、盒子模型 每个标签都有一个盒子模型&#xff0c;有内容区、内边距、边框、外边距。 从内到外&#xff1a;cont…

基于 Android studio 实现停车场管理系统--原创

目录 一、项目演示 二、开发环境 三、项目页面 四、项目详情 五、项目完整源码 一、项目演示 二、开发环境 三、项目详情 1.启动页 这段代码是一个简单的Android应用程序启动活动&#xff08;Activity&#xff09;&#xff0c;具体功能如下&#xff1a; 1. **延迟进入登…

计算机网络三级笔记--原创 风远 恒风远博

典型设备中间设备数据单元网络协议物理层中继器、集线器中继器、集线器数据位(bit) binary digit二进 制数据的缩写HUB使用了光纤、 同轴电缆、双绞 线.数据链路层网卡、网桥、交换机网桥、交换机数据帧(Frame)STP、ARQ、 SW、CSMA/CD、 PPP(点对点)、 HDLC、ATM网络层路由器、…

SQL注入(cookie、base64、dnslog外带、搜索型注入)

目录 COOKIE注入 BASE64注入 DNSLOG注入—注入判断 什么是泛解析&#xff1f; UNC路径 网上邻居 LOAD_FILE函数 搜索型注入—注入判断 本文所使用的sql注入靶场为sqli-labs-master&#xff0c;靶场资源文件已上传&#xff0c;如有需要请前往主页或以下链接下载 信安必备…

【漫谈C语言和嵌入式002】嵌入式中的大小端

在计算机科学中&#xff0c;"端序"&#xff08;Endianness&#xff09;是指多字节数据类型&#xff08;如整数或浮点数&#xff09;在内存中的存储方式。主要分为两种&#xff1a;大端模式&#xff08;Big-Endian&#xff09;和小端模式&#xff08;Little-Endian&am…

星戈瑞FITC-DXMS荧光素标记地塞米松不同方向的应用

FITC-DXMS&#xff0c;全称异硫氰基荧光素-地塞米松&#xff0c;是一种创新的科研试剂。他是由FITC-NH2的&#xff08;-NH2&#xff09;氨基与地塞米松的-OH&#xff08;羟基&#xff09;结合。它结合了地塞米松的特性和荧光素的高灵敏度标记技术&#xff0c;为医药研究、生物医…