C#飞行仪表控件

↘锁芯ラ 提交于 2020-01-26 02:46:28

C#飞行仪表控件

前言

因为工作需要,要在c#下做飞行仪表显示,在网上找了很久,终于找到一个免费开源的飞行仪表控件,下载后发现原来的飞行仪表控件有一些缺陷,我按我的思路修改后,以开源的规则重新发布到网上。
原始代码来源 : https://blog.csdn.net/qq_42237381/article/details/85258478
原代码作者 : CSDN博主「渡之」
本控件主要代码来源于CSDN网站(见上), 由湖南创智艾泰克科技有限公司 王文庆做出完善和改造
遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。

新的改变

原来的控件有一些缺陷,我做了以下修改:

  1. 原控件尺寸固定,这里改为了可缩放的;
  2. 原控件中地平仪的滚动角的三角指示是计算出来的,在角度改变时会变形,比较难看,这里改为直接画一个三角形,然后旋转的方式;
  3. 把常用功能【重合两张图片】、【旋转图片】、【截圆】做成了共通函数;
  4. 原代码注释太少,也不太清晰,给学习带来了一些不便,这里增加了注释;
  5. 优化了原代码的一些结构;

代码以及说明

本控件基于c#
首先准备背景图片
磁罗盘背景图片(500X500)
地平仪背景图片

共通函数

先创建共通函数

/*************************************************************************
 * 文件名称 :CommFunClass.cs                          
 * 描述说明 :画图共通函数
 * 
 * 创建信息 : create by  on 2012-01-10
 * 修订信息 : modify by (person) on (date) for (reason)
 * 
 * 原始代码来源 : https://blog.csdn.net/qq_42237381/article/details/85258478
 * 原代码作者 : CSDN博主「渡之」
 * 本控件主要代码来源于CSDN网站(见上), 由湖南创智艾泰克科技有限公司 王文庆做出完善和改造
 * 遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
 **************************************************************************/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace FlyMeter
{
    /// <summary>
    /// 控件共通函数
    /// </summary>
    public class CommFunClass
    {
        /// <summary>
        /// 重合两张图片 
        /// </summary>
        /// <param name="btm1">图片1</param>
        /// <param name="btm2">图片2</param>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="w"></param>
        /// <param name="h"></param>
        /// <returns></returns>
        public static Bitmap Overlap(Bitmap btm1, Bitmap btm2, int x, int y, int w, int h)
        {
            Bitmap image = new Bitmap(btm1);
            Bitmap hi = new Bitmap(btm2);
            Graphics g = Graphics.FromImage(hi);
            g.DrawImage(image, new Rectangle(x, y, w, h));
            g.Dispose();
            return hi;
        }
        /// <summary>
        /// 旋转一张图片
        /// </summary>
        /// <param name="image">img图片</param>
        /// <param name="angle">旋转角</param>
        /// <param name="width">图片宽</param>
        /// <param name="height">图片高</param>
        /// <returns></returns>
        public static Bitmap RotateBmp(Image image, double angle, int width, int height)
        {
            Bitmap bitmp = new Bitmap(image);
            return RotateBmp(bitmp, angle, width, height);
        }
        /// <summary>
        /// 旋转一张图片
        /// </summary>
        /// <param name="image">bmp位图</param>
        /// <param name="angle">旋转角</param>
        /// <param name="width">图片宽</param>
        /// <param name="height">图片高</param>
        /// <returns></returns>
        public static Bitmap RotateBmp(Bitmap bitmp, double angle, int width, int height)
        {
            //创建一个新图片
            Bitmap pointImage = new Bitmap(width, height);
            System.Drawing.Graphics gPoint = System.Drawing.Graphics.FromImage(pointImage);
            //使用双线性插值
            gPoint.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Bilinear;
            //抗锯齿
            gPoint.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
            //计算偏移量
            Rectangle rectPoint = new Rectangle(0, 0, width, height);
            gPoint.TranslateTransform(width / 2, height / 2);
            //旋转图片
            gPoint.RotateTransform((float)angle);
            //恢复图像在水平和垂直方向的平移 
            gPoint.TranslateTransform(-width / 2, -height / 2);
            //在旋转后的容器中加载原始图片
            gPoint.DrawImage(bitmp, rectPoint);
            //重至绘图的所有变换  
            gPoint.ResetTransform();
            gPoint.Dispose();
            bitmp.Dispose();
            return pointImage;
        }
        /// <summary>
        /// 截圆
        /// </summary>
        /// <param name="width">图片宽</param>
        /// <param name="height">图片高</param>
        public static Region ComshellRegion(int width, int height)
        {
            //截圆
            System.Drawing.Drawing2D.GraphicsPath gp = new System.Drawing.Drawing2D.GraphicsPath();
            gp.FillMode = 0;
            gp.AddEllipse((float)0, (float)0, (float)width, (float)height);
            Region rRegion = new Region(gp);
            gp.Dispose();
            return rRegion;
        }
        /// <summary>
        /// 按比例截圆
        /// </summary>
        /// <param name="width">图片宽</param>
        /// <param name="height">图片高</param>
        /// <param name="ratio">截圆比例(=1-被截后的r/原始r)</param>
        /// <returns></returns>
        public static Region CompointRegion(int width, int height, double ratio)
        {
            //表盘截圆
            System.Drawing.Drawing2D.GraphicsPath gp = new System.Drawing.Drawing2D.GraphicsPath();
            gp.FillMode = 0;
            gp.AddEllipse((float)(width * ratio), (float)(width * ratio), (float)(width * (1 - ratio * 2)), (float)(height * (1 - ratio * 2)));
            Region rRegion = new Region(gp);
            gp.Dispose();
            return rRegion;
        }
    }
}

