GtkSharp跨平台WinForm实现

发布于:2025-06-30 ⋅ 阅读:(19) ⋅ 点赞:(0)

跨平台架构设计

基于GTKSystem.Windows.Forms框架,我们可以实现真正的跨平台WinForm串口通讯应用:

共享代码层
macOS环境
Linux环境
Windows环境
业务逻辑层
串口通讯层
数据处理层
GTK# 3.24
GTKSystem.WinForm
macOS串口驱动
GTK# 3.24
GTKSystem.WinForm
Linux串口驱动
System.IO.Ports
WinForm应用
Win32 API

跨平台项目配置

首先需要正确配置项目文件以支持跨平台运行:

<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <UseWindowsForms>false</UseWindowsForms>
    <!-- 关键:禁用Windows专用的WindowsForms -->
  </PropertyGroup>

  <ItemGroup>
    <!-- 添加GTKSystem.Windows.Forms包 -->
    <PackageReference Include="GTKSystem.Windows.Forms" Version="3.24.24.105" />
    <PackageReference Include="System.IO.Ports" Version="8.0.0" />
  </ItemGroup>
</Project>

GtkSharp串口通讯实现

using GTKSystem.Windows.Forms;
using System;
using System.IO.Ports;
using System.Text;
using System.Threading;

/// <summary>
/// 基于GTKSystem的跨平台串口通讯窗体
/// 支持Windows、Linux、macOS三大平台
/// </summary>
public partial class CrossPlatformSerialForm : Form
{
    private SerialPort serialPort;
    private ComboBox cmbPortName;
    private ComboBox cmbBaudRate;
    private TextBox txtReceived;
    private TextBox txtSend;
    private Button btnConnect;
    private Button btnSend;
    private Label lblStatus;
    private bool isConnected = false;

    public CrossPlatformSerialForm()
    {
        InitializeComponent();
        InitializeSerialPort();
        LoadAvailablePorts();
    }

    /// <summary>
    /// 初始化界面组件
    /// </summary>
    private void InitializeComponent()
    {
        this.Text = "跨平台串口通讯工具";
        this.Size = new System.Drawing.Size(800, 600);
        this.StartPosition = FormStartPosition.CenterScreen;

        // 创建控件
        CreateControls();
        LayoutControls();
    }

    /// <summary>
    /// 创建界面控件
    /// </summary>
    private void CreateControls()
    {
        // 串口配置区域
        var lblPort = new Label { Text = "串口:", Location = new System.Drawing.Point(10, 15) };
        cmbPortName = new ComboBox 
        { 
            Location = new System.Drawing.Point(60, 12), 
            Size = new System.Drawing.Size(100, 23),
            DropDownStyle = ComboBoxStyle.DropDownList
        };

        var lblBaud = new Label { Text = "波特率:", Location = new System.Drawing.Point(180, 15) };
        cmbBaudRate = new ComboBox 
        { 
            Location = new System.Drawing.Point(240, 12), 
            Size = new System.Drawing.Size(100, 23),
            DropDownStyle = ComboBoxStyle.DropDownList
        };
        
        // 添加常用波特率
        cmbBaudRate.Items.AddRange(new object[] { 9600, 19200, 38400, 57600, 115200 });
        cmbBaudRate.SelectedIndex = 0;

        btnConnect = new Button 
        { 
            Text = "连接", 
            Location = new System.Drawing.Point(360, 10), 
            Size = new System.Drawing.Size(80, 25)
        };
        btnConnect.Click += BtnConnect_Click;

        lblStatus = new Label 
        { 
            Text = "状态: 未连接", 
            Location = new System.Drawing.Point(460, 15),
            Size = new System.Drawing.Size(200, 20),
            ForeColor = System.Drawing.Color.Red
        };

        // 数据接收区域
        var lblReceive = new Label 
        { 
            Text = "接收数据:", 
            Location = new System.Drawing.Point(10, 50),
            Size = new System.Drawing.Size(100, 20)
        };
        
        txtReceived = new TextBox 
        { 
            Location = new System.Drawing.Point(10, 75), 
            Size = new System.Drawing.Size(760, 300),
            Multiline = true,
            ReadOnly = true,
            ScrollBars = ScrollBars.Vertical
        };

        // 数据发送区域
        var lblSend = new Label 
        { 
            Text = "发送数据:", 
            Location = new System.Drawing.Point(10, 390),
            Size = new System.Drawing.Size(100, 20)
        };
        
        txtSend = new TextBox 
        { 
            Location = new System.Drawing.Point(10, 415), 
            Size = new System.Drawing.Size(680, 25)
        };
        
        btnSend = new Button 
        { 
            Text = "发送", 
            Location = new System.Drawing.Point(700, 413), 
            Size = new System.Drawing.Size(70, 29),
            Enabled = false
        };
        btnSend.Click += BtnSend_Click;

        // 将控件添加到窗体
        this.Controls.AddRange(new Control[] 
        {
            lblPort, cmbPortName, lblBaud, cmbBaudRate, btnConnect, lblStatus,
            lblReceive, txtReceived, lblSend, txtSend, btnSend
        });
    }

