波形显示器
1. 界面展示
- 单击显示波形按钮,会弹出画图界面。
- 同时在画图界面,添加了快捷键控制主界面和波形显示。
2.开发tips
tips1:
在右下角属性处,点击闪电标志,快速添加事件处理函数。
不用再傻乎乎的自己写定义。
tips2:添加一个新窗口操作流程
tips3:控制谁先显示【在Program.cs的Application.Run()设置】
namespace Drawer{static class Program{[STAThread] //线程控制static void Main(){Application.EnableVisualStyles();Application.SetCompatibleTextRenderingDefault(false);Application.Run(new MainForm()); //首先显示的窗口}}}
tips4:开启双缓冲防止闪烁
//**************************** Drawer窗口初始化 | 防止闪烁 ******************************************//public Drawer(){this.SetStyle(ControlStyles.DoubleBuffer | ControlStyles.UserPaint |ControlStyles.AllPaintingInWmPaint,true);//开启双缓冲 防止闪烁 【因为一直 更新数据 会闪烁】this.UpdateStyles();InitializeComponent();}
3.代码
- 主窗口
using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.Windows.Forms;namespace Drawer{//************************************* 定义委托 ************************************************//public delegate void ShowWindow();public delegate void HideWindow();public delegate void OpenPort();public delegate void ClosePort();public delegate Point GetMainPos();public delegate int GetMainWidth();public partial class MainForm : Form{Drawer Displayer; //实例化Drawer窗口,使得在 MainForm 可操作Drawer//***********************************创建&显示绘图窗口 | 初始化类成员委托**********************************//private void CreateNewDrawer()//创建Drawer窗口{Displayer = new Drawer();//创建新对象Displayer.ShowMainWindow = new ShowWindow(ShowMe);//初始化类成员委托Displayer.HideMainWindow = new HideWindow(HideMe);Displayer.GetMainPos = new GetMainPos(GetMyPos);Displayer.CloseSerialPort = new ClosePort(ClosePort);Displayer.OpenSerialPort = new OpenPort(OpenPort);Displayer.GetMainWidth = new GetMainWidth(GetMyWidth);Displayer.Show();//显示窗口}//************************************* MainForm 窗口初始化************************************************//public MainForm(){InitializeComponent();serialPort1.Encoding = Encoding.GetEncoding(\"GB2312\"); //串口接收编码--实现汉字显示System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false; //不检查线程}private void MainForm_Load(object sender, EventArgs e){for (int i = 1; i < 20; i++){comboBox1.Items.Add(\"COM\" + i.ToString()); //添加串口}comboBox1.Text = \"COM2\"; //默认选项comboBox2.Text = \"4800\";}//************************************* 委托 函数 编写************************************************//public void ClosePort()//关闭串口,供委托调用{try{serialPort1.Close();}catch (System.Exception){}}private Point GetMyPos()//供委托调用{return this.Location;}public void OpenPort()//打开串口,供委托调用{try{serialPort1.Open();}catch (System.Exception){MessageBox.Show(\"串口打开失败,请检查\", \"错误\");}}public void ShowMe()//供委托调用{this.Show();}public void HideMe()//供委托调用{this.Hide();}int GetMyWidth()//供委托调用{return this.Width;}//*************************************串口接受数据 处理函数************************************************//private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e){if (!radioButton3.Checked){textBox1.AppendText(serialPort1.ReadExisting()); //串口类会自动处理汉字,所以不需要特别转换}else{try{byte[] data = new byte[serialPort1.BytesToRead]; //定义缓冲区,因为串口事件触发时有可能收到不止一个字节serialPort1.Read(data, 0, data.Length);if (Displayer != null)Displayer.AddData(data); //添加到绘图窗口到数据链表foreach (byte Member in data) //遍历用法{string str = Convert.ToString(Member, 16).ToUpper();textBox1.AppendText(\"0x\" + (str.Length == 1 ? \"0\" + str : str) + \" \");}}catch { }}}//**************创建绘图界面 & 设置界面大小 与 主窗口 在 显示器屏幕 平行最大化显示******************//private void CreateDisplayer(){this.Left = 0;CreateNewDrawer();Rectangle Rect = Screen.GetWorkingArea(this);Displayer.SetWindow(Rect.Width - this.Width, new Point(this.Width, this.Top));//设置绘制窗口宽度,以及坐标}//*************************************波形显示按钮************************************************//private void button4_Click(object sender, EventArgs e){if (Displayer == null)//第一次创建Displayer = null{CreateDisplayer();}else{if (Displayer.IsDisposed)//多次创建通过判断IsDisposed确定串口是否已关闭,避免多次创建{CreateDisplayer();}}}private void button1_Click(object sender, EventArgs e){try{serialPort1.PortName = comboBox1.Text; //端口号serialPort1.BaudRate = Convert.ToInt32(comboBox2.Text); //波特率serialPort1.Open(); //打开串口button1.Enabled = false;button2.Enabled = true;}catch{MessageBox.Show(\"端口错误\", \"错误\");}}private void button3_Click(object sender, EventArgs e){byte[] Data = new byte[1]; //单字节发数据if (serialPort1.IsOpen){if (textBox2.Text != \"\"){if (!radioButton1.Checked){try{serialPort1.Write(textBox2.Text);//serialPort1.WriteLine(); //字符串写入}catch{MessageBox.Show(\"串口数据写入错误\", \"错误\");}}else //数据模式{try //如果此时用户输入字符串中含有非法字符(字母,汉字,符号等等,try,catch块可以捕捉并提示){for (int i = 0; i < (textBox2.Text.Length - textBox2.Text.Length % 2) / 2; i++)//转换偶数个{Data[0] = Convert.ToByte(textBox2.Text.Substring(i * 2, 2), 16); //转换serialPort1.Write(Data, 0, 1);}if (textBox2.Text.Length % 2 != 0){Data[0] = Convert.ToByte(textBox2.Text.Substring(textBox2.Text.Length - 1, 1), 16);//单独处理最后一个字符serialPort1.Write(Data, 0, 1); //写入}//Data = Convert.ToByte(textBox2.Text.Substring(textBox2.Text.Length - 1, 1), 16);// }}catch{MessageBox.Show(\"数据转换错误,请输入数字。\", \"错误\");}}}}}private void button2_Click(object sender, EventArgs e){try{serialPort1.Close(); //关闭串口button1.Enabled = true;button2.Enabled = false;}catch{}}}}
- 绘图窗口
using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.Windows.Forms;namespace Drawer{public partial class Drawer : Form{//****************************************绘图相关定义********************************************//private const int Unit_length = 32;//单位格大小private int DrawStep = 8;//默认绘制单位private const int Y_Max = 512;//Y轴最大数值private const int MaxStep = 33;//绘制单位最大值private const int MinStep = 1;//绘制单位最小值private const int StartPrint = 32;//点坐标偏移量private List<byte> DataList = new List<byte>();//数据结构----线性链表private Pen TablePen = new Pen(Color.FromArgb(0x00, 0x00, 0x00));//轴线颜色private Pen LinesPen = new Pen(Color.FromArgb(0xa0, 0x00, 0x00));//波形颜色//****************************************实例化 委托对象****************************************//public ShowWindow ShowMainWindow;//定义显示主窗口委托访问权限为publicpublic HideWindow HideMainWindow;//定义隐藏主窗口委托public OpenPort OpenSerialPort;//定义打开串口委托public ClosePort CloseSerialPort;//定义关闭串口委托public GetMainPos GetMainPos;//定义获取主窗口信息委托(自动对齐)public GetMainWidth GetMainWidth;//定义获取主窗口宽度(自动对齐)//****************************************定义快捷键 状态****************************************//private bool KeyShift, KeyShowMain, KeyHideMain, KeyExit, KeyOpen, KeyClose, KeyStepUp, KeyStepDown;//**************************** Drawer窗口初始化 | 防止闪烁 ******************************************//public Drawer(){this.SetStyle(ControlStyles.DoubleBuffer | ControlStyles.UserPaint |ControlStyles.AllPaintingInWmPaint,true);//开启双缓冲 防止闪烁 【因为一直 更新数据 会闪烁】this.UpdateStyles();InitializeComponent();}//************************* 把数据加到 链表中———在MainForm的串口数据接受函数 调用 **********************//public void AddData(byte[] Data){for (int i = 0; i < Data.Length;i++ )DataList.Add(Data[i]);//链表尾部添加数据Invalidate();//刷新显示}//************************* 绘制 xy轴网格 绘制坐 绘制波形 **********************//private void Form1_Paint(object sender, PaintEventArgs e)//画{String Str = \"\";System.Drawing.Drawing2D.GraphicsPath gp = new System.Drawing.Drawing2D.GraphicsPath();e.Graphics.FillRectangle(Brushes.White, e.Graphics.ClipBounds);//Draw Y 纵向轴绘制 y坐标绘制//for (int i = 0; i <= this.ClientRectangle.Width / Unit_length; i++){e.Graphics.DrawLine(TablePen, StartPrint + i * Unit_length, StartPrint, StartPrint + i * Unit_length, StartPrint + Y_Max);//画线gp.AddString((i * (Unit_length / DrawStep)).ToString(), this.Font.FontFamily, (int)FontStyle.Regular, 12, new RectangleF(StartPrint + i * Unit_length - 7,this.ClientRectangle.Height-StartPrint + 4, 400, 50), null);//添加文字}//Draw X 横向轴绘制 x坐标绘制for (int i = 0; i <= this.ClientRectangle.Height / Unit_length; i++){e.Graphics.DrawLine(TablePen, StartPrint, (i + 1) * Unit_length, this.ClientRectangle.Width, (i + 1) * Unit_length);//画线Str = ((16 - i) * 16).ToString(\"X\");Str = \"0x\" + (Str.Length == 1 ? Str + \"0\" : Str);if (i == 0)Str = \"0xFF\";if(i == 17)break;gp.AddString(Str, this.Font.FontFamily, (int)FontStyle.Regular, 14, new RectangleF(0, StartPrint + i * Unit_length - 8, 400, 50), null);//添加文字}e.Graphics.DrawPath(Pens.Black, gp);//写文字if (DataList.Count - 1 >= (this.ClientRectangle.Width - StartPrint) / DrawStep) //如果数据量大于可容纳的数据量,即删除最左数据 | 波形左移{DataList.RemoveRange(0, DataList.Count - (this.ClientRectangle.Width - StartPrint)/DrawStep - 1);}// 绘制波形 DataList.Count;for (int i = 0; i < DataList.Count - 1; i++)//绘制{e.Graphics.DrawLine(LinesPen, StartPrint + i * DrawStep, 17 * Unit_length - DataList[i] * 2, StartPrint + (i + 1) * DrawStep, 17 * Unit_length - DataList[i + 1] * 2);}}private void Form1_Load(object sender, EventArgs e){}//************************* 主窗口设置自己 的 大小位——在MainForm的 CreateDisplayer调用 **********************//public void SetWindow(int width, Point Pt)//允许主窗口设置自己{int height = this.ClientRectangle.Height;height = this.Height - height;int BorderWeigh = this.Width - this.ClientRectangle.Width;this.Size = new Size(width - (width - BorderWeigh) % Unit_length, height + Y_Max + StartPrint + Unit_length);this.Location = Pt;}// ****************************按下 松开后才判断***********************************************************//private void Form1_KeyUp(object sender, KeyEventArgs e)//按键弹开,执行对应动作{if (KeyShift){if (KeyShowMain)//显示主窗体快捷键{ShowMainWindow();Rectangle Rect = Screen.GetWorkingArea(this);SetWindow(Rect.Width - GetMainWidth(), new Point(GetMainWidth(), GetMainPos().Y));//缩小自己KeyShowMain = false;}else{if (KeyOpen){OpenSerialPort();//打开主窗口串口KeyOpen = false;}else{if (KeyClose){CloseSerialPort();//关闭主窗口串口KeyClose = false;}else{if (KeyExit){KeyExit = false;//退出自己Close();}else{if (KeyStepUp){if(DrawStep < MaxStep)//绘制单位递增DrawStep++;Invalidate();KeyStepUp = false;}else{if (KeyStepDown){if (DrawStep > MinStep)//绘制单位递减DrawStep--;Invalidate();KeyStepDown = false;}else{if (KeyHideMain){HideMainWindow();//隐藏主窗口并扩大自己Rectangle Rect = Screen.GetWorkingArea(this);SetWindow(Rect.Width, new Point(0, GetMainPos().Y));}}}}}}}}else//如果是其他按键,清空所有标志位{KeyClose = false;KeyOpen = false;KeyShowMain = false;KeyExit = false;KeyStepUp = false;KeyStepDown = false;}KeyShift = false;//清空shift标志位}// ****************************按下案件 对相应的标志 赋值*********************************************//private void Form1_KeyDown(object sender, KeyEventArgs e)//快捷键检测{if(e.Shift)//shift功能键按下KeyShift = true;//标志位置位switch (e.KeyCode)//功能标志置位{case Keys.S://显示主窗口KeyShowMain = true;break;case Keys.E://退出波形显示KeyExit = true;break;case Keys.O://打开串口KeyOpen = true;break;case Keys.C://关闭串口KeyClose = true;break;case Keys.Up://放大波形KeyStepUp = true;break;case Keys.H://隐藏主窗口KeyHideMain = true;break;case Keys.Down://缩小波形KeyStepDown = true;break;default:break;}}//*********************************关闭 绘图窗口事件处理函数**********************//private void Drawer_FormClosing(object sender, FormClosingEventArgs e){ShowMainWindow();//关闭自己,显示主窗口}}}
4.代码分析
①委托的特性
通过委托这一C#特性,可以实现子窗口对于主窗口的控制【快捷键】
step1:主窗口 定义委托 & 初始化类成员委托
namespace Drawer{//************************************* 定义委托 ************************************************//public delegate void ShowWindow();public delegate void HideWindow();public delegate void OpenPort();public delegate void ClosePort();public delegate Point GetMainPos();public delegate int GetMainWidth();public partial class MainForm : Form{Drawer Displayer; //实例化Drawer窗口,使得在 MainForm 可操作Drawer//***********************************创建&显示绘图窗口 | 初始化类成员委托**********************************//private void CreateNewDrawer()//创建Drawer窗口{Displayer = new Drawer();//创建新对象Displayer.ShowMainWindow = new ShowWindow(ShowMe);//初始化类成员委托Displayer.HideMainWindow = new HideWindow(HideMe);Displayer.GetMainPos = new GetMainPos(GetMyPos);Displayer.CloseSerialPort = new ClosePort(ClosePort);Displayer.OpenSerialPort = new OpenPort(OpenPort);Displayer.GetMainWidth = new GetMainWidth(GetMyWidth);Displayer.Show();//显示窗口}......}
step2:子窗口 实例化 委托对象
namespace Drawer{public partial class Drawer : Form{//****************************************实例化 委托对象****************************************//public ShowWindow ShowMainWindow;//定义显示主窗口委托访问权限为publicpublic HideWindow HideMainWindow;//定义隐藏主窗口委托public OpenPort OpenSerialPort;//定义打开串口委托public ClosePort CloseSerialPort;//定义关闭串口委托public GetMainPos GetMainPos;//定义获取主窗口信息委托(自动对齐)public GetMainWidth GetMainWidth;//定义获取主窗口宽度(自动对齐)...}
step3:子窗口想调用时就直接调用
②快捷键的设计
按下相应的按键时————把状态变量赋值为true
// ****************************按下按键 对相应的标志 赋值*********************************************//private void Form1_KeyDown(object sender, KeyEventArgs e)//快捷键检测{if(e.Shift)//shift功能键按下KeyShift = true;//标志位置位switch (e.KeyCode)//功能标志置位{case Keys.S://显示主窗口KeyShowMain = true;break;case Keys.E://退出波形显示KeyExit = true;break;case Keys.O://打开串口KeyOpen = true;break;case Keys.C://关闭串口KeyClose = true;break;case Keys.Up://放大波形KeyStepUp = true;break;case Keys.H://隐藏主窗口KeyHideMain = true;break;case Keys.Down://缩小波形KeyStepDown = true;break;default:break;}}
松开后按键抬起—————对状态变量判断 进行操作(操作完成清空状态)
// ****************************按下 松开后才判断***********************************************************//private void Form1_KeyUp(object sender, KeyEventArgs e)//按键弹开,执行对应动作{if (KeyShift){if (KeyShowMain)//显示主窗体快捷键{ShowMainWindow();Rectangle Rect = Screen.GetWorkingArea(this);SetWindow(Rect.Width - GetMainWidth(), new Point(GetMainWidth(), GetMainPos().Y));//缩小自己KeyShowMain = false;}else{if (KeyOpen){OpenSerialPort();//打开主窗口串口KeyOpen = false;}else{if (KeyClose){CloseSerialPort();//关闭主窗口串口KeyClose = false;}else{if (KeyExit){KeyExit = false;//退出自己Close();}else{if (KeyStepUp){if(DrawStep < MaxStep)//绘制单位递增DrawStep++;Invalidate();KeyStepUp = false;}else{if (KeyStepDown){if (DrawStep > MinStep)//绘制单位递减DrawStep--;Invalidate();KeyStepDown = false;}else{if (KeyHideMain){HideMainWindow();//隐藏主窗口并扩大自己Rectangle Rect = Screen.GetWorkingArea(this);SetWindow(Rect.Width, new Point(0, GetMainPos().Y));}}}}}}}}else//如果是其他按键,清空所有标志位{KeyClose = false;KeyOpen = false;KeyShowMain = false;KeyExit = false;KeyStepUp = false;KeyStepDown = false;}KeyShift = false;//清空shift标志位}
5.发布&分享给别人
然后在 Drawer\\bin\\Release 就可找到 .exe文件。分享给别人只有分享.exe就好