winform绘图技术
- 格式:pdf
- 大小:140.37 KB
- 文档页数:9
WinForm绘制柱形图⼀、绘制简单的柱形图private void button1_Click(object sender, EventArgs e){//创建画布Bitmap bitM = new Bitmap(this.panel1.Width, this.panel1.Height);Graphics g = Graphics.FromImage(bitM);g.Clear(Color.White);//清除背景⾊并填充为⽩⾊//x y轴坐标以及w h绘制矩形的宽⾼int x, y, w, h;Random ran = new Random();for (int i = 0; i < 4; i++){//绘制名字g.DrawString("名字"+(i+1),new Font("宋体",8,FontStyle.Regular),new SolidBrush(Color.Black),76+40*i,this.panel1.Height-16); x = 78 + 40 * i;int num=ran.Next(40, 400);y = this.panel1.Height - 20-(num*20/100);w = 24;h = num * 20 / 100;g.FillRectangle(new SolidBrush(Color.FromArgb(60, 120, 80)), x, y, w, h);}this.panel1.BackgroundImage = bitM;}⼆、绘制带有辅助线的柱形图private void button1_Click(object sender, EventArgs e){int panelHeight=this.panel1.Height;int panelWidth=this.panel1.Width;//创建新的画布Bitmap bitM = new Bitmap(panelWidth,panelHeight);Graphics g = Graphics.FromImage(bitM);g.Clear(Color.White);Random ran = new Random();Pen RedPan = new Pen(new SolidBrush(Color.Red),2.0f);for (int i = 0; i < 12; i++){//绘制⽔平线g.DrawLine(RedPan, 50, panelHeight - 20 - i * 20, panelWidth - 40, panelHeight - 20 - i * 20);//绘制⽂字g.DrawString(i*100+"",new Font("宋体",10,FontStyle.Regular),new SolidBrush(Color.Black),20,panelHeight-27-i*20);}int x, y, w, h,ranNum;w = 24;for (int i = 0; i < 7; i++){//绘制垂直直线g.DrawLine(RedPan,50+40*i,panelHeight-20,50+40*i,20);//绘制名字g.DrawString("名字" + (i + 1), new Font("宋体", 8, FontStyle.Regular), new SolidBrush(Color.Black), 76 + 40 * i, panelHeight - 16);x = 78 + 40 * i;ranNum=ran.Next(50,250);y = panelHeight - 20 - ranNum;h = ranNum;g.FillRectangle(new SolidBrush(Color.FromArgb(60,130,80)),x,y,w,h);}g.DrawLine(RedPan, 50 + 40 * 7, panelHeight - 20, 50 + 40 * 7, 20);this.panel1.BackgroundImage = bitM;}三、绘制带有⽉份的柱形图private void button1_Click(object sender, EventArgs e){int panelHeight = this.panel1.Height;int panelWidth = this.panel1.Width;//创建新的画布Bitmap bitM = new Bitmap(panelWidth, panelHeight);Graphics g = Graphics.FromImage(bitM);g.Clear(Color.White);//设置字体Font font = new Font("Arial",9,FontStyle.Regular);Font fontSong = new Font("宋体", 20, FontStyle.Regular);LinearGradientBrush brush = new LinearGradientBrush(new Rectangle(0, 0, bitM.Width, bitM.Height),Color.Blue,Color.Green,1.2f,true);g.FillRectangle(Brushes.WhiteSmoke, 0, 0, panelWidth, panelHeight);Brush brush1 = new SolidBrush(Color.Blue);g.DrawString("2018XX⾛势", fontSong, brush1, new PointF(180, 30));//画图⽚的边框线g.DrawRectangle(new Pen(Color.Blue),0,0,panelWidth-4,panelHeight-4);Pen mypen = new Pen(brush,1f);//绘制纵向线条int x = 50;for (int i = 0; i < 13; i++){g.DrawLine(mypen,x,75,x,307);x += 40;}// 绘制横向线条int y = 74;for (int i = 0; i < 10; i++){g.DrawLine(mypen, 50, y, 530, y);y += 26;}//绘制X轴⽉份string[] strX = { "⼀⽉", "⼆⽉", "三⽉", "四⽉", "五⽉", "六⽉", "七⽉", "⼋⽉", "九⽉", "⼗⽉", "⼗⼀⽉", "⼗⼆⽉" };x = 45;for (int i = 0; i < strX.Length; i++){g.DrawString(strX[i].ToString(),font,Brushes.Red,x,panelHeight-30);x += 40;}//绘制Y轴数量string[] StrY = { "0", "100", "200", "300", "400", "500", "600", "700", "800" };y = 78;for (int i = 0; i < StrY.Length; i++){g.DrawString(StrY[StrY.Length-i-1].ToString(), font, Brushes.Red, 25, y);y += 26;}//填充数据Random ran = new Random();x = 55;y = 220;int h=0;for (int i = 0; i < strX.Length; i++){int ranNum=ran.Next(0,800);SolidBrush sb = new SolidBrush(Color.FromArgb(150,0,0));h = ranNum /4;g.FillRectangle(sb, x +i * 30, y-h+88, 30, h);x +=10;}this.panel1.BackgroundImage = bitM;}。
C# WinForm利用GDI+的双缓冲技术来提高绘图效率前言进入.NET时代,Windows的绘图技术也从GDI升级到了GDI+,从名字就能知道GDI+是对以前传统GDI 绘图技术的一次升级,不过在微软几乎把所有的新技术都冠之.NET的情况下,GDI+竟然不叫做,还真让我感到有点意外了。
:)GDI+在一种与设备无关的环境下提供了一套统一的绘图编程模型,极大的提高了Windows绘图编程的方便性,我们再也不用创建什么各种各样复杂的设备环境了,说实话,我现在想起来都头疼。
题归正传,关于如何进行GDI+的基本编程,我不能过多的加以描述,如果有对此概念还不太清楚的朋友,建议先去了解一下相关的资料,我们在这里主要讨论的是一种提高绘图效率(主要是动画效率)的双缓冲技术在GDI+中的应用和实现。
实现目的为了能清楚的对比应用双缓冲技术前后的效果,我编写了一段程序来进行测试。
首先,我创建了一个普通的Windows Application,在主Form中,我放置了一个定时器:timer1,然后将它的Interval属性设置为10,然后在Form上放置两个按纽,分别用来控制定时器的开启和关闭,最后,我还放置了一个label控件,用来显示绘图的帧数。
测试程序在timer1的timer1_Tick事件中,我写下了如下的代码(其中flag是一个bool型标志变量):DateTime t1 = DateTime.Now;Graphics g = this.CreateGraphics();if(flag){brush = new LinearGradientBrush(new PointF(0.0f, 0.0f),new PointF(700.0f, 300.0f), Color.Red, Color.Blue);flag = false;}else{brush = new LinearGradientBrush(new PointF(0.0f, 0.0f),new PointF(700.0f, 300.0f), Color.Blue, Color.Red);flag = true;}for(int j = 0; j < 60; j ++){for(int i = 0; i < 60; i++){g.FillEllipse(brush, i * 10, j * 10, 10, 10);}}DateTime t2 = DateTime.Now;TimeSpan sp = t2 - t1;float per = 1000 / liseconds;bel1.Text = "速度:" + per.ToString() + "帧/秒";运行后,我点击“开始”按纽,效果如下图所示:应用双缓冲以前的效果图(帧数:5帧/秒)正如大家所看到的,我在程序中使用循环画了几百个圆形,然后在每次的定时器脉冲事件中使用不同方向的线性渐变来对它们进行填充,形成了一个动画效果。
winform曲线图动态绘制 在winform开发过程中,有时候会需要开发曲线图相关功能,本⽂将分享⼀个⾃⼰开发的⼀个⾃定义曲线功能,如坐标轴的动态改变,以及曲线点的拖动和曲线动态绘制功能。
⾸先准备⼀张精准的ps坐标图,由于我们要实现坐标轴可动态变化,所以这张ps图不含具体坐标轴的值。
然后在winform窗⼝的panel⾥添加这张图作为背景,并且在坐标轴的位置添加lable⽤于显⽰坐标轴数值。
接下来是坐标点,我⽤的是12个坐标点,⽅法是添加12个pictureBox,然后给每⼀个pictureBox添加红⾊的背景,并缩到最⼩⼤⼩;同时添加12个textbox⽤于显⽰每个坐标点的坐标(相对于坐标轴)效果如图。
曲线图功能核⼼是要实现点与点之间的连线,这个通过DrawLine⽅法即可实现,但是要实现坐标点的拖动,就必须是实时更新绘制,我的解决⽅法是: 1、每次有点在拖动的时候清空已经绘制的线条。
2、坐标点移动到新的位置以后,重新绘制界⾯。
3、在界⾯绘制事件⾥加⼊所有曲线点之间线条绘制的功能。
⾸先实现坐标点拖动功能,由于12个坐标的拖动功能都⼀样,给12个pictureBox添加相同的事件,贴上坐标点的拖动功能代码:Point downPoint;//记录⿏标按下时的坐标(相对于panel)Point centerPoint = new Point(5, 505);//坐标轴原点(相对于panel)List<TextBox> textXList = new List<TextBox>();//横坐标集合List<TextBox> textYList = new List<TextBox>();//纵坐标集合int iMaxX=1000;//横纵最⼤值int iMaxY=500;//纵轴最⼤值 private void PictureBox_MouseMove(object sender, MouseEventArgs e){if (e.Button == MouseButtons.Left){PictureBox current = (PictureBox)sender;current.Location = new Point(current.Location.X + e.Location.X - downPoint.X, current.Location.Y + e.Location.Y - downPoint.Y);}}private void PictureBox_MouseDown(object sender, MouseEventArgs e){if (e.Button == MouseButtons.Left){downPoint = e.Location;}}private void PictureBox_Move(object sender, EventArgs e)//限制每个点的移动位置{PictureBox current = (PictureBox)sender;#region 限制位置,根据⾃⼰的panel分辨率和坐标轴分辨率来定,我的panel分辨率为1020*520,坐标轴分辨率为1000*500,坐标轴居中显⽰int index = int.Parse(current.Tag.ToString()) - 1;//每个pictureBox都有⼀个tag值,从1到12,⽤于区分对象int x, y;if (index == 0){x = current.Location.X <= panel1.Controls[index + 1].Location.X ? current.Location.X : panel1.Controls[index + 1].Location.X;x = x >= 5 ? x : 5;}else if (index == 11){x = current.Location.X >= panel1.Controls[index - 1].Location.X ? current.Location.X : panel1.Controls[index - 1].Location.X;x = x <= 1005 ? x : 1005;}else{x = current.Location.X;if (current.Location.X <= panel1.Controls[index - 1].Location.X){x = panel1.Controls[index - 1].Location.X;}if (current.Location.X >= panel1.Controls[index + 1].Location.X){x = panel1.Controls[index + 1].Location.X;}}y = current.Location.Y;if (current.Location.Y < 5){y = 5;}if (current.Location.Y > 505){y = 505;}current.Location = new Point(x, y);#endregionstring x1 = (Math.Round((float)(current.Location.X- centerPoint.X)* iMaxX / 1000 ,1)).ToString();string y1 = (Math.Round((float)(centerPoint.Y - current.Location.Y )* iMaxY / 500 ,1)).ToString();for (int i = 0; i < 12; i++)//记录所有点的横纵坐标{if (current.Tag == textXList[i].Tag){textXList[i].Text = x1;}if (current.Tag == textYList[i].Tag){textYList[i].Text = y1;panel1.Invalidate();//⽤于重绘界⾯return;}}} 然后就是绘制坐标点之间的连线: private void Panel1_Paint(object sender, PaintEventArgs e){for (int i = 1; i < 12; i++){Point current = new Point(panel1.Controls[i].Location.X + panel1.Controls[i].Width / 2, panel1.Controls[i].Location.Y + panel1.Controls[i].Height / 2);//当前点Point last = new Point(panel1.Controls[i - 1].Location.X + panel1.Controls[i - 1].Width / 2, panel1.Controls[i - 1].Location.Y + panel1.Controls[i - 1].Height / 2);//上⼀个点 e.Graphics.DrawLine(new Pen(Color.Red, 2), current, last);//当前点和上⼀个点连线}} 最后显⽰坐标轴数值: public void ChangXValue()//卸载窗体的load事件⾥{label1.Text = (iMaxX / 10).ToString();label2.Text = (2 * iMaxX / 10).ToString();label3.Text = (3 * iMaxX / 10).ToString();label4.Text = (4 * iMaxX / 10).ToString();label5.Text = (5 * iMaxX / 10).ToString();label6.Text = (6 * iMaxX / 10).ToString();label7.Text = (7 * iMaxX / 10).ToString();label8.Text = (8 * iMaxX / 10).ToString();label9.Text = (9 * iMaxX / 10).ToString();label10.Text = (10 * formShip.curveClass.iMaxX / 10).ToString();label11.Text = (iMaxY / 10).ToString();label12.Text = (2 * iMaxY / 10).ToString();label13.Text = (3 * iMaxY / 10).ToString();label14.Text = (4 * iMaxY / 10).ToString();label15.Text = (5 * iMaxY / 10).ToString();label16.Text = (6 * iMaxY / 10).ToString();label17.Text = (7 * iMaxY / 10).ToString();label18.Text = (8 * iMaxY / 10).ToString();label19.Text = (9 * iMaxY / 10).ToString();label20.Text = (10 *iMaxY / 10).ToString();} 此时,已经完成曲线点的绘制以及点的拖动功能,效果图如下: 最后,如果想实现通过直接配置textbox来实现点的坐标变化,可以加上如下代码: private void X2_LostFocus(object sender, EventArgs e)//失去焦点事件{TextBox tempTxt = (TextBox)sender;int index = int.Parse(tempTxt.Tag.ToString()) - 1;int x = (int)(float.Parse(tempTxt.Text) * 1000 / formShip.curveClass.iMaxX + centerPoint.X);panel1.Controls[index].Location = new Point(x, panel1.Controls[index].Location.Y);}private void Y12_LostFocus(object sender, EventArgs e)//失去焦点事件{TextBox tempTxt = (TextBox)sender;int index = int.Parse(tempTxt.Tag.ToString()) - 1;int y = (int)(centerPoint.Y - float.Parse(tempTxt.Text) * 500 / formShip.curveClass.iMaxY);panel1.Controls[index].Location = new Point(panel1.Controls[index].Location.X, y);}private void X2_KeyPress(object sender, KeyPressEventArgs e){if (e.KeyChar != 8 && !Char.IsDigit(e.KeyChar))//退格键和数字{e.Handled = true;}if (e.KeyChar == 13 )//回车键{pictureBox1.Focus();}} ⾃此,功能全部完成。
winform绘图控件chart实时曲线图官⽅教程:更多参考:实现思路:线程 while (true)循环,当集合满了10个元素之后,每次再进⾏绘制需要移除第⼀个点,添加新点到集合末尾。
这个chart控件⼗分的强⼤⽹上资料也很多。
我也只了解⽪⽑,刚好够解决我的问题。
所以没啥写的。
chart初始化:th = new Thread(GetData);chart1.Series[0].Name = strLinePh;chart1.Series[0].Color = Color.Red;chart1.Series.Add(strLinePress);chart1.Series[1].Color = Color.Blue;chart1.ChartAreas[0].AxisY2.ArrowStyle = AxisArrowStyle.Triangle;//chart1.ChartAreas[0].Axes[3].Interval = 50;chart1.Series[strLinePress].YAxisType = AxisType.Secondary;chart1.Titles.Add("PH 压⼒实时曲线");chart1.Legends[0].LegendStyle = LegendStyle.Row;chart1.Legends[0].Docking = Docking.Top;chart1.ChartAreas[0].AxisX2.LineColor = Color.Transparent;var cArea = chart1.ChartAreas[0];cArea.AxisY.LineColor = Color.Red;cArea.AxisY2.LineColor = Color.Blue;cArea.AxisY.Minimum = 0;cArea.AxisY.Maximum = 7;cArea.AxisY2.Minimum = -50;cArea.AxisY2.Maximum = 50;cArea.AxisX2.LineColor = Color.DarkGreen;cArea.AxisY2.MajorGrid.LineColor = Color.Transparent;cArea.AxisX.MajorGrid.LineColor = Color.Transparent;cArea.AxisX.ArrowStyle = AxisArrowStyle.Triangle;cArea.AxisY.ArrowStyle = AxisArrowStyle.Triangle;// Set series chart typechart1.Series[strLinePh].ChartType = SeriesChartType.Line;chart1.Series[strLinePress].ChartType = SeriesChartType.Line;// Set point labelschart1.Series[strLinePh].IsValueShownAsLabel = true;chart1.Series[strLinePress].IsValueShownAsLabel = true;// Enable X axis marginchart1.ChartAreas[0].AxisX.IsMarginVisible = true;chart1.Visible = false;。
C#WinForm实践开发教程》4.图型图像GDI编程.ppt//////直线//////amname="sender">///privatevoidbutton1_Click(objectsender,EventArgse){Graphicsg=this.CreateGraphics();g.Clear(Color.White);Penp=newPen(Color.Black);g.DrawLine(p,0,this.Height/2,this.Width,this.Height/2);p.Dispose();g.Dispose();}//////矩形/////////e="e">privatevoidbutton2_Click(objectsender,EventArgse){Graphicsg=this.CreateGraphics();g.Clear(Color.White);Penp=newPen(Color.Black);g.DrawRectangle(p,50,50,200,100);p.Dispose();g.Dispose();}//////圆形/////////aram>privatevoidbutton3_Click(objectsender,EventArg se){Graphicsg=this.CreateGraphics();g.Clear(Color.White);Penp=newPen(Color.Black);g.DrawEllipse(p,50,50,100,100);p.Dispose();g.Dispose();}///ummary>///柱形//////name="sender">///privatevoidbutton4_Click(objectsender,EventArgse){intheight=this.ClientSize.Height-100;intwidth=this.ClientSize.Width-50;intvHeigth=200;intvwidth=100;Graphicsg=this.CreateGraphics();g.Clear(Color.White);Penpen=newPen(Color.Gray);SolidBrushbrush=newSolidBrush(Color.Gainsboro);for(inti=height/2;i>0;i--){g.DrawEllipse(pen,width/2,i,vHeigth,vwidth);}g.FillEllipse(brush,width/2,0,vHeigth,vwidth);}usingSystem;usingSystem.Collections.Generic;usingSystem.Co mponentModel;usingSystem.Data;usingSystem.Drawing;usingSyste m.Text;usingSystem.Windows.Forms;usingSystem.Drawing.Drawi ng2D;namespaceWindowsApplication1{publicpartialclassForm2 :Form{publicForm2(){InitializeComponent();}//////填充矩形/////////privatevoidbutton1_Click(objectsender,EventArgse){Graphicsg=this.CreateGraphics();PendrawPen=newPen(Color.Red,3);SolidBrushmybrush=newSolidBrush(Color.Blue);g.DrawRectangle(drawPen,10,10,300,100);g.FillRectangle(mybrush,10,10,300,100);g.Dispose();drawPen.Dispose();mybrush.Dispose();}//////填充图形/////////privatevoidbutton2_Click(objectsender,EventArgse){Graphicsg=this.CreateGraphics();PendrawPen=newPen(Color.Red,3);TextureBrushmybrush=newTextureBrush(Image.FromFile(@"c:\1.jpg"));g.DrawRectangle(drawPen,10,10,300,100);g.FillRectangle(mybrush,10,10,300,100);g.Dispose();drawPen.Dispose();mybrush.Dispose();}//////填充椭圆//////er">///privatevoidbutton3_Click(objectsender,EventArgse){ Graphicsg=this.CreateGraphics();PendrawPen=newPen(Color.Red,3);SolidBrushsb=newSolidBru sh(Color.Blue);g.FillEllipse(sb,50,30,200,100);g.Dispose();drawPen.Dispose();}//////填充扇形/////////e">privatevoidbutton4_Click(objectsender,Eve ntArgse){Graphicsg=this.CreateGraphics();PendrawPen=newPen(Color.Red,3);S olidBrushsb=newSolidBrush(Color.Blue);g.FillPie( sb,50,10,150,100,0,90);g.Dispose();drawPen.Dispose();}//////填充多边形//////aram>///privatevoidbutton5_Click(objectsender,EventArgse){Graphicsg=this.CreateGraphics();SolidBrushsb=new SolidBrush(Color.Green);Pointp1,p2,p3,p4;p1=newPoint(100,10);p2=newPoint(120,70);p3=newPoint(200,110);p4=newPoint(160,30);Point[]points={p1,p2,p3,p4};g.FillPolygon(sb,points);g.Dispose();sb.Dispose();}//////填充波形//////param>///privatevoidbutton6_Click(objectsender,EventArgse){G raphicsg=this.CreateGraphics();PendrawPen=new Pen(Color.Red,3);HatchBrushmybrush=newHatchBrus h(HatchStyle.Wave,Color.Red,Color.Blue);g.DrawRect angle(drawPen,10,10,300,100);g.FillRectangle(myb rush,10,10,300,100);g.Dispose();drawPen.Dispose();mybrush.Dispose();}//////颜色渐变/////////m>privatevoidbutton7_Click(objectsender,EventArgse){Graphicsg=this.CreateGraphics();PendrawPen=newPen(Color.Red,3);LinearGradientBrushmybrush=newLinearGradientBrush(ClientRectangl e,Color.Black,Color.White,LinearGradientMode.Vertical);g.DrawRectangle(drawPen,10,10,500,500);g.FillRectangle(mybrush,10,10,500,500);g.Dispose();drawPen.Dispose();mybrush.Dispose();}//////填充渐变圆/////////"e">privatevoidbutton8_Click(objectsender,EventArgse){Graphicsg=this.CreateGraphics();Rectangler=newRectangle(150,50,200,200);LinearGradientBrushbrush=newLinearGradientBrush(r,Color.Orange,Color.Purple,90);g.FillEllipse(brush,r);g.Dispose();brush.Dispose();}}}--------------基于C#语言Windows程序设计第四章、图型图像GDI编程本章主要内容介绍4.1繪圖的基本觀念4.2繪圖屬性與方法4.3繪圖相關類別CONTENT本章学习目标:了解System.Drawing命名空间掌握矢量图形和绘制对象可以绘制简单的几何图形掌握图像的基本处理技术4.1什么是GDI+首先先了解什么是GDI 呢?GDI是从Windows95到Windows2000随附的旧版绘图装置接口(GraphicsDeviceInterface),是属于绘图方面的API(ApplicationProgrammingInterface)。
【转】C#WinForm⽤图表(chart)控件绘制曲线图⽅法先以图⼀条样条曲线(Spline)为例(1)在Visual Studio中新建⼀个“Windows窗体应⽤程序”(2)在Form1上布置⼀个Chart控件。
默认地,Chart控件显⽰的是直⽅图(3)Form1的窗体代码Form1.csing System;using System.Windows.Forms;using System.Windows.Forms.DataVisualization.Charting;namespace WindowsFormsApplication1{public partial class Form1 : Form{public Form1(){InitializeComponent();// 设置曲线的样式Series series = chart1.Series[0];// 画样条曲线(Spline)series.ChartType = SeriesChartType.Spline;// 线宽2个像素series.BorderWidth = 2;// 线的颜⾊:红⾊series.Color = System.Drawing.Color.Red;// 图⽰上的⽂字series.LegendText = "演⽰曲线";// 准备数据float[] values = { 95, 30, 20, 23, 60, 87, 42, 77, 92, 51, 29 };// 在chart中显⽰数据int x = 0;foreach (float v in values){series.Points.AddXY(x, v);x++;}// 设置显⽰范围ChartArea chartArea = chart1.ChartAreas[0];chartArea.AxisX.Minimum = 0;chartArea.AxisX.Maximum = 10;chartArea.AxisY.Minimum = 0d;chartArea.AxisY.Maximum = 100d;}}}(4)运⾏效果(5)FastLine类型曲线。
winform 画椭圆算法椭圆的绘制算法有很多种。
下面我将介绍两种常见的绘制椭圆的算法。
第一种算法是中点椭圆绘制算法。
该算法的思想是:通过划分椭圆为四个象限,并使用对称性质来进行绘制。
算法步骤:1. 初始化参数:给定椭圆的中心坐标(xc, yc)、长轴和短轴的长度(a, b),以及绘制椭圆的颜色。
2. 计算参数:初始化两个计算用的变量,分别为d1和d2,将长轴和短轴长度变量赋值给它们。
3. 初始化绘制的点位置:给定椭圆的起始点位置(x, y)为(0, b)。
4. 划分椭圆为四个象限:分别计算四个象限的对应的坐标点位置- 区域1:d1 = b^2 + a^2/4 - a^2b- 如果d1 < 0,则绘制像素点(x+1, y),并更新d1和d2的值,d1 = d1 + 2b^2*x + b^2;d2 = d2 + 2b^2*x + b^2 - 2a^2*y + a^2。
- 如果d1 >= 0,则绘制像素点(x+1, y-1),并更新d1和d2的值,d1 = d1 + 2b^2*x + b^2 - 2a^2*y + a^2;d2 = d2 - 2a^2*y + a^2。
- 区域2、3、4: 对称操作与区域1一样,只是坐标点的位置不同。
5. 绘制完一个象限后,更新椭圆的坐标点位置:x = x + 1,y = y - 1。
6. 重复步骤4-5,直到所有的象限画完。
第二种算法是Bresenham椭圆绘制算法。
该算法利用Bresenham直线算法的思想来进行绘制。
算法步骤:1. 初始化参数:给定椭圆的中心坐标(xc, yc)、长轴和短轴的长度(a, b),以及绘制椭圆的颜色。
2. 计算参数:初始化两个计算用的变量,分别为x和y,将长轴和短轴长度变量赋值给它们。
3. 初始化绘制的点位置:给定椭圆的起始点位置(x, y)为(0, b)。
4. 计算初始点的决策参数:初始化决策参数d1,计算公式为d1 = b^2 - a^2b + a^2/4。
幸运的是当编写一个典型的Windows 窗体程序时,窗体和控件的绘制、效果等操作是不需要特别加以考虑的。
这是为什么呢?因为通过使用 .Net 框架,开发人员可以拖动一系列的控件到窗体上,并书写一些简单的与事件相关联的代码然后在IDE中按F5,一个完完全全的窗体程序就诞生了!所有控件都将 自己绘制自己,窗体或者控件的大小和缩放都调整自如。
在这里经常会用到的,且需要引起一点注意的就是控件效果。
游戏,自定义图表控件以及屏幕保护程序的编 写会需要程序员额外撰写用于响应 Paint 事件的代码。
本文针对那些Windows 窗体开发人员并有助于他们在应用程序编制过程中使用简单的绘图技术。
首先,我们会讨论一些基本的绘图概念。
到底谁在负责进行绘制操作?Windows 窗体程序是如何知道何时该进行绘制的?那些绘制代码究竟被放置在哪里?之后,还将介绍图像绘制的双重缓冲区技术,你将会看到它是怎样工作的,怎样通过一个 方法来实现缓存和实际显示的图像间的交替。
最后,我们将会探讨”智能无效区域”,实际就是仅仅重绘或者清除应用程序窗体上的无效部分,加快程序的显示和响 应速度。
希望这些概念和技术能够引导读者阅读完本文,并且有助于更快和更有效的开发Windows 窗体程序。
Windows 窗体使用GDI+图像引擎,在本文中的所有绘图代码都会涉及使用托管的.Net 框架来操纵和使用Windows GDI+图像引擎。
尽管本文用于基本的窗体绘图操作,但是它同样提供了快速的、有效的且有助于提高程序性能的技术和方法。
所以,在通读本文之前建议读者对.Net框架有个 基本的了解,包括Windows 窗体事件处理、简单的GDI+对象譬如Line,Pen和Brush等。
熟悉Visual Basic .Net或者C#编程语言。
概念Windows 应用程序是自己负责绘制的,当一个窗体”不干净”了,也就是说窗体改变了大小,或者部分被其它程序窗体遮盖,或者从最小化状态恢复时,程序都会收到需要绘 制的信息。
Windows把这种”不干净”状态称为”无效的(Invalidated)”状态,我们理解为:需要重绘,当Windows 窗体程序需要重绘窗体时它会从Windows消息队列中获取绘制的信息。
这个信息经过.Net框架封装然后传递到窗体的 PaintBackground 和 Paint 事件中去,在上述事件中适当的书写专门用于绘制的代码即可。
简单的绘图示例如下:ing System;ing System.Drawing;ing System.Windows.Forms;4.public class BasicX : Form5.{6.public BasicX()7.{8.InitializeComponent();9.}10.11.private void BasicX_Paint(object sender, PaintEventArgs e)12.{13.Graphics g = e.Graphics;14.Pen p = new Pen(Color.Red);15.int width = ClientRectangle.Width;16.int height= ClientRectangle.Height;17.g.DrawLine(p, 0,0, width, height);18.g.DrawLine(p, 0, height, width, 0);19.p.Dispose();20.}21.private void InitializeComponent()22.{23.this.SetStyle(ControlStyles.ResizeRedraw, true);24.this.ClientSize = new System.Drawing.Size(300, 300);25.this.Text = "BasicX";26.this.Paint += new PaintEventHandler(this.BasicX_Paint);27.}28.[System.STAThreadAttribute()]29.public static void Main()30.{31.Application.Run(new BasicX());32.}33.}复制代码上述代 码分成两个基本的步骤来创建示例程序。
首先 InitializeComponent 方法包含一些属性的设置和附加窗体 Paint 事件的处理过程。
注意,在方法中控件的样式也同时被设置,设置控件的样式也是自定义Windows 窗体及控件行为的一种有效途径,譬如:控件的"ResizeRedraw"属性指示当窗体的大小变化发生以后需要对其完全进行重绘,也就是说重绘时总是需 要对整个窗体的客户区域进行重绘。
窗体的“客户区域”是指除了标题栏和边框的所有窗体区域。
可以进行一个有趣的试验,取消该控件的属性然后再运行程序,我 们可以很明显的看出为什么该属性会被经常的设置,因为窗体调整大小后的无效区域根本不会被重绘。
好了,我们需要注意一下 BasicX_Paint方法,正如先前所提到的,Paint 事件在程序需要重绘时被激活,程序窗体利用Paint事件来负责回应需要重绘的系统消息,BasicX_Paint方法的调用需要一个对象 sender 和一个PaintEventArgs类型的变量,PaintEventArgs类的实例或称之为变量 e 封装了两个重要的数据,第一个就是窗体的 Graphics 对象,该对象表示窗体可绘制的表面也称之为画布用于绘制诸如线、文本以及图像等,第二个数据就是ClipRectangle,该Rectangle对象表 示窗体上无效的的矩形范围,或者说就是窗体需要重绘的区域。
记住,当窗体的ResizeRedDraw设置后,调整大小后该ClipRectangle的 大小实际就等于窗体整个客户区域的大小,或者是被其它程序窗体遮盖的那部分剪切区域。
关于部分剪切区域的用处我们会在智能重绘章节作更详细的阐述。
BasicX 示例程序的运行界面双重缓冲区绘图技术双重缓冲区技术能够使程序的绘图更加快速和平滑,有效减少绘制时的图像闪烁。
该技术的基本原理是先将图像绘制到内存中的一块画布上,一旦所有的绘制操作 都完成了,再将内存中的画布推到窗体的或者控件的表面将其显示出来。
通过这种操作后的程序能使用户感觉其更加快速和美观。
下面提供的示例程序能够阐明双重缓冲区的概念和实现方法,这个示例所包含的功能已相当完整,且完全可以在实际应用中使用。
在该章节后面还会提及该技术应该配合控件的一些属性设置才能达到更好的效果。
要想领略双重缓冲区绘图技术所带来的好处就请运行SpiderWeb示例程序吧。
程序启动并运行后对窗口大小进行调整,你会发现使用这种绘图算法的效率不高,并且在调整大小的过程中有大量的闪烁出现。
不具备双重缓冲区技术的SpiderWeb示例程序纵观程序的源码你会发现在程序Paint事件激活后是通过调用LineDrawRoutine方法来实现线的绘制的。
LineDrawRoutine方 法有两个参数,第一个是Graphics对象是用于绘制线条的地方,第二个是绘图工具Pen对象用来画线条。
代码相当简单,一个循环语 句,LINEFREQ常量等,程序从窗体表面的左下一直划线到其右上。
请注意,程序使用浮点数来计算在窗体上的绘制位置,这样做的好处就是当窗体的大小发 生变化时位置数据会更加精确。
1.private void LineDrawRoutine(Graphics g, Pen p)2.{3.float width = ClientRectangle.Width;4.float height = ClientRectangle.Height;5.float xDelta = width / LINEFREQ;6.float yDelta = height / LINEFREQ;7.for (int i = 0; i < LINEFREQ; i++)8.{9.g.DrawLine(p, 0, height - (yDelta * i), xDelta * i, 0);10.}11.}复制代码撰写很简 单的用于响应Paint事件SpiderWeb_Paint的代码,正如前面所提到的,Graphics对象就是从Paint事件参数 PaintEventArgs对象中提取出来的表示窗体的绘制表面。
这个Graphics对象连同新创建Pen对象一起传递给 LineDrawRoutine方法来画出蜘蛛网似的线条,使用完Graphics对象和Pen对象后释放其占用的资源,那么整个绘制操作就完成了。
1.private void SpiderWeb_Paint(object sender, PaintEventArgs e)2.{3.Graphics g = e.Graphics;4.Pen redPen = new Pen(Color.Red);5.LineDrawRoutine(g, redPen);6.redPen.Dispose();7.g.Dispose();8.}复制代码那么到底 作怎么样的改动才能使上面的SpiderWeb程序实现简单的双重缓冲区技术呢?原理其实相当简单,就是将应该画到窗体表面的绘制操作改成先画到内存中的 位图上,LineDrawRoutine向这个在内存中隐藏的画布执行同样的蜘蛛网绘制操作,等到绘制完毕再通过调用 Graphics.DrawImage方法将隐藏的画布上内容推到窗体表面来显示出来,最后,再加上一些小的改动一个高性能的绘图窗体程序就完成了。
请比较下面双重缓冲区绘图事件与前面介绍的简单绘图事件间的区别:1.private void SpiderWeb_DblBuff_Paint(object sender,PaintEventArgs e)2.{3.Graphics g = e.Graphics;4.Pen bluePen = new Pen(Color.Blue);5.Bitmap localBitmap = newBitmap(ClientRectangle.Width,ClientRectangle.Height);6.Graphics bitmapGraphics = Graphics.FromImage(localBitmap);7.LineDrawRoutine(bitmapGraphics, bluePen);8.//把在内存里处理的bitmap推向前台并显示9.g.DrawImage(localBitmap, 0, 0);10.bitmapGraphics.Dispose();11.bluePen.Dispose();12.localBitmap.Dispose();13.g.Dispose();14.}复制代码C#栏目的最新浏览文章:• C#使用FTP实现客户端程序自动更新• 异步窗体实现操作进度(ProgressWindow)• Splash窗体(ProgressWindowForm修改)• C# 4.0 Unleashed• Pro .NET 4 Parallel Programming in C#• Effective C#: 50 Specific Ways to Improve Your C#, Second Edition (Covers C# 4.0)• Effective C# 中文版(改善程序的50种方法)PDF下载• 使用C#,轻松发邮件• 基于TCP的网络游戏黑白棋系列(一):建立连接• 基于TCP的网络游戏黑白棋系列(二):数据传输• 车牌识别及验证码识别的一般思路• Winform的DataGridView实现行可折叠• C#设计模式教程• C# Socket 实现的淘宝秒杀器(抢拍器)• C# Graphics Programming• 使用键盘指示灯的闪烁来显示网络流量(C#编写)cobra - 2007-10-29 16:27:00上面的示例代码创建了 内存位图对象,它的大小等于窗体的客户区域(就是绘图表面)的大小,通过调用Graphics.FromImage将内存中位图的引用传递给 Graphics对象,也就是说后面所有对该Graphics对象的操作实际上都是对内存中的位图进行操作的,该操作在C++中等同于将位图对象的指针复 制给Graphics对象,两个对象使用的是同一块内存地址。