    /// <summary>
    /// 布局控件(可选的美化布局)
    /// </summary>
    private void LayoutControls()
    {
        // 可以在这里添加更复杂的布局逻辑
        // GTKSystem支持大部分标准的WinForm布局特性
    }

    /// <summary>
    /// 初始化串口对象
    /// </summary>
    private void InitializeSerialPort()
    {
        serialPort = new SerialPort();
        serialPort.DataReceived += SerialPort_DataReceived;
        serialPort.ErrorReceived += SerialPort_ErrorReceived;
    }

    /// <summary>
    /// 加载可用串口列表
    /// 跨平台自动识别串口设备
    /// </summary>
    private void LoadAvailablePorts()
    {
        try
        {
            cmbPortName.Items.Clear();
            string[] ports = SerialPort.GetPortNames();
            
            if (ports.Length == 0)
            {
                cmbPortName.Items.Add("无可用串口");
                lblStatus.Text = "状态: 未找到可用串口";
                lblStatus.ForeColor = System.Drawing.Color.Orange;
            }
            else
            {
                cmbPortName.Items.AddRange(ports);
                cmbPortName.SelectedIndex = 0;
                lblStatus.Text = $"状态: 找到 {ports.Length} 个串口";
                lblStatus.ForeColor = System.Drawing.Color.Blue;
            }
        }
        catch (Exception ex)
        {
            ShowError($"加载串口列表失败: {ex.Message}");
        }
    }

    /// <summary>
    /// 连接/断开按钮事件处理
    /// </summary>
    private void BtnConnect_Click(object sender, EventArgs e)
    {
        if (isConnected)
        {
            DisconnectSerial();
        }
        else
        {
            ConnectSerial();
        }
    }

    /// <summary>
    /// 连接串口
    /// </summary>
    private void ConnectSerial()
    {
        try
        {
            if (cmbPortName.SelectedItem == null)
            {
                ShowError("请选择串口");
                return;
            }

            string portName = cmbPortName.SelectedItem.ToString();
            if (portName == "无可用串口")
            {
                ShowError("没有可用的串口");
                return;
            }

            // 配置串口参数
            serialPort.PortName = portName;
            serialPort.BaudRate = (int)cmbBaudRate.SelectedItem;
            serialPort.DataBits = 8;
            serialPort.Parity = Parity.None;
            serialPort.StopBits = StopBits.One;
            serialPort.Handshake = Handshake.None;

            // 设置超时
            serialPort.ReadTimeout = 3000;
            serialPort.WriteTimeout = 3000;

            // 打开串口
            serialPort.Open();
            isConnected = true;

            // 更新界面状态
            UpdateConnectionStatus(true);
            
            // 清空接收区域
            txtReceived.Clear();
            
            ShowInfo($"成功连接到 {portName}");
        }
        catch (Exception ex)
        {
            ShowError($"连接失败: {ex.Message}");
            isConnected = false;
            UpdateConnectionStatus(false);
        }
    }

    /// <summary>
    /// 断开串口连接
    /// </summary>
    private void DisconnectSerial()
    {
        try
        {
            if (serialPort != null && serialPort.IsOpen)
            {
                serialPort.Close();
            }
            isConnected = false;
            UpdateConnectionStatus(false);
            ShowInfo("已断开连接");
        }
        catch (Exception ex)
        {
            ShowError($"断开连接失败: {ex.Message}");
        }
    }

