2024年1月6日发(作者:)

C#上位机实战开发指南第三章3.13.1.1第一个窗体程序Windows窗体程序新建本地工程文件夹为了使工程易于管理,我们首先在电脑本地新建一个文件夹用于存放整个共工程,比如命名为”DEMO”。注意文件夹名虽然根据喜好命名,但最好不要使用中文命名,因为有时候如果上位机需要加载本地文件遇到中文名必须要转码,比如加载本地URL时路径存在中文就相对麻烦,因此我建议使用英文命名。3.1.2新建工程启动VS2015,新建一个工程,如图3-1所示。图3-1:在VS2015中新建工程1.选择.NET版本通常我们可以选择.NET2.0或者.NET4.0,.NET3.x版本兼容性差,BUG较多,几乎没有人使用,.NET4.5以上版本太高,不再支持WindowsXP。一般情况下,WindowsXP已经很少使用.NET4.0,而.NET2.0可以流畅的在WindowsXP及其以上版本系统上运行,即使在企业级开发中4.0也已经算非常高的版本了,因此出于兼容性的考虑,建议选择.NET2.0进行开发。当然在讲到波形绘制项目时,我们会优先选择.NET4.0。15

C#上位机实战开发指南2.选择项目类别上位机开发选择Windows窗体应用程序。3.命名项目名建议使用英文名命名,避免路径中出现中文,这里我取名”Demo”。图3-2:创建项目我们将新建的工程放置于DEMO文件夹中即可。此时VS2015切换至窗体设计器界面。4.控件及属性栏介绍Windows窗体程序离不开控件的使用,在新建好工程后我们就需要找到Windows原生的控件库。在界面左边的工具箱中就存放着所有我们可能会用到的Windows原生控件。一般情况工具箱会自动隐藏,考虑到设计界面时我们需要比较大的空间去揣摩界面设计思路,因此建议使用时都将工具箱隐藏,用到时再选择控件即可。当然出于个人喜好又或者开发电脑有一个非常大的屏幕,我们也可以将工具栏显示在设计器界面中。属性栏一般在界面右下角,使用频率非常高,控件样式,事件的注册等都需要在属性一栏中进行设置。16

C#上位机实战开发指南右上角解决方案管理器是整个工程文件的结构脉络。设计器界面如图3-3所示。图3-3:设计器界面控件库界面如图3-4所示。图3-4:控件栏3.1.3项目代码区在解决方案管理器中选中文件右击出现查看代码选项,单击即可进入窗体代码区。同时资源管理器中还有文件,在第二章我们已经大概了解了17

C#上位机实战开发指南的代码结构。通常我们几乎不会去修改文件中的代码,所以我们暂时忽略。一般上位机代码的主体全部在文件中。我们在第二章分析命名空间时已经查看了相关代码。查看代码步骤以及窗体代码如图3-5,3-6所示。图3-5:查看步骤图:3-6:窗体代码3.1.4调试窗体程序VS2015的调试功能非常强大,我们经常用到的并不会太多,和单片机在线仿真类似,通常使用打断点单步调试,查看变量值,调用堆栈等功能。当然我们也可以实时查看CPU的使用率,每一句代码所运行的时间,这些可18

C#上位机实战开发指南以方便我们优化代码结构以及算法。图3-7:启动调试当按下启动调试后窗体程序也就弹出,此时即可打断点调试。最终的软件则在Debug文件夹中生成。图3-8:exe生成目录3.23.2.1Windows控件简述控件概述在学习emWin时我们已经接触过控件的概念,控件是用户可以操作的窗体内部对象。我非常喜欢将控件比喻成电子元器件,窗体比喻成PCB板框,那么此时程序代码自然也就等价于布线。我们完全可以将上位机的开发过程当作一次PCB板的设计过程。3.2.2添加控件我们可以通过三种方式添加控件至窗体,这三种方式分别是“在窗体绘制”,“拖动至窗体”,“程序添加”。这三种方式是等效的,我们最常使用第二种方19

