当希望使用GDI进行绘图的时候,一定要先定义一个Graphics的对象,这个对象主要
是用来创建一个进行绘图的容器,这个对象如果是窗体创建的,那么该对象调用画图函数画出的图就在窗体中,如果这个对象是某个容器创建的,那么该对象调用画图函数画出的图就在容器中。
对于主窗体Form,有如下几种方式来定义该对象:
(1)、直接在Form窗体的某个事件或方法中定义对象:
private void Form1_Load(object sender, EventArgs e)
{
Graphics g = this.CreateGraphics();
}
(2)、重写窗体的OnPaint方法,在OnPaint方法中定义对象:
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Graphics g = e.Graphics;
}
(3)、在窗体的Paint事件中定义对象:
private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
}
对于别的容器,则必须在其Paint事件中去创建:
private void panel1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
}
重绘的概念:
当窗体或者容器由不可见到可见,以及由一种可见的样式转变成另外一种样式,这个过程就是重绘的过程,也就是重新绘制,重新绘画。加载窗体或者窗体失去焦点又得到焦点时,都会重绘。
重绘过程触发的事件:
不管是窗体还是别的容器,只要发生了重绘,那么一定会触发这个窗体或者容器的Paint事件;在加载窗体的时候,就会重绘窗体以及其上的所有容器,所以加载窗体的时候会首先去调用窗体的OnPaint方法,然后去调用窗体的Paint事件,然后去调用窗体上面的其他的各个容器的Paint事件。
如果没有重写OnPaint方法,那么会隐含的调用从父类继承过来的OnPaint方法,如果重写了,那么默认在重写的OnPaint方法中,会有base.OnPaint(e)语句,也就是还会默认的调用父类的这个方法。只有调用了这个方法,才会去触发窗体的Paint事件,如果在重写方法中删除base.OnPaint(e)语句,那么窗体的Paint事件就不会被触发。
在程序中控制窗体或者容器重绘:
控制某个具体的容器重绘:
this.panel1.Invalidate();
控制当前窗体重绘:
this.Invalidate();
通过以上的方式重绘之后也会触发重绘控件的Paint事件。
颜色Color:
Color c = Color.Black;
c = Color.FromArgb(5, 3, 4, 5); //a,r,g,b
c = Color.FromName("Black");//System.Drawing.KnownColor.Black.ToString()
字体Font:
Font ft = new Font("Arial", 20);//第一个参数是字体,第二个是大小
整数尺寸Size和浮点数尺寸SizeF:
Size s = new Size(10, 20);//width,height
SizeF sf = new SizeF(10.5f, 20.3f);//width,height
Size s1 = s;//10, 20
Size s2 = new Size(5, 5);
Size s3 = s1 - s2;//5, 15
平面上的点Point(整数坐标)和PointF(浮点坐标):
Point p = new Point(10,100);//X坐标,Y坐标
矩形Rectangle(整数坐标)和RectangleF(浮点坐标):
Rectangle r = new Rectangle(2, 2, 90, 80);//x,y,width,height
r.Bottom//底部纵坐标
r.Top//顶部纵坐标
r.Left;//左边横坐标
r.Right;//邮编横坐标
r.Location;//左上角的点
r.X;//左上角点的横坐标
r.Y;//左上角点的纵坐标
r.Height;//高度
r.Width;//宽度
r.Size;//尺寸
Rectangle rf = new RectangleF(2.2, 2.2, 90.4, 80.5);//x,y,width,height
画笔Pen:
画笔是用来画图的,画的是图片的边缘。
Color c = Color.Black;
Pen pen = new Pen(c,5); //颜色,画笔的宽度
画刷Brush:
画刷是用来填充图的,这个画刷是一个抽象的父类,它有几十个实体子类:
Color c = Color.Black;
SolidBrush sb = new SolidBrush(c); //实心画刷,最常用
HatchBrush hb = new HatchBrush();//带阴影线的画刷
LinearGradientBrush lgb = new LinearGradientBrush();//填充颜色线性渐变的画刷
PathGradientBrush pgb = new PathGradientBrush(); //填充颜色路径渐变的画刷
//以下是使用图像进行填充的画刷
Image brushImage = new Bitmap(@"c:\inmage.jpg");
TextureBrush tb = new TextureBrush(brushImage);
划线函数DrawLine:
g.DrawLine(pen,p1,p2); //画笔,起始点,结束点
划多条线函数DrawLines:
g.DrawLines(pen, new Point[] { p1,p2,p3});
这个函数会按照顺序,从p1开始连接p2,然后从p2开始连接p3,不会去跳跃相连,很显然以上这个是非封闭的,如果想封闭,那么最后再加一个点(起始点)作为结束点即可。
绘制多边形函数DrawPolygon:
g.DrawPolygon(pen, new Point[] { p1, p2, p3 });
这个函数会按照顺序,从p1开始连接p2,然后从p2开始连接p3,不会去跳跃相连,但是最后还会默认的从p3连接到p1,完成封闭。
绘制矩形函数DrawRectangle:
Rectangle r = new Rectangle(2, 2, 180, 80);
g.DrawRectangle(pen, r);
绘制饼图函数DrawRectangle:
Rectangle r = new Rectangle(2, 2, 180, 80);
g.DrawPie(pen, r,60,280);
第一边是以X轴作为起始轴,然后顺时针转动60度得到的;第二边又是以第一边为起始轴,顺时针转动280度得到的。
绘制椭圆函数DrawEllipse:
Rectangle r = new Rectangle(2, 2, 180, 80);
g.DrawEllipse(pen, r);
填充多边形FillPolygon:
Rectangle r = new Rectangle(2, 2, 180, 80);
g.FillPolygon(sb, new Point[] { p1, p2, p3 });
填充矩形FillRectangle:
Rectangle r = new Rectangle(2, 2, 180, 80);
g.FillRectangle(sb, r);
填充饼图FillPie:
Rectangle r = new Rectangle(2, 2, 180, 80);
g.FillPie(sb, r, 60, 280);
填充椭圆形FillEllipse:
Rectangle r = new Rectangle(2, 2, 180, 80);
g.FillEllipse(sb, r);
用画刷写字:
g.DrawString("hello!", new Font("Arial", 20), sb, new PointF(30, 30));
写字只能用画刷来写,最后那个点表示子的起始位置。
(1)、画图保存
Bitmap bm = new Bitmap(300, 200);
Graphics g = Graphics.FromImage(bm);
Rectangle r = new Rectangle(2, 2, 180, 80);
SolidBrush sb = new SolidBrush(Color.Red);
g.FillRectangle(sb, r);
bm.Save(@"d:\a.jpg");
(1)、当DataGridView控件绑定数据的时候,会重绘,不管第几次绑定数据,哪怕每次数据源都是一样的,都会重绘。
(2)、重绘的时候,每个单元格重绘都会先触发单元格重绘事件,事件执行完之后,然后再重绘单元格,显示出来。