C# 开发桌面应用简单介绍

发布时间 2023-11-28 14:44:36作者: 火星写程序

一. C#使用场景介绍

  C#是微软公司发布的一种由C和C++衍生出来的面向对象的编程语言、运行于.NET Framework和.NET Core(完全开源,跨平台)之上的高级程序设计语言。

二. 开发流程

  1. 创建项目:打开Visual Studio后右侧选择《创建新项目》,然后选择《C# Windows窗体应用》即可创建桌面程序

  2. 创建窗体:创建后会自动创建一个Form窗体作为主窗口,可以采用拖拽方式进行项目开发,通过导航栏的《视图》-->《工具栏》打开内置的工具箱功能

  3. 启动开发:开发时有两种模式,可视化开发和编写代码。可视化开发选中窗体或者元素右侧会弹出属性栏可以设置样式以及定义事件;代码开发可以通过对应的映射文件进行逻辑开发。

  4. 运行程序:开发完成后点击屏幕上方的绿色箭头按钮,“启动”按钮运行程序同时进入Debug模式,支持功能调试,“开始运行”按钮只运行程序,没有进入Debug模式。

  5. 生成解决方案:点击导航栏上的“生成”->“生成解决方案”就可以生成项目文件

三. 常用功能

  1. 弹出框和自动关闭弹框:项目开发中经常需要弹出框以及关闭弹出框,建议根据不同的等级封装统一组件进行调用

// 提示弹框
public static void PopWarning(string tip, string title = "提示")
{
    MessageBox.Show(tip, title);
}

// 错误弹框
public static void PopError(string tip, string title = "错误")
{
    MessageBox.Show(tip, title);
}

// 自动关闭弹框
public void BoxAutoClose(string tip, int time = 3000)
{
    Form msg = new Form();
    Task.Run(new Action(() =>
    {
        Thread.Sleep(time);
        msg.Invoke((Action)(() =>
        {
            msg.Close();
        }));
    }));
    MessageBox.Show(msg, tip, "提示");
}

  2. 串口的使用和指令的封装:项目开发中需要和固件通过串口进行数据交互,C#内置Serialport组件可以使用,但是建议不要拖拽,最好是new一个,然后将发送指令、接收指令以及超时机制结合在一起使用

// 命令类,存储指令和时间对象
class Command {
    // 存储指令数据集
    public Dictionary<string, Hashtable> data = new Dictionary<string, Hashtable>();

    // 默认超时时间是7秒
    public void Set(string id, int timeUpper = 7)
    {
        if (data.ContainsKey(id))
        {
            data[id]["time"] = DateTime.Now;
        }
        else
        {
            Hashtable ht = new Hashtable();
            ht.Add("time", DateTime.Now);
            ht.Add("timeUpper", timeUpper);
            data[id] = ht;
        }
    }

    public void Delete(string id)
    {
        data.Remove(id);
    }

    public void DeleteAll()
    {
        data.Clear();
    }

    /**
     * 识别超时的命令
     */
    public string[] CheckOverTime()
    {
        if (data == null)
        {
            return new string[0] { };
        }
        string[] coms = new string[data.Count];

        for (int i = 0; i < data.Count; i++)
        {
            DateTime val = (DateTime)data.ElementAt(i).Value["time"];
            int timeUpper = (int)data.ElementAt(i).Value["timeUpper"];
            if (new TimeSpan(DateTime.Now.Ticks - val.Ticks).TotalSeconds > timeUpper)
            {
                coms[i] = data.ElementAt(i).Key;
            }
        }

        return coms.Where(e => e != null).ToArray();
    }
}
创建一个Command类,主要存储发送指令集以及判断指令是否超时
class Serial {
    private SerialPort port;
    Form main;
    Command cmd;
    
    public Serial(Form main)
    {
        this.main = main;
        cmd = new Command();
        this.Init();
        this.CreateTimer();
    }

    /**
     * 创建定时器,每秒查询一次是否有超时的命令
     */
    public void CreateTimer()
    {
        System.Timers.Timer cTimer = new System.Timers.Timer();
        cTimer.Interval = 1000;
        cTimer.Enabled = true;
        cTimer.AutoReset = true;
        cTimer.Elapsed += new System.Timers.ElapsedEventHandler((Object source, ElapsedEventArgs e) =>
        {
            string[] cmds = cmd.CheckOverTime();
            for (int i = 0; i < cmds.Length; i++)
            {
                cmd.Delete(cmds[i]);
                if (cmds[i] != "FF")
                {
                    // 返回主窗口超时标识
                    main.DealRes(new byte[] { (byte)Convert.ToInt32(cmds[i], 16) }, 1);
                }
            }
        });
    }

