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

C# .NET多窗体场景下如何扩展串口通信类支持第二个窗体?

解决串口通信类支持多窗体(Form1/Form2)的方案

核心思路

将串口类与具体窗体解耦,通过事件机制传递接收的数据,让任意窗体都能订阅串口消息;同时保留统一的发送接口,供所有窗体调用,避免绑定单一窗体的紧耦合问题。

修改步骤

1. 重构SerialComm类,移除Form1依赖并添加事件机制

修改后的串口类代码:

namespace SerialPort_GUI
{
    // 定义接收数据的事件参数类,用于传递接收内容
    public class SerialDataReceivedEventArgs : EventArgs
    {
        public string ReceivedData { get; set; }
        public byte[] ReceivedBytes { get; set; } // 如需字节数据可保留
    }

    class SerialComm : SerialPort
    {
        private bool IsReceiving = false;
        private bool IsRuning = false;
        private bool IsPeriodSending = false;
        private int sleepTime = 200;
        Thread receivingThread = null;

        // 定义接收数据的事件,供外部窗体订阅
        public event EventHandler<SerialDataReceivedEventArgs> DataReceived;

        // 无参构造函数,移除Form1依赖
        public SerialComm()
        {
            base.WriteTimeout = 100;
            receivingThread = new Thread(DoReceive);
            IsRuning = true;
            receivingThread.Start();
        }

        private void DoReceive()
        {
            while (IsRuning)
            {
                if (IsOpen && !IsReceiving)
                {
                    IsReceiving = true;
                    try
                    {
                        // 根据实际需求读取数据,示例为读取字符串
                        string data = ReadExisting();
                        // 如需字节数组可替换为:byte[] bytes = ReadBytes(1024);
                        if (!string.IsNullOrEmpty(data))
                        {
                            // 触发事件,将数据传递给订阅的窗体
                            DataReceived?.Invoke(this, new SerialDataReceivedEventArgs
                            {
                                ReceivedData = data
                                // ReceivedBytes = bytes // 如需字节数据可启用
                            });
                        }
                    }
                    catch (Exception ex)
                    {
                        // 此处可添加异常日志或提示逻辑
                    }
                    finally
                    {
                        IsReceiving = false;
                        Thread.Sleep(sleepTime);
                    }
                }
                else
                {
                    Thread.Sleep(sleepTime);
                }
            }
        }

        // 封装公共发送方法,供所有窗体调用
        public void SendData(string data)
        {
            if (IsOpen)
            {
                try
                {
                    Write(data);
                }
                catch (Exception ex)
                {
                    // 处理发送异常逻辑
                }
            }
        }

        // 重载发送方法,支持字节数组
        public void SendData(byte[] data)
        {
            if (IsOpen)
            {
                try
                {
                    Write(data, 0, data.Length);
                }
                catch (Exception ex)
                {
                    // 处理发送异常逻辑
                }
            }
        }

        // 停止串口线程与连接的方法
        public void Stop()
        {
            IsRuning = false;
            receivingThread?.Join();
            Close();
        }
    }
}

2. 在Form1中使用重构后的SerialComm

全局声明串口实例,确保Form2可复用同一连接:

public partial class Form1 : Form
{
    // 全局串口实例,Form2将使用同一实例
    public SerialComm serialComm;

    public Form1()
    {
        InitializeComponent();
        serialComm = new SerialComm();
        // 订阅串口接收事件
        serialComm.DataReceived += SerialComm_DataReceived;
    }

    private void SerialComm_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        // 跨线程更新UI需使用Invoke
        Invoke(new Action(() =>
        {
            // Form1的UI更新逻辑,比如显示接收数据
            txtReceived.Text += e.ReceivedData;
        }));
    }

    // 串口连接与发送密码逻辑
    private void btnConnect_Click(object sender, EventArgs e)
    {
        try
        {
            serialComm.PortName = txtPortName.Text;
            serialComm.BaudRate = int.Parse(txtBaudRate.Text);
            serialComm.Open();
            // 发送密码
            serialComm.SendData("YourDevicePassword");
        }
        catch (Exception ex)
        {
            MessageBox.Show("连接失败:" + ex.Message);
        }
    }

    // 打开Form2时传递串口实例
    private void btnSettings_Click(object sender, EventArgs e)
    {
        Form2 form2 = new Form2(serialComm);
        form2.ShowDialog();
    }

    // Form1关闭时停止串口
    private void Form1_FormClosed(object sender, FormClosedEventArgs e)
    {
        serialComm.Stop();
    }
}

3. 在Form2中使用SerialComm实例

通过构造函数接收Form1传递的串口实例,实现发送与接收逻辑:

public partial class Form2 : Form
{
    private SerialComm _serialComm;

    // 构造函数接收串口实例
    public Form2(SerialComm serialComm)
    {
        InitializeComponent();
        _serialComm = serialComm;
        // 订阅接收事件(如需在Form2处理响应)
        _serialComm.DataReceived += SerialComm_DataReceived;
    }

    private void SerialComm_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        // 跨线程更新UI
        Invoke(new Action(() =>
        {
            // Form2的UI反馈逻辑
            lblStatus.Text = "设备响应:" + e.ReceivedData;
        }));
    }

    // 示例:设置按钮1的发送逻辑
    private void btnSetting1_Click(object sender, EventArgs e)
    {
        _serialComm.SendData("SETTING_1_COMMAND");
    }

    // 示例:设置按钮2的发送逻辑
    private void btnSetting2_Click(object sender, EventArgs e)
    {
        _serialComm.SendData("SETTING_2_COMMAND");
    }

    // 其余10个按钮同理,调用SendData发送对应指令

    // Form2关闭时取消事件订阅(可选)
    private void Form2_FormClosed(object sender, FormClosedEventArgs e)
    {
        _serialComm.DataReceived -= SerialComm_DataReceived;
    }
}

关键说明

  • 解耦设计:移除串口类对Form1的依赖,改用事件驱动,让任意窗体都能接入串口通信。
  • 线程安全:串口接收在后台线程执行,更新UI必须使用Invoke避免跨线程异常。
  • 单实例复用:Form1与Form2共用同一SerialComm实例,确保串口连接唯一,避免重复打开端口。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 15:08:37