C#上位机实战开发指南式,傻瓜式操作,直接在工具箱选中控件然后拖动到窗体内部即可。3.33.3.1常用控件的使用方法文本标签控件(Label)Label控件主要用于显示一些不能编辑的文本,文本的显示本质上是修改了Label的Text属性,例如我们需要上位机实时显示下位机传来的温度数据,那么只要将温度值转换为字符串格式赋值给Label的Text属性即可。下面详细介绍Label控件的基本使用方法。1.设置标签文本我们可以通过2种方式设置Label控件的显示文本:第一种是直接在属性面板中设置Text属性,第二种是通过代码设置Text属性。◇属性面板设置我们从工具箱中将Label控件拖至窗体中,选中Label控件后在属性面板中找到Text属性输入你要显示的文本,比如“C#上位机实战开发指南”。操作流程如图3-9所示。图3-9:属性面板设置Label属性◇代码设置通常固定不变的文本我们在属性面板中设置一次即可,但如果是实时动态刷新的数据则需要通过代码设置修改。代码清单如3-1所示。代码3-1:代码设置Label的Text属性="C#上位机实战开发指南";//代码设置Text属性20

C#上位机实战开发指南在第二章介绍类时我们已经了解到类就相当于结构体,通过代码清单3-1可以看出访问类的内部成员和结构体一样都是通过”.”来访问。注意C#对类内部成员的访问不可以使用”->”。2.修改Label文本字体大小以及颜色通常为了字体美观,我们会放弃默认字体显示格式,重新设置新的显示样式。同样颜色和字体也有2种方式修改:第一种在属性面板直接修改,第二种通过代码修改。一般颜色和字体样式决定后就不再变,因此我们大多数都是在属性面板中一次设置成功。设置方法同设置Text文本属性一致。选中Label后在属性面板中找到属性Font即可修改字体大小以及风格等。找到ForeColor即可修改Label文本的显示颜色。如图3-10所示。图3-10:文本样式设置3.3.2按钮控件(Button)按钮Button控件的用法非常简单,和Label一样文本样式都可以被修改,这里便不再对文本样式做过多介绍。我们使用过的上位机中按钮Button通常用作打开或者关闭串口,又或者清空缓存,清空计数等,这都由于Button可以触发Click事件。那么我们如何为Button绑定Click事件呢?通常我们将Button拖至窗体中布好局后双击Button就可以自动注册Click单击事件,同时VS2015自动跳至Click事件的函数体内。在上一节中我们已经学会了Label的使用方法,现在利用按钮Button来修改label1的显示文本。label1的默认文本为:“C#上位机实战开发指南”,由Button触发Click事件修改其文本为:“Button1_Click事件成功触发”。我们先将Button拖至窗体并为其注册Click事件(注册方式请看上文)。21

C#上位机实战开发指南最后我们在Click事件回调函数内书写代码修改label1的Text属性。完整代码请看代码清单3-2:Button单击Click事件。代码清单3-2:Button单击Click事件evoidbutton1_Click(objectsender,EventArgse){="Button1_Click事件成功触发";}注意此函数为注册后自动生成的,而不是手动输入,只有函数体内部修改文本语句为手动添加的。3.3.3文本框控件(TextBox)上位机通常都会有2个文本框,一个用作接收区,一个用作发送区。顾名思义他们的作用也就是用于显示文本的。不同于Label控件,TextBox允许运行中由用户修改即我们可以随时通过键盘增删文本内容。默认情况下,TextBox只单行显示,如果将属性Multiline设置为ture,那么此时TextBox便支持多行显示。TextBox的方法中最常用的是AppendText方法,它的作用是将新的文本数据从末尾处追加至TextBox中。当TextBox一直追加文本后就会带来本身长度不够无法显示全部文本的问题,此时我们需要使能TextBox的纵向滚动条来跟踪显示最新文本,所以我们将属性ScrollBars的值设置为:Vertical即可。紧接着上一个案例,我们再为窗体添加一个TextBox控件,设置Multiline属性为true,并将滚动条设置为纵向滚动。同时在button1的单击事件中往TextBox追加文本:“C#上位机实战开发指南rn”。详细代码见代码清单3-3。代码清单3-3:TextBox追加文本案例evoidbutton1_Click(objectsender,EventArgse){="Button1_Click事件成功触发";Text("C#上位机实战开发指南rn");}运行效果如图3-11所示。22

