C#飞行仪表控件
前言
因为工作需要,要在c#下做飞行仪表显示,在网上找了很久,终于找到一个免费开源的飞行仪表控件,下载后发现原来的飞行仪表控件有一些缺陷,我按我的思路修改后,以开源的规则重新发布到网上。
原始代码来源 : https://blog.csdn.net/qq_42237381/article/details/85258478
原代码作者 : CSDN博主「渡之」
本控件主要代码来源于CSDN网站(见上), 由湖南创智艾泰克科技有限公司 王文庆做出完善和改造
遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
新的改变
原来的控件有一些缺陷,我做了以下修改:
- 原控件尺寸固定,这里改为了可缩放的;
- 原控件中地平仪的滚动角的三角指示是计算出来的,在角度改变时会变形,比较难看,这里改为直接画一个三角形,然后旋转的方式;
- 把常用功能【重合两张图片】、【旋转图片】、【截圆】做成了共通函数;
- 原代码注释太少,也不太清晰,给学习带来了一些不便,这里增加了注释;
- 优化了原代码的一些结构;
代码以及说明
本控件基于c#
首先准备背景图片
共通函数
先创建共通函数
/*************************************************************************
* 文件名称 :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函数,设置俯仰角、滚动角即可;
资源
来源:CSDN
作者:tiansuo_
链接:https://blog.csdn.net/tiansuo_/article/details/104065737