    /**
     * 初始化创建串口组件
     */
    void Init() {

        port = new SerialPort();
        port.BaudRate = 9600;
        port.Parity = Parity.None;
        port.StopBits = StopBits.One;
        port.DataBits = 8;
        port.DataReceived += new SerialDataReceivedEventHandler(ReceiveData);
    }

    /**
     * 将字符数组转换成字符串
     */
    public static string ByteToStr(byte[] data)
    {
        string str = "";
        for (int i = 0; i < data.Length; i++)
        {
            str += data[i].ToString("X2");
        }
        return str;
    }

    /**
     * 接收数据并解析,将解析结果返回主窗口
     */
    public void ReceiveData(object sender, SerialDataReceivedEventArgs e) {
        // 接受数据时建议保留延时200毫秒,否则会存在由于接收不及时,一包数据被分成两段返回的情况
        System.Threading.Thread.Sleep(200);
        main.BeginInvoke((EventHandler)delegate
        {
            if (port.IsOpen)
            {
                int readLength = port.BytesToRead;
                byte[] buff = new byte[readLength];

                port.Read(buff, 0, buff.Length);
                if (buff.Length != 0)
                {
                    
                    cmd.Delete(ByteToStr(new byte[] { buff[0] }));
                    main.DealRes(buff);
                }
            }
        });
    }
}
创建一个串口类,主要用于发送指令和接收返回的数据

  3. 监测串口的插拔状态:框架内置了方法可以监控串口的变化,只需要重写该方法即可

protected override void WndProc(ref Message m)
{
    try
    {
        // Windows消息编号
        if (m.Msg == 0x219)
        {
            if ((bool)portButton1.Tag && !port1.IsOpen && (bool)portButton2.Tag && !port2.IsOpen)
            {
                closePort(true, true);
                Util.PopError("测量串口和校准串口状态有更新");
            }
            else if ((bool)portButton1.Tag && !port1.IsOpen) {
                closePort(true, false);
                Util.PopError("测量串口状态有更新");
            } else if ((bool)portButton2.Tag && !port2.IsOpen) {
                closePort(false, true);
                Util.PopError("校准串口状态有更新");
            }
        }
    }
    catch
    {
        Util.PopError("监控串口状态错误");
    }
    base.WndProc(ref m);
}
监控串口状态

  4. 安装NPOI组件实现Excel的读写功能:Excel的读写依赖三方件nuget。

    安装NPOI有两种办法:

    第一种利用Visual Studio导航栏的“工具”->“NuGet包管理器”进行下载,这种比较简单方便,下载后项目可以直接引用使用

    第二种则是手动安装,解决无法在线安装的情况,比如网络受限等:

    a. Nuget下载:可以从官方https://www.nuget.org/downloads进行下载,将下载的nuget.exe拷贝到某个目录,同时在该目录下打开命令窗口。

    b. NPOI安装:在目录A打开命令窗口,执行命令nuget install NPOI -SolutionDirectory 项目根目录 -PackageSaveMode nupkg,安装后会生成一个packages文件

    

    c. 还需要在项目根目录下的.csproj目录下手动添加引入文件,同时注意packages需要和安装后的目录对应,否则引用无效,安装完成后返回VS工具会提示“全部刷新引用”,同意即可。

