波形显示器
1. 界面展示![在这里插入图片描述](https://img-blog.csdnimg.cn/20200712103928644.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDU3NjQ4Ng==,size_16,color_FFFFFF,t_70)
- 单击显示波形按钮,会弹出画图界面。
- 同时在画图界面,添加了快捷键控制主界面和波形显示。
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;//定义显示主窗口委托访问权限为public
public 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;//定义显示主窗口委托访问权限为public
public 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就好