C#上位机实战开发指南图3-11:TextBox追加文本3.3.4下拉组合框控件(ComboBox)下拉组合框控件通常用于选择串口号,波特率等功能,它由2部分组成,第一部分是允许用户输入修改的文本框,第二部分是列表框,它提供给用户有限的选择项。通常ComboBox也有两种使用模式,一种是DropDown,这种模式下用户既可以在下拉列表中选择所需要的选择项,又可以在列表没有所需选择项的情况下手动输入,例如某上位机的波特率选择列表框只有“9600”一项,此时只要ComboBox工作在DropDown模式下就可以输入任意你想要的波特率。那么相反另一种DropDownList模式则受到限制,无法手动输入新的选择项。设置ComboBox的属性DropDownStyle便可以切换ComboBox的下拉模式。那么如何为ComboBox添加下拉选项呢?C#为我们提供了两个方法,第一种是单个添加的方法,第二种是批量添加的方法,下面详细介绍这两种方法。◇单个添加单个添加方法操作简单,直接传入字符串即可,同样我们紧接着之前的案例使用按钮button1的Click事件进行添加单个下拉选项,代码清单如下。代码清单3-4:ComboBox单个添加下拉选项evoidbutton1_Click(objectsender,EventArgse){("下拉选项1");("下拉选项2");}23

C#上位机实战开发指南◇批量添加批量添加时首先要定义好一个字符串数组,然后将数组名传入即可完成批量添加。通常上位机在启动时会获取当前电脑中所有的串口,然后将串口号缓存至定义的字符串数组中,最后批量传入ComboBox。使用方法见代码清单3-5。代码清单3-5:ComboBox批量添加evoidbutton1_Click(objectsender,EventArgse){//单个添加("下拉选项1");("下拉选项2");//批量添加string[]Com=newstring[3];Com[0]="下拉选项3";Com[1]="下拉选项4";Com[2]="下拉选项5";ge(Com);}//批量导入//C#数组定义方式在代码清单中我们看到一个全新的字符串数组定义方式,其中new就是实例化,也就是实际要分配内存的,当然new的使用方法还有很多,但上位机使用不多,因此这里便不再做过多讲解,请读者自行百度。效果图如下图所示。图3-12:ComboBox下拉选择项添加24

C#上位机实战开发指南3.3.5复选框控件(CheckBox)复选框控件一般用于使能或失能某项功能,比如上位机是否开启时间戳显示功能。通常我们会为CheckBox注册一个Click或者Mouse_UP事件,注册方式我们采用在属性面板注册的方式,这里为CheckBox注册一个Click事件。注册步骤如下图所示。图3-13:CheckBox事件注册选中文本框区域后鼠标双击即可注册好Click事件,同时VS2015自动跳转至事件回调函数内部。事件注册好后如下图所示。图3-14:Click事件成功注册单击事件注册好之后我们就可以在CheckBox1_Click事件回调函数体内对CheckBox1的选中状态进行读取判断即判断CheckBox的Checked属性,如果复选框被选中则Checked的值为true,那么相反复选框未被选中则Checked的值为false。注意默认情况下Checked的值为false,因此当我们将复选框拖进窗体时默认未被选中。C复选框CheckBox的Checked属性设置方式如图3-15所示。25