    /// <summary>
    /// 发送数据按钮事件处理
    /// </summary>
    private void BtnSend_Click(object sender, EventArgs e)
    {
        SendData();
    }

    /// <summary>
    /// 发送数据
    /// </summary>
    private void SendData()
    {
        try
        {
            if (!isConnected || !serialPort.IsOpen)
            {
                ShowError("请先连接串口");
                return;
            }

            string textToSend = txtSend.Text;
            if (string.IsNullOrEmpty(textToSend))
            {
                ShowError("请输入要发送的数据");
                return;
            }

            // 发送数据
            serialPort.WriteLine(textToSend);
            
            // 在接收区域显示发送的数据
            AppendReceivedText($"[发送] {DateTime.Now:HH:mm:ss} - {textToSend}");
            
            // 清空发送文本框
            txtSend.Clear();
        }
        catch (Exception ex)
        {
            ShowError($"发送数据失败: {ex.Message}");
        }
    }

    /// <summary>
    /// 串口数据接收事件处理
    /// </summary>
    private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        try
        {
            // 读取接收到的数据
            string receivedData = serialPort.ReadExisting();
            
            // 跨线程更新UI
            this.Invoke(new Action(() =>
            {
                AppendReceivedText($"[接收] {DateTime.Now:HH:mm:ss} - {receivedData.Trim()}");
            }));
        }
        catch (Exception ex)
        {
            this.Invoke(new Action(() =>
            {
                ShowError($"接收数据异常: {ex.Message}");
            }));
        }
    }

    /// <summary>
    /// 串口错误事件处理
    /// </summary>
    private void SerialPort_ErrorReceived(object sender, SerialErrorReceivedEventArgs e)
    {
        this.Invoke(new Action(() =>
        {
            ShowError($"串口错误: {e.EventType}");
        }));
    }

    /// <summary>
    /// 更新连接状态
    /// </summary>
    private void UpdateConnectionStatus(bool connected)
    {
        btnConnect.Text = connected ? "断开" : "连接";
        btnSend.Enabled = connected;
        cmbPortName.Enabled = !connected;
        cmbBaudRate.Enabled = !connected;
        
        lblStatus.Text = connected ? "状态: 已连接" : "状态: 未连接";
        lblStatus.ForeColor = connected ? System.Drawing.Color.Green : System.Drawing.Color.Red;
    }

    /// <summary>
    /// 在接收文本框中追加文本
    /// </summary>
    private void AppendReceivedText(string text)
    {
        txtReceived.AppendText(text + Environment.NewLine);
        txtReceived.SelectionStart = txtReceived.Text.Length;
        txtReceived.ScrollToCaret();
    }

    /// <summary>
    /// 显示错误消息
    /// </summary>
    private void ShowError(string message)
    {
        MessageBox.Show(message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }

    /// <summary>
    /// 显示信息消息
    /// </summary>
    private void ShowInfo(string message)
    {
        MessageBox.Show(message, "信息", MessageBoxButtons.OK, MessageBoxIcon.Information);
    }

    /// <summary>
    /// 窗体关闭时清理资源
    /// </summary>
    protected override void OnFormClosed(FormClosedEventArgs e)
    {
        if (serialPort != null && serialPort.IsOpen)
        {
            serialPort.Close();
        }
        serialPort?.Dispose();
        base.OnFormClosed(e);
    }
}

跨平台部署配置

Linux系统配置
# 安装GTK运行时环境
sudo apt-get update
sudo apt-get install gtk-sharp3-dev

# 设置串口权限
sudo usermod -a -G dialout $USER
sudo chmod 666 /dev/ttyUSB*
sudo chmod 666 /dev/ttyACM*

# 运行应用程序
dotnet run
macOS系统配置
# 使用Homebrew安装GTK
brew install gtk+3

# 设置串口权限
sudo dseditgroup -o edit -a $USER -t user wheel

# 运行应用程序
dotnet run

相关学习资源

GTK#跨平台开发
跨平台.NET开发
Linux开发环境
macOS开发环境
跨平台UI框架对比
容器化部署
开源项目参考
性能优化与调试

在这里插入图片描述