17.2 图形绘制8
版权声明:本文为博主原创文章,转载请在显著位置标明本文出处以及作者网名,未经作者允许不得用于商业目的。
17.2.10 重绘
先看以下例子:
【例 17.28】【项目:code17-028】绘制填充矩形。
private void button1_Click(object sender, EventArgs e)
{
Graphics g = this.CreateGraphics();
g.FillRectangle(new SolidBrush(Color.Blue), new Rectangle(260, 20, 100, 100));
}
private void Form1_Load(object sender, EventArgs e)
{
Graphics g = this.CreateGraphics();
g.FillRectangle(new SolidBrush(Color.Red), new Rectangle(20, 20, 100, 100));
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics g = this.CreateGraphics();
g.FillRectangle(new SolidBrush(Color.Green), new Rectangle(140, 20, 100, 100));
}
这个例子在窗体Load、窗体Paint、按钮Click时分别绘制了红色、绿色、蓝色填充的矩形。
当运行时:
图17-31 运行时只显示绿色矩形
按下按钮时:
图17-32 显示绿色和蓝色矩形
最小化又恢复后:
图17-33 显示绿色矩形
原本的计划是:1、在Load的时候绘制一个红色正方形;2、在Paint的时候绘制一个绿色正方形;3、在按下按钮的时候绘制一个蓝色正方形;一共三个矩形。
运行后窗体上只显示了绿色而没有显示红色矩形,因为窗口Load事件之后是Paint事件,而Paint执行的时候将界面刷新了,对整个窗口进行了重绘:先是清除之前绘制的内容,然后按照Paint里面代码做了绘制,所以绿色矩形正常显示,而红色矩形没有显示。同样的,最小化后再恢复窗口,也触发了窗体的Paint事件,进行了重绘,所以绿色矩形在而蓝色矩形没有了。
分析了造成矩形不显示的原因,那么针对重绘的问题,也就有了以下解决方法:只需要把绘制的图形保存下来,当重绘时在绘制出来即可。
【例 17.29】【项目:code17-029】防止重绘时图形消失。
//用来保存绘制的图形
Bitmap bmp;
Graphics g;
private void Form1_Load(object sender, EventArgs e)
{
//使图像宽度和高度与窗体相同
bmp = new Bitmap(this.Width, this.Height);
//通过图像创建Graphic
g = Graphics.FromImage(bmp);
g.FillRectangle(new SolidBrush(Color.Red), new Rectangle(20, 20, 100, 100));
ShowImg();
}
private void button1_Click(object sender, EventArgs e)
{
g.FillRectangle(new SolidBrush(Color.Green), new Rectangle(140, 20, 100, 100));
g.FillRectangle(new SolidBrush(Color.Blue), new Rectangle(260, 20, 100, 100));
ShowImg();
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
ShowImg();
}
//在窗体上显示图像
private void ShowImg()
{
Graphics gPaint = this.CreateGraphics();
gPaint.DrawImage(bmp, new Point(0, 0));
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
g.Dispose();
bmp.Dispose();
}
这下,窗体Load时的红色正方形以及按下按钮后的绿色和蓝色矩形,即使窗体最小化也不怕消失了。
17.2.11 绘制统计图
【例 17.30】【项目:code17-030】绘制统计图
假设知道某个公司1-4季度的经营利润情况,完成统计图的绘制。
新建一个窗体,主要使用到的控件如下:
添加4个Label控件,分别是“一季度”、“二季度”、“三季度”、“四季度”。
添加4个NumericUpDown控件,名称从 nudSeason1至 nudSeason4。
添加1个ComboBox控件,名称为cbType,代码中为它的Items增加柱状图、折线图、扇形图、环形图。
添加1个PictureBox控件,名称为pbShow,将在这里面显示统计图。
添加2个Button控件,名称分别为btnCreate和btnSave,Text属性分别为“生成”和“保存”。当按下“生成”按钮后,将在pbShow中显示统计图;当按下“保存”按钮后,将弹出保存文件对话框,设置文件名后将统计图保存下来。
具体代码如下:
Graphics g;
Bitmap bmp;
//刻度线每份对应的数量
float perScaleValue;
//原点x轴移动
int TranslateX = 40;
//原点y轴移动
int TranslateY = 40;
//季度(x轴)之间间隔
int xSpan = 60;
private void Form1_Load(object sender, EventArgs e)
{
bmp = new Bitmap(pbShow.Width, pbShow.Height);
g = Graphics.FromImage(bmp);
cbType.Items.Add("柱形图");
cbType.Items.Add("折线图");
cbType.Items.Add("扇形图");
cbType.Items.Add("环形图");
cbType.Text = cbType.Items[0].ToString();
}
private void btnCreate_Click(object sender, EventArgs e)
{
g.Clear(System.Drawing.Color.White);
switch(cbType.Text)
{
case "柱形图":
DrawAxis();
DrawBarGraph();
break;
case "折线图":
DrawAxis();
DrawLine();
break;
case "扇形图":
DrawPie();
break;
case "环形图":
DrawAnnular();
break;
default:
break;
}
pbShow.Image = bmp;
}
//绘制坐标轴
private void DrawAxis()
{
int[] seasonValue = new int[4];
seasonValue[0] = (int)nudSeason1.Value;
seasonValue[1] = (int)nudSeason2.Value;
seasonValue[2] = (int)nudSeason3.Value;
seasonValue[3] = (int)nudSeason4.Value;
//获得最大值
int seasonMax = seasonValue.Max();
//获得需要的刻度数量
int scaleCount;
scaleCount = seasonMax / 10 + 1;
//使用红色绘制坐标轴
Pen p = new Pen(System.Drawing.Color.Red, 1);
//坐标轴末尾箭头
p.EndCap = LineCap.ArrowAnchor;
int AxisYHeight;
AxisYHeight = pbShow.Height - TranslateY * 2;
perScaleValue = AxisYHeight / scaleCount;
//坐标原点
int originX = TranslateX;
int originY = pbShow.Height - TranslateY;
Point originPoint = new Point(originX, originY);
//绘制横坐标
g.DrawLine(p, originPoint, new Point(pbShow.Width - TranslateX, originY));
//绘制纵坐标
g.DrawLine(p, originPoint, new Point(originX, TranslateY));
//红色绘制坐标轴刻度
Pen pAxisY = new Pen(System.Drawing.Color.Red, 1);
Point AxisYPos;
string AxisYValue;
//在纵轴上标明刻度线,每10个刻度标注一下
for(int i = 0;i< scaleCount;i++)
{
//刻度值
AxisYValue = (i * 10).ToString();
//刻度位置
AxisYPos = new Point(20, originY - i * (int)perScaleValue - 5);
//标明刻度值
g.DrawString(AxisYValue, new Font("宋体", 10), new SolidBrush(System.Drawing.Color.Blue), AxisYPos);
//画刻度,实际0刻度线是和横坐标轴重合
g.DrawLine(pAxisY, new Point(TranslateX, originY - i * (int)perScaleValue), new Point(TranslateX + 10, originY - i * (int)perScaleValue));
}
}
//绘制柱状图
private void DrawBarGraph()
{
//标注每个季度
int[] seasonValue = new int[4];
seasonValue[0] = (int)nudSeason1.Value;
seasonValue[1] = (int)nudSeason2.Value;
seasonValue[2] = (int)nudSeason3.Value;
seasonValue[3] = (int)nudSeason4.Value;
string[] seasonName = { "一季度", "二季度", "三季度", "四季度" };
//立柱(矩形)的左上角坐标点
int recX, recY;
//循环画四个矩形
for(int i = 0;i<4;i++)
{
recX = (i + 1) * xSpan;
recY = pbShow.Height - TranslateY - (int)(seasonValue[i] / 10 * perScaleValue);
g.FillRectangle(new SolidBrush(Color.Blue), new Rectangle(recX, recY, 40, (int)(seasonValue[i] / 10 * perScaleValue)));
}
//标出每个季度
int strX, strY;
for(int i =0;i<4;i++)
{
strX = (i + 1) * xSpan - 5;
strY = pbShow.Height - TranslateY + 10;
g.DrawString(seasonName[i], new Font("黑体", 10), new SolidBrush(Color.Blue), new Point(strX, strY));
}
}
//绘制折线图
private void DrawLine()
{
//标注每个季度
int[] seasonValue = new int[4];
seasonValue[0] = (int)nudSeason1.Value;
seasonValue[1] = (int)nudSeason2.Value;
seasonValue[2] = (int)nudSeason3.Value;
seasonValue[3] = (int)nudSeason4.Value;
string[] seasonName = { "一季度", "二季度", "三季度", "四季度" };
//先要获得每个值所在的坐标点
//为了显眼,绘制点的显示为一个直径为8的蓝色圆形
int signX, signY;
//将每个坐标点存入数组,画折线时候需要
Point[] pointSign = new Point[4];
for(int i =0; i<4; i++)
{
signX = (i + 1) * xSpan + 10;
signY = pbShow.Height - TranslateY - (int)(seasonValue[i] / 10 * perScaleValue);
pointSign[i] = new Point(signX, signY);
//请注意画园时候的Rectangle位置
g.FillEllipse(new SolidBrush(Color.Blue), new Rectangle(signX - 4, signY - 4, 8, 8));
}
//使用红色画折线
Pen penSign = new Pen(Color.Red, 2);
//将四个坐标点连接起来,注意画的是三条线
for (int i = 0; i < 3; i++)
g.DrawLine(penSign, pointSign[i], pointSign[i + 1]);
//标出每个季度
int strX, strY;
for(int i = 0;i<4;i++)
{
strX = (i + 1) * xSpan - 5;
strY = pbShow.Height - TranslateY + 10;
g.DrawString(seasonName[i], new Font("黑体", 10), new SolidBrush(Color.Blue), new Point(strX, strY));
}
}
//绘制扇形图
private void DrawPie()
{
//标注每个季度
int[] seasonValue = new int[4];
seasonValue[0] = (int)nudSeason1.Value;
seasonValue[1] = (int)nudSeason2.Value;
seasonValue[2] = (int)nudSeason3.Value;
seasonValue[3] = (int)nudSeason4.Value;
string[] seasonName = { "一季度", "二季度", "三季度", "四季度" };
//我们要获得4个季度总的盈利
int seasonSum = 0;
for (int i = 0; i < 4; i++)
seasonSum += seasonValue[i];
//根据总赢利情况,来获得每个季度在饼图中所占的份额(角度)
//为了简化起见,这里直接取整数
int[] seasonAngle = new int[5];
seasonAngle[0] = 0;
seasonAngle[1] = seasonValue[0] * 360 / seasonSum;
seasonAngle[2] = seasonValue[1] * 360 / seasonSum + seasonAngle[1];
seasonAngle[3] = seasonValue[2] * 360 / seasonSum + seasonAngle[2];
seasonAngle[4] = 360;
//分别用4种颜色表示不同季度的盈利
System.Drawing.Color[] seasonColor = { Color.Red, Color.Blue, Color.Green, Color.GreenYellow };
for(int i = 0;i<4;i++)
{
g.FillPie(new SolidBrush(seasonColor[i]), new Rectangle(50, 80, 200, 200), seasonAngle[i], seasonAngle[i + 1] - seasonAngle[i]);
//饼图中特别需要说明每个季度对应的颜色
g.FillRectangle(new SolidBrush(seasonColor[i]), new Rectangle(260, i * 30 + 20, 30, 20));
//标出每个季度
g.DrawString(seasonName[i], new Font("宋体", 12), new SolidBrush(Color.Black), new Point(300, i * 30 + 20));
}
}
//绘制环形图
private void DrawAnnular()
{
//标注每个季度
int[] seasonValue = new int[4];
seasonValue[0] = (int)nudSeason1.Value;
seasonValue[1] = (int)nudSeason2.Value;
seasonValue[2] = (int)nudSeason3.Value;
seasonValue[3] = (int)nudSeason4.Value;
string[] seasonName = { "一季度", "二季度", "三季度", "四季度" };
//我们要获得4个季度总的盈利
int seasonSum = 0;
for (int i = 0; i < 4; i++)
seasonSum += seasonValue[i];
//根据总赢利情况,来获得每个季度在饼图中所占的份额(角度)
//为了简化起见,这里直接取整数
int[] seasonAngle = new int[5];
seasonAngle[0] = 0;
seasonAngle[1] = seasonValue[0] * 360 / seasonSum;
seasonAngle[2] = seasonValue[1] * 360 / seasonSum + seasonAngle[1];
seasonAngle[3] = seasonValue[2] * 360 / seasonSum + seasonAngle[2];
seasonAngle[4] = 360;
//圆环内弧与外弧之间的距离
int xSpace = 50;
//分别用4种颜色表示不同季度的盈利
Color[] seasonColor = { Color.Red, Color.Blue, Color.Green, Color.GreenYellow };
//这里使用GraphicsPath和Region
for(int i=0; i<4;i++)
{
//大的扇形
GraphicsPath pathPieMax = new GraphicsPath();
pathPieMax.AddPie(new Rectangle(50, 80, 200, 200), seasonAngle[i], seasonAngle[i + 1] - seasonAngle[i]);
//小的扇形
GraphicsPath pathPieMin = new GraphicsPath();
pathPieMin.AddPie(new Rectangle(50 + xSpace, 80 + xSpace, 200 - xSpace * 2, 200 - xSpace * 2), seasonAngle[i], seasonAngle[i + 1] - seasonAngle[i]);
//圆环=大的扇形-小的扇形
Region regoinAnnular = new Region(pathPieMax);
regoinAnnular.Exclude(pathPieMin);
//填充区域
g.FillRegion(new SolidBrush(seasonColor[i]), regoinAnnular);
//饼图中特别需要说明每个季度对应的颜色
g.FillRectangle(new SolidBrush(seasonColor[i]), new Rectangle(260, i * 30 + 20, 30, 20));
//标出每个季度
g.DrawString(seasonName[i], new Font("宋体", 12), new SolidBrush(Color.Black), new Point(300, i * 30 + 20));
}
}
//保存图片
private void btnSave_Click(object sender, EventArgs e)
{
SaveFileDialog sfd = new SaveFileDialog();
sfd.Filter = "JPG文件|*.jpg";
if (sfd.ShowDialog() != DialogResult.OK)
return;
string filename = sfd.FileName;
bmp.Save(filename);
}
运行结果如下图所示:
图17-34 生成环状图
学习更多vb.net知识,请参看vb.net 教程 目录
学习更多C#知识,请参看C#教程 目录