一塊電路板上經常會出現各種電阻、二極管、跳線之類的元器件,許多生產加工電路板的自動化設備都會根據電路板上的元器件生成一個模擬圖。我今天就試著做一個簡單的,當然實際開發中遠遠不止文中提到的這些,還需要不斷的完善和改進,這個例子的目的主要是為了演示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);
}
}
閱讀更多 路馬編程 的文章