C#上位机实战开发指南图3-15:Checked属性紧接着上一个案例,我们将CheckBox拖入窗体中并为其注册单击Click事件,然后对选中状态进行判断并弹窗提示。详细代码见代码清单3-6。代码清单3-6:CheckBox使用方式evoidcheckBox1_Click(objectsender,EventArgse){//如果CheckBox被选中if(d){("选中");//消息提示框,用于调试//也可标志位置位}if(!d){("未选中");//消息提示框,用于调试}}在代码清单中,我们可以看到MessageBox的使用,MessageBox主要用于打印一些调试信息或者一些异常信息,我们可以将MessageBox理解为串口打印调试。调用Show方法传入要显示的字符串即可。当然MessageBox有很多的重载参数,我们只要传入一个调试信息就足够了。代码运行效果如图3-16所示。26

C#上位机实战开发指南图3-16:CheckBox被选中3.3.6单选按钮控件(RadioButton)RadioButton单选按钮控件使用方式和CheckBox类似,但CheckBox允许多个进行复选,每个控件之间不存在互斥关系。而RadioButton则不允许多选,当用户选中其中某一个后,其它几个的选中状态便全部为false。比如上位机串口接收方式,16进制和字符串便不能同时存在,同一时间只能存在一种接收方式,这种情况下就不适合使用CheckBox,只有RadioButton具有互斥关系,因此我们通常使用RadioButton。RadioButton控件如图3-16所示。图3-16:RadioButton注意RadioButton并不是所有的都会互斥,只有在同一容器下才会互斥。因此RadioButton在使用时都要进行分组分容器。一般情况下我们会使用Panel容器控件进行分组。27

C#上位机实战开发指南3.3.7容器控件(Panel)容器控件主要用于为其它控件提供一个可识别的分组,比如上一节中我们讲到的RadioButton单选按钮控件。Panel就好比商场中的楼层,1楼卖化妆品,2楼卖电子产品一样。在将Panel拖进窗体中时选中区域外会有一个虚线框包围,但在窗体运行后虚线框消失。Panel控件如图3-17所示。图3-17:Panel控件如上图所示虚线框即为Panel,每个Panel都包围了4个RadioButton控件,因此在这种情况下只有Panel内部的单选按钮互斥。3.3.8定时器控件(Timer)严格来说Timer是一个组件并不能算作是控件,因为控件是可视的,能在窗体中看到这个控件的样式,而组件不行,组件不需要绘制,不能在窗体中显示出来。因此我们可以将组件理解为一个抽象的控件。添加Timer控件的方式和普通控件一致,只是添加后控件出现在设计器下方,如图3-18所示。图3-18:Timer定时器控件既然Timer是定时器,那就有一个定时的时长,通过设置Timer的Interval属性就可以设置定时的时长。注意Interval的单位是毫秒(ms),默认为100毫28

C#上位机实战开发指南秒触发一次Tick事件。启动定时器使用Start方法,停止定时器使用Stop方法。定时器使用方式基本和单片机的定时器类似。我们使用一个button作为定时器timer1的开关,并通过属性面板为该butoon的Text属性设置为“开始”,当启动定时器后将Text属性改为“停止”,并在Tick事件中定时改变label2的文本来验证Timer的使用方式。我们在属性面板中设置定时周期为1秒,并为其注册Tick事件,并且同时为button2注册Click事件。代码见清单3-7。代码清单3-7:定时器的使用=0;{Cnt++;=ng();//ToString是将实际数值变为字符串形式的方法//等价于单片机中加上:0x30}privatevoidbutton2_Click(objectsender,EventArgse){if(=="开始"){();//开启定时器="停止";}else{if(=="停止"){();//停止定时器="开始";}}}//全局变量,用于计时显示privatevoidtimer1_Tick(objectsender,EventArgse)代码中ToString方法用于将数字变为字符串,比如上位机获取到传感器数据如果要显示在Label标签上则必须要使用ToString方法。除非协议中传感器的数据已经是字符串格式,当然一般数传协议很少会使用这种方式。ToString方法还可以格式化输出字符串只需要在括号中填入参数。整段代码逻辑非常清晰,易于理解。Timer在上位机中可以用于定时发送功能的实现,也可以用作超时处理机制的实现。button2_Click事件内部用于开关定时器,通过按钮的文本来选择何时打开定时器,何时关闭定时器。代码运行效果如图3-19所示。29

