1.绘制图形相关参数设置
//绘图相关定义
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));//波形颜色
2.开启双缓冲
开启双缓冲的目的是防止闪烁,因为数据一致在更新的过程中,波形会一直闪烁
//Drawer窗口初始化|防止闪烁
public Drawer()
{
this.SetStyle(ControlStyles.DoubleBuffer | ControlStyles.UserPaint |
ControlStyles.AllPaintingInWmPaint,
true);//开启双缓冲 防止闪烁 【因为一直 更新数据 会闪烁】
this.UpdateStyles();
InitializeComponent();
}
3.绘制xy轴网格,绘制波形
//绘制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);
}
}
4.获取串口数据,并添加至链表中
//把数据加到链表中———在MainForm的串口数据接收函数调用
public void AddData(byte[] Data)
{
for (int i = 0; i < Data.Length; i++)
DataList.Add(Data[i]);//链表尾部添加数据
Invalidate();//刷新显示
}
5.实例化委托
//实例化委托对象
public ShowWindow ShowMainWindow; //定义显示主窗口委托访问权限为public
public HideWindow HideMainWindow; //定义隐藏主窗口委托
public OpenPort OpenSerialPort; //定义打开串口委托
public ClosePort CloseSerialPort; //定义关闭串口委托
public GetMainPos GetMainPos; //定义获取主窗口信息委托(自动对齐)
public GetMainWidth GetMainWidth; //定义获取主窗口宽度(自动对齐)
6.定义委托
//定义委托
public delegate void ShowWindow();
public delegate void HideWindow();
public delegate void OpenPort();
public delegate void ClosePort();
public delegate Point GetMainPos();
public delegate int GetMainWidth();
7.委托函数
//委托函数编写
//关闭串口,供委托调用
public void ClosePort()
{
try
{
serialPort1.Close();
}
catch (System.Exception)
{
}
}
//打开串口,供委托调用
public void OpenPort()
{
try
{
serialPort1.Open();
}
catch (System.Exception)
{
MessageBox.Show("串口打开失败,请检查", "错误");
}
}
//供委托调用
private Point GetMyPos()
{
return this.Location;
}
//供委托调用
public void ShowMe()
{
this.Show();
}
//供委托调用
public void HideMe()
{
this.Hide();
}
//供委托调用
int GetMyWidth()
{
return this.Width;
}
8.显示绘图窗口
//实例化Drawer窗口,使得在MainForm可操作Drawer
Drawer Displayer;
//创建&显示绘图窗口 | 初始化类成员委托
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();
}
9.设置主窗口大小
//主窗口设置自己 的 大小位——在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;
}
10.定义快捷键状态
//定义快捷键状态
private bool KeyShift, KeyShowMain, KeyHideMain, KeyExit, KeyOpen, KeyClose, KeyStepUp, KeyStepDown;
11.实现快捷键
//按下松开后才判断
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;
}
}
12.关闭绘图窗口
//关闭绘图窗口事件处理函数
private void Drawer_FormClosing(object sender, FormClosingEventArgs e)
{
ShowMainWindow();//关闭自己,显示主窗口
}
13.波形显示按钮
//波形显示按钮
private void button3_Click(object sender, EventArgs e)
{
if (Displayer == null)//第一次创建Displayer = null
{
CreateDisplayer();
}
else
{
//多次创建通过判断IsDisposed确定串口是否已关闭,避免多次创建
if (Displayer.IsDisposed)
{
CreateDisplayer();
}
}
}
14.创建绘图界面
//创建绘图界面 & 设置界面大小 与 主窗口 在 显示器屏幕 平行最大化显示
private void CreateDisplayer()
{
this.Left = 0;
CreateNewDrawer();
Rectangle Rect = Screen.GetWorkingArea(this);
Displayer.SetWindow(Rect.Width - this.Width, new Point(this.Width, this.Top));//设置绘制窗口宽度,以及坐标
}
15.打开串口
//打开串口
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("端口错误", "错误");
}
}
16.关闭串口
//关闭串口
private void button2_Click(object sender, EventArgs e)
{
try
{
serialPort1.Close(); //关闭串口
button1.Enabled = true;
button2.Enabled = false;
}
catch
{
}
}
17.发送数据
private void button4_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); //写入
}
}
catch
{
MessageBox.Show("数据转换错误,请输入数字。", "错误");
}
}
}
}
}
18.串口接收数据处理函数
//串口接收数据处理函数
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 { }
}
}
19.窗体设置
public Form1()
{
//串口接收编码--实现汉字显示
//serialPort1.Encoding = Encoding.GetEncoding("GB2312");
//不检查线程
System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
for (int i = 1; i < 20; i++)
{
comboBox1.Items.Add("COM" + i.ToString());//添加串口
}
comboBox1.Text = "COM1"; //默认选项
comboBox2.Text = "4800";
}