导入背景图片

把上面背景图片导入工程的资源里

创建地平仪控件

先创建一个自定义控件,然后在自定义控件上添加图片控件,命名为HoriBox,把Dock属性设置为Fill,把ErrorImage属性导入先前的地平仪背景图片。
设置控件resize事件,关联下面代码中HoriCt_Resize函数。
下面是地平仪控件的代码:

/*************************************************************************
 * 文件名称 :CompointControl.cs                          
 * 描述说明 :飞行仪表控件
 * 
 * 创建信息 : create by  on 2012-01-10
 * 修订信息 : modify by (person) on (date) for (reason)
 * 
 * 原始代码来源 : https://blog.csdn.net/qq_42237381/article/details/85258478
 * 原代码作者 : CSDN博主「渡之」
 * 本控件主要代码来源于CSDN网站(见上), 由湖南创智艾泰克科技有限公司 王文庆做出完善和改造
 * 遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
 **************************************************************************/

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace FlyMeter
{
    public partial class HoriCt : UserControl
    {
        private Image imgtmp, imgori;
        private Bitmap bitmp, bm;

        public HoriCt()
        {
            InitializeComponent();
        }
        /// <summary>
        /// 地平仪刻度划线函数
        /// </summary>
        /// <returns></returns>
        private Image Hori_Line()
        {
            bitmp = new Bitmap(this.Width, this.Width);//350
            System.Drawing.Graphics gscale = System.Drawing.Graphics.FromImage(bitmp);
            #region 准心绘线
            Pen p1 = new Pen(Color.Red, 3);
            Pen p2 = new Pen(Color.Green, 2);
            gscale.DrawLine(p2, (float)(this.Width * 29 / 70), (float)(this.Width / 2), (float)(this.Width * 41 / 70), (float)(this.Width / 2));
            gscale.DrawLine(p1, (float)(this.Width * 29 / 70), (float)(this.Width * 37 / 70), (float)(this.Width / 2), (float)(this.Width / 2));
            gscale.DrawLine(p1, (float)(this.Width * 41 / 70), (float)(this.Width * 37 / 70), (float)(this.Width / 2), (float)(this.Width / 2));
            #endregion
            #region 滚转刻度线
            //画圆
            gscale.DrawEllipse(Pens.White, (float)(this.Width * 0.1), (float)(this.Width * 0.1), (float)(this.Width * 0.8), (float)(this.Width * 0.8));
            #region 在圆上画刻度
            int i, i1, j, j1, k;
            for (k = 0; k < 73; k++)
            {
                i = Convert.ToInt32(this.Width * 0.4 * Math.Cos(k * Math.PI / 36) + this.Width / 2);
                j = Convert.ToInt32(this.Width * 0.4 * Math.Sin(k * Math.PI / 36) + this.Width / 2);
                if (k % 2 == 0)
                {
                    i1 = Convert.ToInt32(this.Width * 31 / 70 * Math.Cos(k * Math.PI / 36) + this.Width / 2);
                    j1 = Convert.ToInt32(this.Width * 31 / 70 * Math.Sin(k * Math.PI / 36) + this.Width / 2);
                }
                else
                {
                    i1 = Convert.ToInt32(this.Width * 30 / 70 * Math.Cos(k * Math.PI / 36) + this.Width / 2);
                    j1 = Convert.ToInt32(this.Width * 30 / 70 * Math.Sin(k * Math.PI / 36) + this.Width / 2);
                }
                gscale.DrawLine(Pens.White, i, j, i1, j1);
            }
            #endregion
            #endregion
            gscale.Dispose();
            return bitmp;
        }
        /// <summary>
        /// 地平仪显示函数
        /// </summary>
        /// <param name="pitch_angle">俯仰角 pitch_angle 范围-90~90 度 </param>
        /// <param name="row_angle">滚动角 row_angle   范围-90~90 度</param>
        public void Hori_Disp(double pitch_angle, double row_angle)
        {
            //1地平仪图像载入带平移
            //int pic_position = Convert.ToInt32(pitch_angle * 3.86);
            int pic_position = Convert.ToInt32(pitch_angle * this.Height / 90.67);
            try
            {
                //取得水平仪背景图--从ErrorImage中取得
                imgtmp = new Bitmap(HoriBox.ErrorImage);
                row_angle = row_angle % 360;
                ////弧度转换  
                //double ar = 2;
                //double radian = (row_angle - 90) * Math.PI / 180.0;
                //double radiana = (row_angle - 90 - ar) * Math.PI / 180.0;
                //double radianc = (row_angle - 90 + ar) * Math.PI / 180.0;
                //double cos = Math.Cos(radian);
                //double cosa = Math.Cos(radiana);
                //double cosc = Math.Cos(radianc);
                //double sin = Math.Sin(radian);
                //double sina = Math.Sin(radiana);
                //double sinc = Math.Sin(radianc);
                //目标位图
                Bitmap dsImage = new Bitmap(this.Width, this.Height);
                System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(dsImage);
                //双线性插值
                g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Bilinear;
                //抗锯齿
                g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
                //计算偏移量
                Rectangle rect = new Rectangle(-this.Width / 2, -this.Height / 2 + pic_position, this.Width * 2, this.Height * 2);
                //平移图形
                g.TranslateTransform(this.Width / 2, this.Height / 2);
                //旋转图形
                g.RotateTransform((float)row_angle);
                //恢复图像在水平和垂直方向的平移 
                g.TranslateTransform(-this.Width / 2, -this.Height / 2);
                g.DrawImage(imgtmp, rect);
                //重至绘图的所有变换  
                g.ResetTransform();
                g.Dispose();
                //保存旋转后的图片
                bm = dsImage;
                //调用imgori 已经是画好的刻度盘
                //把刻度盘图形从Img格式转换为Bmp格式
                Bitmap bitmp = new Bitmap(imgori);
                //重合背景图和刻度盘
                bm = CommFunClass.Overlap(bitmp, bm, 0, 0, this.Width, this.Height);
                #region 指针设计
                Bitmap pointImage = new Bitmap(this.Width, this.Height);
                System.Drawing.Graphics gPoint = System.Drawing.Graphics.FromImage(pointImage);
                //红色
                SolidBrush h = new SolidBrush(Color.Red);
                //Point a = new Point(Convert.ToInt32(this.Width / 2 + this.Width * 131 / 350 * cosa), Convert.ToInt32(this.Width * 180 / 350 + this.Width * 131 / 350 * sina));
                //Point b = new Point(Convert.ToInt32(this.Width * 141 / 350 * cos + this.Width / 2), Convert.ToInt32(this.Width * 141 / 350 * sin + this.Height / 2));
                //Point c = new Point(Convert.ToInt32(this.Width / 2 + this.Width * 131 / 350 * cosc), Convert.ToInt32(this.Width * 180 / 350 + this.Width * 131 / 350 * sinc));
                //Point[] pointer = { a, b, c };
                ////填充点所围的区域
                //gPoint.FillPolygon(h, pointer);
                Point a = new Point(Convert.ToInt32(this.Width / 2), Convert.ToInt32(this.Height * 0.05));
                Point b = new Point(a.X - Convert.ToInt32(this.Height * 0.05), a.Y - Convert.ToInt32(this.Height * 0.05));
                Point c = new Point(a.X + Convert.ToInt32(this.Height * 0.05), a.Y - Convert.ToInt32(this.Height * 0.05));
                Point[] pointer = { a, b, c };
                //填充点所围的区域
                gPoint.FillPolygon(h, pointer);
                Bitmap aaa = CommFunClass.RotateBmp(pointImage, row_angle, this.Width, this.Height);
                #endregion
                //重合【指针】与【背景图和刻度盘】
                bm = CommFunClass.Overlap(aaa, bm, 0, 0, this.Width, this.Height);
                HoriBox.Image = bm;
                g.Dispose();
                imgtmp.Dispose();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
            #region 截出控件大小的一个圆
            HoriBox.Region = CommFunClass.ComshellRegion(this.Width, this.Height);
            #endregion
        }
        /// <summary>
        /// 保持正方形
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void HoriCt_Resize(object sender, EventArgs e)
        {
            this.Height = this.Width;
            //画刻度盘
            imgori = Hori_Line();
            Hori_Disp(0, 0);
        }
    }
}

磁罗盘控件

磁罗盘控件稍微复杂一点。先创建一个自定义控件,然后放上一个图片控件,命名为ComshellBox,它显示磁罗盘的外圈,设置image属性,把前面资源中磁罗盘背景图片加载上去,Dock属性设置为Fill。再直接在ComshellBox上叠加一个图片控件命名为CompointBox,Dock属性也设置为Fill,把前面资源中磁罗盘背景图片加载上去,它显示磁罗盘的内圈(会做一个切割)。
设置控件resize事件,关联下面代码中CompointCt_Resize函数。
下面是代码

/*************************************************************************
 * 文件名称 :CompointControl.cs                          
 * 描述说明 :飞行仪表控件
 * 
 * 创建信息 : create by  on 2012-01-10
 * 修订信息 : modify by (person) on (date) for (reason)
 * 
 * 原始代码来源 : https://blog.csdn.net/qq_42237381/article/details/85258478
 * 原代码作者 : CSDN博主「渡之」
 * 本控件主要代码来源于CSDN网站(见上), 由湖南创智艾泰克科技有限公司 王文庆做出完善和改造
 * 遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
 **************************************************************************/

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace FlyMeter
{
    /// <summary>
    /// 磁罗盘控件
    /// </summary>
    public partial class CompointCt : UserControl
    {
        private double dir_angle;
        /// <summary>
        /// 航向角
        /// </summary>
        public double DirAngle
        {
            get
            {
                return dir_angle;
            }
            set
            {
                dir_angle = value;
                Compass_Disp(dir_angle);
            }
        }
        public CompointCt()
        {
            InitializeComponent();
        }

        //-------------------磁罗盘显示函数-------------------//
        /// <summary>
        /// 航向角设置
        /// 航向角 dir_angle 范围0~360 度
        /// </summary>
        /// <param name="dir_angle">航向角(0~360)</param>
        public void Compass_Disp(double dir_angle)
        {
            CompointBox.Image = CommFunClass.RotateBmp(CompointBox.ErrorImage, dir_angle, this.Width, this.Height);
        }
        /// <summary>
        /// 保持正方形
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CompointCt_Resize(object sender, EventArgs e)
        {
            this.Height = this.Width;
            ComshellBox.Region = CommFunClass.ComshellRegion(this.Width, Height);
            CompointBox.Region = CommFunClass.CompointRegion(this.Width, Height, 0.13);
        }
    }
}

使用控件

以把这个工程加入你需要使用的解决方案中,编译后,在VS的控件栏就会出现磁罗盘控件和地平仪控件。你就可以使用。
使用磁罗盘,就只要设置航向角属性DirAngle即可;
使用地平仪,只需要调用Hori_Disp函数,设置俯仰角、滚动角即可;

资源

代码工程文件下载

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!