C#上位机实战开发指南图3-19:Timer运行效果图中Label2在Tick事件下每秒自增1。3.3.9串口控件(SerialPort)串口控件和定时器一样,不需要绘制,因此无法将其添加至窗体中,只能和定时器一样显示在设计器下方,如图3-20所示。图3-20:添加串口控件串口常用的属性有2个,一个是端口号(PortName),一个是波特率(BaudRate),其它停止位,校验位等默认即可。串口打开与关闭都有接口可以直接调用。串口同时还有一个IsOpen属性,IsOpen为true则表示串口已经打开,IsOpen为false则表示串口已经关闭。串口控件的使用是整个上位机开发的基础,因此在接下来的几个小结我将详细介绍串口控件的使用方法。下面我从两个方面开始介绍。1.串口发送串口发送方法也有2种,一种是字符串发送WriteLine,一种是16进制发送,30

C#上位机实战开发指南Write。其中字符串发送WriteLine默认已经在末尾添加换行符。串口开关发送示例见代码清单3-8。代码清单3-8:串口开关发送[]SendByte=newbyte[5];//数组定义SendByte[0]=1;SendByte[1]=2;SendByte[2]=3;SendByte[3]=4;SendByte[4]=5;();//打开串口();//关闭串口(SendByte,0,5);//发送16进制ine("字符串发送");//字符串发送其中字符串发送直接传入要发送的字符串即可,最终发出去的自带换行符。16进制发送一共有3个参数,第一个参数是要发送的缓存也就是数组名,第二个是偏移量即从缓存中第几个字节开始发送,第三个是发送的字节数。2.串口接收使用串口接收之前先要为串口控件注册一个Receive事件,作用等价于串口接收中断。然后在中断内部对缓冲区的数据进行读取。串口接收与发送相同都有2种方法,一个是16进制方式读,一个是字符串方式读。首先我们先注册串口接收事件,如图3-21所示。图3-21:串口接收事件注册在注册好后接收事件之后,我们即可在事件中读取缓冲区中的数据,见代码清单3-9。代码清单3-9:串口数据读取1.2.3.4.5.6.//串口接收事件,相当于单片机串口接收中断privatevoidserialPort1_DataReceived(objectsender,DataReceivedEventArgse){//字符串方式读stringstr=isting();//字符串方式读//16进制读31

C#上位机实战开发指南=oRead;byte[]Buf=newbyte[n];(Buf,0,n);}//获取缓冲区中的字节数//定义一个以有效字节数为大小的缓存//读取至缓存其中定义有效字节数为大小的缓存相当于动态分配,非常方便。Read方法有三个参数,第一个是缓存数组,第二个是偏移量,第三个是写入字节数。通常从缓存头开始一直写满为止。3.3.10Windows常用控件使用总结Windows原生控件的使用非常简单,上位机开发所涉及到的控件并不多,所谓熟能生巧,在多次使用之后便会非常熟悉。关于其它未曾讲解的控件,读者可以自行百度学习,或者去MSDN官网学习。在对某个控件或者对某个方法的使用不熟悉时可在代码区选中它,按下“F1”,浏览器将自动跳转至微软官方的学习介绍网页。注意在使用Button等可以添加Click事件的控件时,一般我们都使用MouseUp鼠标弹起事件来代替Click事件,因为Click事件的触发条件比较特殊,而鼠标弹起则每次都会触发。32