一块电路板上经常会出现各种电阻、二极管、跳线之类的元器件,许多生产加工电路板的自动化设备都会根据电路板上的元器件生成一个模拟图。我今天就试着做一个简单的,当然实际开发中远远不止文中提到的这些,还需要不断的完善和改进,这个例子的目的主要是为了演示GDI的使用。如有不足或者错误的地方欢迎大家指正。
首先我们看一下日常生活中我们所见到的电路板的样子:
然后再看一下我们程序运行时候的样子,当然我的程序模拟的并不是上图电路板。
这个例子主要的思路是这样子:有一个文本文件保存了一块电路板上电子元件的XY坐标、宽度、方向、以及元件的名称。其实这个文本文件就是一种自动化设备生产用的数据。如果没有这个数据文件,我们完全可以自己定义一个集合保存一些坐标、方向就可以了。
具体步骤如下:
- 新建一个winform应用程序,在设计器中添加一个pictureBox控件,在load事件中设置picturebox控件的属性dock为DockStyle.Fill
- 定义一个用来保存电路板数据的一个类
- 编写一个方法从文本文件中获取所需要的数据
- 将公制毫米为单位的坐标数据转换成屏幕像素
- 得到数据中最大的坐标然后计算出与pictureBox控件的宽高的比例
- 重写OnPaint()事件根据获取到的数据在pictureBox控件中绘制图形
设置picturebox控件的属性dock
pictureBox1.Dock = DockStyle.Fill;
定义一个DataContent类
List<datacontent> GetPointFromFile()方法从文本文件中获取所需要的数据/<datacontent>
int MillimetersToPixelsWidth(int length)公制毫米单位转换成屏幕像素
得到数据中最大的坐标然后计算出与pictureBox控件的宽高的比例
在pictureBox控件中绘制图形
在绘制图形的时候,其实人们需要考虑的因素有许多,比如实际的坐标Y轴与屏幕Y轴的坐标相反等
最后我把完整代码贴出来:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
namespace WindowsFormsApplication3
{
public class DataContent
{
public Point InsCoordinate { get; set; }
public int Span { get; set; }
public int InsAngle { get; set; }
public string Comment { get; set; }
public DataContent(Point point, int span, int angle,string comment)
{
this.InsCoordinate = point;//坐标
this.Span = span;//跨度
this.InsAngle = angle;//角度
this.Comment = comment;//元件名称
}
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
pictureBox1.Dock = DockStyle.Fill;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
try
{
Bitmap bitmap = new Bitmap(pictureBox1.ClientRectangle.Width, pictureBox1.ClientRectangle.Height);
List<datacontent> list = GetPointFromFile();/<datacontent>
Graphics dc = Graphics.FromImage(bitmap);// Create dc
pictureBox1.Image = bitmap;
Point maxPoint = GetMaxCoordinate(list);//获取程序中最大的XY坐标
double xRate = ((double)maxPoint.X) / ((double)pictureBox1.ClientRectangle.Width - 40);
double yRate = ((double)maxPoint.Y) / ((double)pictureBox1.ClientRectangle.Height - 60);
dc.FillRectangle(new SolidBrush(Color.Green), pictureBox1.ClientRectangle);//绘制一个绿色背景的矩形
for (int i = 0; i < list.Count; i++)//绘制多个矩形
{
double x = ((double)list[i].InsCoordinate.X) / xRate;//x坐标 = 实际坐标 除以 坐标与屏幕pixel的比例
double y = ((double)list[i].InsCoordinate.Y) / yRate;
//Y轴坐标+20 pixel 如果实际坐标0,0 反转Y轴之后就会是Y轴最大的坐标,矩形会绘制在Y轴最大坐标上无法显示
//Point point = new Point((int)x,pictureBox1.ClientRectangle.Height - ((int)y + 20));//反转Y轴坐标
Point point = new Point((int)Math.Round(x,MidpointRounding.AwayFromZero),
pictureBox1.ClientRectangle.Height - ((int)Math.Round(y,MidpointRounding.AwayFromZero) + 20));//反转Y轴坐标
Rectangle rect = new Rectangle(point, new Size(0, 0));
Rectangle rectBlack = new Rectangle(point, new Size(0, 0));
double height = 6 / yRate;//元件的高度
int rectHeight = (int)Math.Round(height,MidpointRounding.AwayFromZero);//四舍五入将小数转换成整数
Color[] colors = { Color.Black, Color.White ,Color.Gray,Color.Gold,Color.Orange,Color.Silver,Color.Green,
Color.Blue,Color.Brown,Color.Yellow,Color.Violet};//电阻色环
double dSpan = list[i].Span / xRate;//元件的大小适应屏幕显示
int rectWidth = (int)Math.Round(dSpan,MidpointRounding.AwayFromZero);//元件矩形的宽度
rectWidth = rectWidth - (rectWidth / 2 / 2);//减去两边引脚的长度
//计算出元件两端的引脚长度
int leadLength = rectWidth / 2 / 2;
if (list[i].InsAngle == 0 || list[i].InsAngle == 180)// 0 180度元件模拟图绘制
{
#region
//绘制元件白色部分的本体
rect = new Rectangle(point, new Size(rectWidth, rectHeight));
if (list[i].Comment.Contains("D") || list[i].Comment.Contains("Z"))//判断是否为二极管
{
dc.FillRectangle(new SolidBrush(Color.White), rect);//绘制元件白色区域矩形
//绘制元件黑色部分的本体
if (list[i].InsAngle == 0)
{
rectBlack = new Rectangle(new Point(point.X + rectWidth / 4, point.Y), new Size(rectWidth - rectWidth / 4, rectHeight));
}
else//180度
{
rectBlack = new Rectangle(new Point(point.X , point.Y), new Size(rectWidth - rectWidth / 4, rectHeight));
}
DrawLead(list[i].InsAngle, dc, point, rectHeight, rectWidth, leadLength);
dc.FillRectangle(new SolidBrush(Color.Black), rectBlack);//绘制黑色元件区域
}
else if (list[i].Comment.Contains("J"))//如果是跳线
{
dc.DrawLine(new Pen(Color.White, 3), point, new Point(point.X + rectWidth, point.Y));
}
else//认为是电阻
{
dc.FillRectangle(new SolidBrush(Color.Pink), rect);//用粉红色绘制电阻元件本体
Random rnd = new Random();
for (int j = 1; j < 6; j++)
{
//绘制电阻色环
dc.DrawLine(new Pen(Color.FromArgb(rnd.Next(0, 256), rnd.Next(0, 256), rnd.Next(0, 256)), 2),
new Point(point.X + rectWidth / 6 * j, point.Y),
new Point(point.X + rectWidth / 6 * j, point.Y + rectHeight));
}
DrawLead(list[i].InsAngle, dc, point, rectHeight, rectWidth, leadLength);
}
#endregion
}
else
{
#region
rect = new Rectangle(point, new Size(rectHeight, rectWidth));
if (list[i].Comment.Contains("D") || list[i].Comment.Contains("Z"))//判断是否为二极管
{
dc.FillRectangle(new SolidBrush(Color.White), rect);//绘制元件白色区域矩形
//绘制元件黑色部分的本体
if (list[i].InsAngle == 90)
{
rectBlack = new Rectangle(new Point(point.X, point.Y + rectWidth / 4),
new Size(rectHeight, rectWidth - rectWidth / 4));
}
else
{
rectBlack = new Rectangle(new Point(point.X, point.Y),
new Size(rectHeight, rectWidth - rectWidth / 4));
}
DrawLead(list[i].InsAngle, dc, point, rectHeight, rectWidth, leadLength);
dc.FillRectangle(new SolidBrush(Color.Black), rectBlack);
}
else if (list[i].Comment.Contains("J"))
{
dc.DrawLine(new Pen(Color.White, 3), point, new Point(point.X, point.Y + rectWidth));
}
else//认为是电阻
{
dc.FillRectangle(new SolidBrush(Color.Gray), rect);//绘制元件白色区域矩形
Random rnd = new Random();
for (int j = 1; j < 6; j++)
{
dc.DrawLine(new Pen(colors[rnd.Next(0,colors.Length)], 2), new Point(point.X, point.Y + rectWidth / 6 * j),
new Point(point.X + rectHeight, point.Y + rectWidth / 6 * j));
}
DrawLead(list[i].InsAngle, dc, point, rectHeight, rectWidth, leadLength);
}
#endregion
}
//dc.DrawString((i + 1).ToString(), new Font("宋体", 8, FontStyle.Regular),
//new SolidBrush(Color.Red), new Point(rect.Left, rect.Y));
//if(i>1)//绘制线条
//{
// double x2 = ((double)list[i - 1].InsCoordinate.X) / xRate;
// double y2 = ((double)list[i - 1].InsCoordinate.Y) / yRate;
// Point point2 = new Point((int)x2, pictureBox1.ClientRectangle.Height - ((int)y2 + 20));
// dc.DrawLine(new Pen(Color.White), point, point2);//连接前后两个点
//}
}
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private static void DrawLead(int angle,Graphics dc, Point point, int rectHeight, int rectWidth, int leadLength)
{
if(angle == 0||angle==180)
{
//绘制元件左边的引脚
dc.DrawLine(new Pen(Color.Silver, 3), new Point(point.X - leadLength, point.Y + rectHeight / 2),
new Point(point.X, point.Y + rectHeight / 2));
//绘制元件右边的引脚
dc.DrawLine(new Pen(Color.Silver, 3), new Point(point.X + rectWidth, point.Y + rectHeight / 2),
new Point(point.X + rectWidth + leadLength, point.Y + rectHeight / 2));
}
else
{
//绘制元件左边的引脚
dc.DrawLine(new Pen(Color.Silver, 3), new Point(point.X + rectHeight / 2, point.Y - leadLength),
new Point(point.X + rectHeight / 2, point.Y));
//绘制元件右边的引脚
dc.DrawLine(new Pen(Color.Silver, 3), new Point(point.X + rectHeight / 2, point.Y + rectWidth),
new Point(point.X + rectHeight / 2, point.Y + rectWidth + leadLength));
}
}
/// <summary>
/// 获取程序中最大的坐标
///
/// <param>
/// <returns>
private Point GetMaxCoordinate(List<datacontent> list)/<datacontent>
{
Point point = new Point(0, 0);
for (int i = 0; i < list.Count; i++)
{
if (list[i].InsCoordinate.X > point.X) point.X = list[i].InsCoordinate.X;
if (list[i].InsCoordinate.Y > point.Y) point.Y = list[i].InsCoordinate.Y;
}
return point;
}
private List<datacontent> GetPointFromFile()/<datacontent>
{
List<datacontent> list = new List<datacontent>();/<datacontent>/<datacontent>
List<point> offsetList = new List<point>();//保存拼板程序偏移量/<point>/<point>
try
{
string[] points = File.ReadAllLines(@"e:\\1.ncd", Encoding.Default);
bool bIs = false;
for (int i = 0; i < points.Length; i++)
{
if (points[i] != "*")
{
string x = points[i].Substring(points[i].IndexOf("X") + 1, 7);
string y = points[i].Substring(points[i].IndexOf("Y") + 1, 7);
string span = points[i].Substring(points[i].IndexOf("W+") + 2, 5);
span = span.Substring(0, 3);
string angle = points[i].Substring(points[i].IndexOf('T') + 2, 2);
string comment = points[i].Substring(86, 8).Trim();//Comment
if (angle == "01")
{
angle = "0";
}
else if (angle == "02")
{
angle = "90";
}
else if (angle == "11")
{
angle = "180";
}
else if (angle == "12")
{
angle = "270";
}
x = x.Substring(0, 7);
y = y.Substring(0, 7);
x = x.Insert(5, ".");
y = y.Insert(5, ".");
double dx = Convert.ToDouble(x);
double dy = Convert.ToDouble(y);
dx = Math.Round(dx,MidpointRounding.AwayFromZero);
dy = Math.Round(dy,MidpointRounding.AwayFromZero);
Point point = new Point(MillimetersToPixelsWidth((int)dx * 2),MillimetersToPixelsWidth((int)dy * 2));
if (point.X < 0 || point.Y < 0)
{
bIs = true;
}
string value = points[i].Substring(10, 3);//取PR值
if (value == "902" || value == "912" || value == "922" || value == "932")
{
offsetList.Add(point);
}
else
{
string s = points[i].Substring(points[i].IndexOf("T") + 1, 3);//去掉T004这一空白行
if (!(Convert.ToInt32(s) == 4 || Convert.ToInt32(s) == 0))
{
list.Add(new DataContent(point, MillimetersToPixelsWidth(Convert.ToInt32(span)) * 2,
Convert.ToInt32(angle), comment));
}
}
}
}
if (bIs)//如果坐标中有负数
{
Point minPoint = new Point(0, 0);
for (int i = 0; i < list.Count; i++)
{
if (list[i].InsCoordinate.X < minPoint.X) minPoint.X = list[i].InsCoordinate.X;
if (list[i].InsCoordinate.Y < minPoint.Y) minPoint.Y = list[i].InsCoordinate.Y;
}
minPoint = new Point(Math.Abs(minPoint.X) + 15, Math.Abs(minPoint.Y) + 15);//绝对值加15个pixel 防止从0,0坐标画图
for (int i = 0; i < list.Count; i++)//把负数坐标变成正数
{
list[i].InsCoordinate = new Point(list[i].InsCoordinate.X + minPoint.X,
list[i].InsCoordinate.Y + minPoint.Y);
}
}
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
return list;
}
/// <summary>
/// 将公制毫米转换成屏幕像素
///
/// <param>
/// <returns>
private static int MillimetersToPixelsWidth(int length)
{
System.Windows.Forms.Panel p = new Panel();
Graphics dc = Graphics.FromHwnd(p.Handle);
IntPtr hdc = dc.GetHdc();
int width = GetDeviceCaps(hdc, 4);
int pixels = GetDeviceCaps(hdc, 8);
dc.ReleaseHdc(hdc);
return (((int)pixels / (int)width) * (int)length); //将公制毫米转换成屏幕像素点
}
[DllImport("gdi32.dll")]
private static extern int GetDeviceCaps(IntPtr hdc, int Index);
}
}
閱讀更多 路馬編程 的文章