C#上位机设计项目实战——06波形显示器

波形显示器

1. 界面展示在这里插入图片描述

  1. 单击显示波形按钮,会弹出画图界面。
  2. 同时在画图界面,添加了快捷键控制主界面和波形显示。

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.代码

  1. 主窗口
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
            {

            }
        }
    }
}

  1. 绘图窗口
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就好
在这里插入图片描述

  • 10
    点赞
  • 111
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值