<Reference Include="BouncyCastle.Cryptography, Version=2.0.0.0, Culture=neutral, PublicKeyToken=072edcf4a5328938, processorArchitecture=MSIL">
      <HintPath>.\packages\BouncyCastle.Cryptography.2.2.1\lib\net461\BouncyCastle.Cryptography.dll</HintPath>
    </Reference>
    <Reference Include="Enums.NET, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7ea1c1650d506225, processorArchitecture=MSIL">
      <HintPath>.\packages\Enums.NET.4.0.1\lib\net45\Enums.NET.dll</HintPath>
    </Reference>
    <Reference Include="ICSharpCode.SharpZipLib, Version=1.3.3.11, Culture=neutral, PublicKeyToken=1b03e6acf1164f73, processorArchitecture=MSIL">
      <HintPath>.\packages\SharpZipLib.1.3.3\lib\net45\ICSharpCode.SharpZipLib.dll</HintPath>
    </Reference>
    <Reference Include="MathNet.Numerics, Version=4.15.0.0, Culture=neutral, PublicKeyToken=cd8b63ad3d691a37, processorArchitecture=MSIL">
      <HintPath>.\packages\MathNet.Numerics.Signed.4.15.0\lib\net461\MathNet.Numerics.dll</HintPath>
    </Reference>
    <Reference Include="Microsoft.IO.RecyclableMemoryStream, Version=2.3.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
      <HintPath>.\packages\Microsoft.IO.RecyclableMemoryStream.2.3.2\lib\net462\Microsoft.IO.RecyclableMemoryStream.dll</HintPath>
    </Reference>
    <Reference Include="NPOI.Core, Version=2.6.2.0, Culture=neutral, PublicKeyToken=0df73ec7942b34e1, processorArchitecture=MSIL">
      <HintPath>.\packages\NPOI.2.6.2\lib\net472\NPOI.Core.dll</HintPath>
    </Reference>
    <Reference Include="NPOI.OOXML, Version=2.6.2.0, Culture=neutral, PublicKeyToken=0df73ec7942b34e1, processorArchitecture=MSIL">
      <HintPath>.\packages\NPOI.2.6.2\lib\net472\NPOI.OOXML.dll</HintPath>
    </Reference>
    <Reference Include="NPOI.OpenXml4Net, Version=2.6.2.0, Culture=neutral, PublicKeyToken=0df73ec7942b34e1, processorArchitecture=MSIL">
      <HintPath>.\packages\NPOI.2.6.2\lib\net472\NPOI.OpenXml4Net.dll</HintPath>
    </Reference>
    <Reference Include="NPOI.OpenXmlFormats, Version=2.6.2.0, Culture=neutral, PublicKeyToken=0df73ec7942b34e1, processorArchitecture=MSIL">
      <HintPath>.\packages\NPOI.2.6.2\lib\net472\NPOI.OpenXmlFormats.dll</HintPath>
    </Reference>
    <Reference Include="SixLabors.Fonts, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d998eea7b14cab13, processorArchitecture=MSIL">
      <HintPath>.\packages\SixLabors.Fonts.1.0.0\lib\netstandard2.0\SixLabors.Fonts.dll</HintPath>
    </Reference>
    <Reference Include="SixLabors.ImageSharp, Version=2.0.0.0, Culture=neutral, PublicKeyToken=d998eea7b14cab13, processorArchitecture=MSIL">
      <HintPath>.\packages\SixLabors.ImageSharp.2.1.4\lib\net472\SixLabors.ImageSharp.dll</HintPath>
    </Reference>
    <Reference Include="System.ComponentModel.DataAnnotations" />
    <Reference Include="System.Configuration" />
    <Reference Include="System.Core" />
    <Reference Include="System.DirectoryServices" />
    <Reference Include="System.Memory, Version=4.0.1.2, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
      <HintPath>.\packages\System.Memory.4.5.5\lib\net461\System.Memory.dll</HintPath>
    </Reference>
    <Reference Include="System.Numerics" />
    <Reference Include="System.Numerics.Vectors, Version=4.1.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
      <HintPath>.\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
    </Reference>
    <Reference Include="System.Runtime.CompilerServices.Unsafe, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
      <HintPath>.\NPOISystem.Runtime.CompilerServices.Unsafe.5.0.0\lib\net45\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
    </Reference>
    <Reference Include="System.Runtime.Serialization" />
    <Reference Include="System.Security" />
    <Reference Include="System.Security.AccessControl, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
      <HintPath>.\packages\System.Security.AccessControl.6.0.0\lib\net461\System.Security.AccessControl.dll</HintPath>
    </Reference>
    <Reference Include="System.Security.Cryptography.Xml, Version=6.0.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
      <HintPath>.\packages\System.Security.Cryptography.Xml.6.0.1\lib\net461\System.Security.Cryptography.Xml.dll</HintPath>
    </Reference>
    <Reference Include="System.Security.Principal.Windows, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
      <HintPath>.\packages\System.Security.Principal.Windows.5.0.0\lib\net461\System.Security.Principal.Windows.dll</HintPath>
    </Reference>
    <Reference Include="System.Text.Encoding.CodePages, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
      <HintPath>.\packages\System.Text.Encoding.CodePages.5.0.0\lib\net461\System.Text.Encoding.CodePages.dll</HintPath>
    </Reference>
配置引用内容

    d. 导出Excel和读取Excel网上案列较多你再扩展。