前两天web项目中遇到要绘一个半饼图,图表控件中没有这样的图,就用gdi+简单的绘了一个。
实现思路:
基于xml进行半饼图的数据提供,格式如下:
<imagedraw><imagedata data=\"100\" color=\"#1B3C72\" /><imagedata data=\"200\" color=\"#1B3Cad\" /><imagedata data=\"200\" color=\"#ffccdd\" /></imagedraw>
半饼图中每个区域可以分别指定颜色。每个区域的大小由提供数据的data属性值决定。每个区域的角度大小为:
iCurrAngle = Convert.ToInt32(当前区域数据 / 所有区域数据和 * 180);,因为是半饼图所以*180度。对于每个区域上绘制文字坐标,使用放大半径求圆弧所在点的位置的方式。
具体实现如下:
BaseDraw:绘图基类
PartPieDraw:绘半饼图实现类,主要是 RenerImage方法
using System;
using System.Collections;
using System.Drawing;
using System.Drawing.Drawing2D;
/// <summary>
/// BaseDraw 的摘要说明
/// </summary>
public class BaseDraw
{
private int imageWidth = 0;
private int imageHeight = 0;
private string xmlData = "";
public BaseDraw()
{
//
// TODO: 在此处添加构造函数逻辑
//
}
public int ImageWidth
{
get { return this.imageWidth; }
set { this.imageWidth = value; }
}
public int ImageHeight
{
get { return this.imageHeight; }
set { this.imageHeight = value; }
}
public string XmlData
{
get { return this.xmlData; }
set { this.xmlData = value; }
}
public byte[] Draw()
{
System.Drawing.Bitmap image = new System.Drawing.Bitmap(this.imageWidth,this.imageHeight);
Graphics g = Graphics.FromImage(image);
this.RenerImage(g);
System.IO.MemoryStream ms = new System.IO.MemoryStream();
image.Save(ms, System.Drawing.Imaging.ImageFormat.Gif);
byte[] buffer = ms.ToArray();
g.Dispose();
image.Dispose();
return buffer;
}
public virtual ArrayList ParseXmlData()
{
return null;
}
public virtual void RenerImage(Graphics g)
{
}
}
using System;
using System.Collections;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Xml;
/// <summary>
/// PartPieDraw 的摘要说明
/// </summary>
public class PartPieDraw:BaseDraw
{
public PartPieDraw()
{
//
// TODO: 在此处添加构造函数逻辑
//
}
public override void RenerImage(System.Drawing.Graphics g)
{
ArrayList dataList = this.ParseXmlData();
//开始计算总的数值
double sumValue = 0.00;
foreach (ImageData data in dataList)
{
sumValue += Convert.ToDouble(Math.Round(data.DrawData, 2));
}
sumValue = Convert.ToDouble(Math.Round(sumValue, 2));
int iSumAngle = 180; //总要绘的角度
int iCurrAngle = 0; //当前角度
int iPosAngle = 180; //当前角度的基值
int iCurrSumAngle = 0;
double dCurrBfb = 0;
//定义半图所在的矩形区域大小,画缓的位置占整个图的2/3
int iAreaX = Convert.ToInt32((this.ImageWidth - Convert.ToInt32(this.ImageWidth * 2 / 3)) / 2);
int iAreaY = iAreaX;
int iAreaWidth = Convert.ToInt32(this.ImageWidth * 2 / 3); //长边
int iAreaHeight = iAreaWidth;
double r = iAreaWidth / 2; //半径
double rx = Convert.ToDouble(iAreaX) + r; //圆心x
double ry = Convert.ToDouble(iAreaY) + r; //圆心y
//重设置半径
r += 20;
g.Clear(Color.White); //清除背景
g.SmoothingMode = SmoothingMode.AntiAlias; //消除绘的时候的锯齿
g.DrawRectangle(new System.Drawing.Pen(Color.Black, 1), 0, 0, this.ImageWidth, this.ImageHeight);
for (int i = 0; i < dataList.Count; i++)
{
ImageData imageData = (ImageData)dataList[i];
dCurrBfb += imageData.DrawData / sumValue * 100;
dCurrBfb = Convert.ToDouble(Math.Round(dCurrBfb, 2)); //格式化两位小数
if (i < dataList.Count - 1)
{
iCurrAngle = Convert.ToInt32(imageData.DrawData / sumValue * iSumAngle);
}
else
{
iCurrAngle = 360 - iPosAngle;
}
g.SmoothingMode = SmoothingMode.AntiAlias; //消除绘的时候的锯齿
g.FillPie(new SolidBrush(imageData.DrawColor), iAreaX, iAreaY, iAreaWidth, iAreaHeight, iPosAngle, iCurrAngle);
g.DrawPie(new Pen(new SolidBrush(imageData.DrawColor)), iAreaX, iAreaY, iAreaWidth, iAreaHeight, iPosAngle, iCurrAngle);
//计算绘数字
//求圆弧所在点的位置,有偏差
iCurrSumAngle += iCurrAngle;
double pointX = Math.Cos(iCurrSumAngle * Math.PI / 180) * r; //对边,
double pointY = Math.Sin(iCurrSumAngle * Math.PI / 180) * r; //邻边
pointY = ry - pointY;
pointX = rx - pointX;
Font strFont=new Font("宋体", 12, FontStyle.Italic);
//开始测试绘字符需要的位置
SizeF sizeF = g.MeasureString(dCurrBfb.ToString(), strFont);
pointX = pointX - sizeF.Width / 2;
pointY = pointY + sizeF.Height / 2;
g.DrawString(dCurrBfb.ToString(), strFont, new SolidBrush(Color.Red), Convert.ToInt32(pointX), Convert.ToInt32(pointY) - 20);
iPosAngle += iCurrAngle;
iCurrAngle = 0;
}
}
public override ArrayList ParseXmlData()
{
//解析数据格式
//<imagedraw>
// <imagedata data="100" color="#fff000">
//</imagedraw>
ArrayList dataList = new ArrayList();
System.Xml.XmlDocument doc = new XmlDocument();
doc.LoadXml(this.XmlData);
System.Xml.XmlElement root = doc.DocumentElement;
foreach (System.Xml.XmlNode node in root.ChildNodes)
{
string sData = node.Attributes["data"].Value;
string sColor = node.Attributes["color"].Value;
dataList.Add(new ImageData(sData, sColor));
}
return dataList;
}
public class ImageData
{
private double drawData = 0.00;
private Color drawColor=Color.White;
public ImageData(string data, string htmlColor)
{
this.drawData = Convert.ToDouble(data);
drawColor = System.Drawing.ColorTranslator.FromHtml(htmlColor);
}
public double DrawData
{
get { return this.drawData; }
}
public Color DrawColor
{
get { return drawColor; }
}
}
}