版權聲明:本文為博主原創文章,轉載請在顯著位置標明本文出處以及作者網名,未經作者允許不得用于商業目的。
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#教程 目錄