先看winform整体布局
以下是整体代码以及注释
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;
using System.IO.Ports;
namespace WindowsFormsApp2
{
public partial class Form1 : Form
{
String serialPortName;
public Form1()
{
InitializeComponent();
serialPort1.DataReceived += new SerialDataReceivedEventHandler(serialPort1_DataReceived);
}
private void Form1_Load(object sender, EventArgs e)
{
string[] ports = System.IO.Ports.SerialPort.GetPortNames(); // 获取本机可用串口端口
comboBoxPort.Items.AddRange(ports);
comboBoxPort.SelectedIndex = comboBoxPort.Items.Count > 0 ? 0 : -1; // 有可用端口显示第一个
string[] Bauds = new string[] { "自定義", "4800", "9600", "12800", "19200", "38400" };
comboBoxBaudrate.DataSource = Bauds;
comboBoxBaudrate.SelectedIndex = 2;
int[] datas = new int[] { 5, 6, 7, 8 };
comboBoxDataBits.DataSource = datas;
comboBoxDataBits.SelectedIndex = 3;
string[] stopBits = new string[] { "1", "1.5", "2" };
comboBoxStopbit.DataSource = stopBits;
comboBoxStopbit.SelectedIndex = 0;
string[] paritys = new string[] { "無", "奇校驗", "偶校驗" };
comboBoxCheck.DataSource = paritys;
comboBoxCheck.SelectedIndex = 0;
}
/// <summary>
/// 重写系统消息函数
/// </summary>
/// <param name="m"></param>
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
}
/// <summary>
/// 串口数据接收
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
// 获取当前串口可读取的字节数
int len = serialPort1.BytesToRead;
// 根据len的大小创建一个byte数组buff用于存储从串口读取的数据
byte[] buff = new byte[len];
// 从串口读取数据到buff数组中,从索引0开始,读取长度为len的字节
serialPort1.Read(buff, 0, len);
// 将byte数组buff中的数据转换成默认编码(通常是ASCII)的字符串str
string str = Encoding.Default.GetString(buff);
Invoke((new Action(() => {
if (checkBox3.Checked) // 黑底白字
{
textBoxRecv.BackColor = Color.Black;
textBoxRecv.ForeColor = Color.White;
}
else
{
textBoxRecv.BackColor = SystemColors.Window; // 默认窗口背景色
textBoxRecv.ForeColor = SystemColors.WindowText; // 默认文本颜色
}
if (checkBoxHexShow.Checked) // Hex(16进制显示)
{
// 调用byteToHexStr方法将buff数组转换为十六进制字符串
string hexStr = byteToHexStr(buff);
if (checkBoxRtsShow.Checked && checkBox5.Checked && checkBoxDtrShow.Checked) // 同时选择 时间戳、RTS、DTR显示
{
textBoxRecv.AppendText($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] RTS:{serialPort1.RtsEnable} DTR:{serialPort1.DtrEnable} {hexStr}{Environment.NewLine}");
}
else if (checkBoxRtsShow.Checked && checkBox5.Checked) // 同时选择 时间戳和RTS显示
{
textBoxRecv.AppendText($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] RTS:{serialPort1.RtsEnable} {hexStr}{Environment.NewLine}");
}
else if (checkBoxDtrShow.Checked && checkBox5.Checked) // 同时选择 时间戳和DTR显示
{
textBoxRecv.AppendText($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] DTR:{serialPort1.DtrEnable} {hexStr}{Environment.NewLine}");
}
else if (checkBoxDtrShow.Checked && checkBoxRtsShow.Checked) // 同时选择 RTS和DTR显示
{
textBoxRecv.AppendText($"RTS:{serialPort1.RtsEnable} DTR:{serialPort1.DtrEnable} {hexStr}{Environment.NewLine}");
}
else if (checkBox5.Checked) // 只显示时间戳
{
textBoxRecv.AppendText($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] {hexStr}{Environment.NewLine}");
}
else if (checkBoxRtsShow.Checked) // 只显示RTS
{
textBoxRecv.AppendText($"RTS:{serialPort1.RtsEnable} {hexStr}{Environment.NewLine}");
}
else if (checkBoxRtsShow.Checked) // 只显示DTR
{
textBoxRecv.AppendText($"DTR:{serialPort1.DtrEnable} {hexStr}{Environment.NewLine}");
}
else // 不选择任何项
{
textBoxRecv.AppendText($"{hexStr}{Environment.NewLine}");
}
}
else // 直接将ASCII字符串str追加到文本框中
{
if (checkBoxRtsShow.Checked && checkBox5.Checked && checkBoxDtrShow.Checked) // 同时选择 时间戳、RTS、DTR显示
{
textBoxRecv.AppendText($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] RTS:{serialPort1.RtsEnable} DTR:{serialPort1.DtrEnable} {str} {Environment.NewLine}");
}
else if (checkBoxRtsShow.Checked && checkBox5.Checked) // 同时选择 时间戳和RTS显示
{
textBoxRecv.AppendText($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] RTS:{serialPort1.RtsEnable} {str}{Environment.NewLine}");
}
else if (checkBoxDtrShow.Checked && checkBox5.Checked) // 同时选择 时间戳和DTR显示
{
textBoxRecv.AppendText($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] DTR:{serialPort1.DtrEnable} {str}{Environment.NewLine}");
}
else if (checkBoxDtrShow.Checked && checkBoxRtsShow.Checked) // 同时选择 RTS和DTR显示
{
textBoxRecv.AppendText($"RTS:{serialPort1.RtsEnable} DTR:{serialPort1.DtrEnable} {str}{Environment.NewLine}");
}
else if (checkBox5.Checked) // 只选择 时间戳显示
{
textBoxRecv.AppendText($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] {str}{Environment.NewLine}");
}
else if (checkBoxRtsShow.Checked) // 只选择 RTS显示
{
textBoxRecv.AppendText($"RTS:{serialPort1.RtsEnable} {str}{Environment.NewLine}");
}
else if (checkBoxDtrShow.Checked) // 只选择 DTR显示
{
textBoxRecv.AppendText($"DTR:{serialPort1.DtrEnable} {str}{Environment.NewLine}");
}
else // 不选择任何额外选项
{
textBoxRecv.AppendText($"{str}{Environment.NewLine}");
}
}
})));
}
/// <summary>
/// 构建十六进制,用于接收数据显示出来
/// </summary>
/// <param name="bytes"></param>
/// <returns></returns>
public static string byteToHexStr(byte[] bytes)
{
string hexStr = ""; //用于存储转换后的十六进制字符串
try
{
if (bytes != null) // 检查传入的字节数组是否为null
{
for (int i = 0; i < bytes.Length; i++) // 遍历字节数组中的每一个元素
{
//X2 用于将字节数据格式化为两位的大写十六进制字符串,不足两位时在前面补0
hexStr += bytes[i].ToString("X2");
hexStr += " "; // 在每个十六进制值后面添加一个空格
}
}
return hexStr; // 返回构建好的十六进制字符串
}
catch (Exception)
{
return hexStr; // 如果发生异常,则返回初始的空字符串hexStr
}
}
/// <summary>
/// 打开关闭串口
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void buttonClearSend_Click(object sender, EventArgs e)
{
if (buttonPortOnOff.Text == "打开串口")
{
try
{
serialPort1.PortName = comboBoxPort.Text; // 获取选择的串口端口
serialPortName = comboBoxPort.Text;
serialPort1.BaudRate = int.Parse(comboBoxBaudrate.Text); // 获取选择的波特率
serialPort1.DataBits = int.Parse(comboBoxDataBits.Text); // 获取选择的数据位
// 设置停止位
if (comboBoxStopbit.Text == "1") { serialPort1.StopBits = StopBits.One; }
else if (comboBoxStopbit.Text == "1.5") { serialPort1.StopBits = StopBits.OnePointFive; }
else if (comboBoxStopbit.Text == "2") { serialPort1.StopBits = StopBits.Two; }
// 设置奇偶校验
if (comboBoxCheck.Text == "無") { serialPort1.Parity = Parity.None; }
else if (comboBoxCheck.Text == "偶校驗") { serialPort1.Parity = Parity.Even; }
else if (comboBoxCheck.Text == "奇校驗") { serialPort1.Parity = Parity.Odd; }
// 打开串口
serialPort1.Open();
buttonPortOnOff.Text = "关闭串口";
}
catch (Exception ex)
{
MessageBox.Show("串口打开失败" + ex.ToString(), "警告",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
else
{
try
{
serialPort1.Close();
}
catch (Exception ex)
{
MessageBox.Show("發生錯誤" + ex.ToString(), "警告", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
buttonPortOnOff.Text = "打开串口";
}
}
/// <summary>
/// 清空接收数据
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void buttonClearRecv_Click(object sender, EventArgs e)
{
textBoxRecv.Clear();
}
/// <summary>
/// 发送数据
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void buttonSendData_Click(object sender, EventArgs e)
{
string str = textBoxSend.Text; // 获取文本框里的数据
try
{
if (str.Length > 0)
{
byte[] bytesToSend; // 初始化一個byte字節數組
if (comboBoxEncoding.Checked)
{
// 移除所有空格,然后每两个字符解析为一个字节
str = str.Replace(" ", "");
int numberChars = str.Length; // 获取移除空格后的字符串长度,并存储在变量中40
// 创建一个新的byte数组,其大小为numberChars除以2(因为每个字节由两个字符表示)40/2
bytesToSend = new byte[numberChars / 2];
// 循环遍历字符串,步长为2,即每次处理两个字符
for (int i = 0; i < numberChars; i += 2)
{
// 使用Substring方法从当前索引i开始提取两个字符组成的子串
bytesToSend[i / 2] = Convert.ToByte(str.Substring(i, 2), 16);
}
}
else // 默认为ASCII编码
{
bytesToSend = Encoding.ASCII.GetBytes(str);
}
serialPort1.Write(bytesToSend, 0, bytesToSend.Length); // 使用字节数组发送
}
}
catch (Exception ex)
{
MessageBox.Show($"发送数据失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
/// <summary>
/// 清除发送数据
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void buttonClearSend_Click1(object sender, EventArgs e)
{
textBoxSend.Clear();
}
/// <summary>
/// 保存接收到的数据
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button2_Click(object sender, EventArgs e)
{
string str = textBoxRecv.Text;
bool isSaved = SaveSendRecord(str, "Active");
if (isSaved)
{
ShowNotification("数据保存成功!");
}
}
/// <summary>
/// 保存数据
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
private bool SaveSendRecord(string data, string txt)
{
try
{
string filePath = Path.Combine(Application.StartupPath, $"{txt}.txt");
// 使用File.AppendAllText方法将数据追加到文件末尾,并加上换行符
File.AppendAllText(filePath, $"{DateTime.Now}: {Environment.NewLine} {data}\n");
return true; // 如果保存成功返回true
}
catch (Exception ex)
{
// 处理异常情况
Console.WriteLine($"保存失败: {ex.Message}");
return false; // 如果保存失败返回false
}
}
/// <summary>
/// 保存成功弹窗
/// </summary>
/// <param name="message"></param>
private void ShowNotification(string message)
{
Label notificationLabel = new Label
{
Text = message,
AutoSize = false,
Width = this.ClientSize.Width,
Height = 25,
BackColor = System.Drawing.ColorTranslator.FromHtml("#4CAF50"), // 设置为绿色背景
ForeColor = System.Drawing.SystemColors.Window,
Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Bold),
TextAlign = System.Drawing.ContentAlignment.MiddleCenter
};
// 将通知标签添加到窗体顶部
this.Controls.Add(notificationLabel);
notificationLabel.Location = new System.Drawing.Point(0, 0);
// 启动计时器自动移除通知
Timer timer = new Timer { Interval = 1500 }; // 1.5秒后自动隐藏
timer.Tick += (s, ev) =>
{
notificationLabel.Visible = false;
timer.Stop();
};
timer.Start();
}
}
}