asp.net中用VML动态的画出数据分析图表详解【转】

在开发系统的时候有许多数据分析需要用图形的方式来表现出,这样更直观又清淅。如果我们使用高级去动态
生成统计图表的话不但编写起来非常困难,而且实用性不是很好,从美观的角度上讲也是很设计的。然而Microsoft公司提供了一个专们的矢量画图语言,这就是VML.

  如果要用VML去画静态页面的话那是比较好看也好操作,但实用性不是很高。但要是画出来的图所表示的数据
是从数据库里面读取下来的可以动态表示要统计的内容的话,那实用性就不言而喻了。

    最近我们也要做一个数据统计图表,我想如果能把VML画图做成一个控件那该多很,做自定义控件(本人才疏学浅)我不怎么会做,但我们公司有个.NET很牛 的人,我经常看到他重写.net里自带的控件使这些控件变得更好用,于是我也产生一种想法,看能不能把VML也嵌套进去.结果做的还是有点起色.下面跟大 家分享一下.

我是把VML图表用Lable控件显示出来的,Lable类添加了一些自定义属性.(在下面的代码里面有的属性还没有用到用与以后扩展)

页面代码如下
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="DrawTest.aspx.cs" Inherits="DrawTest" %>

<html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office"
    xmlns
="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    
<title>Page</title>
    
<style type="text/css">
v":*
{ behavior: url(#default#VML) }
o":*
{ behavior: url(#default#VML) }
.changeBg
{FILTER: progid:DXImageTransform.Microsoft.Gradient(gradientType=0,startColorStr=#FFFFFF,endColorStr=#9DCDFA);
}
.shadwDiv
{ width:600px; height:500px; text-align:center; filter : progid:DXImageTransform.Microsoft.DropShadow ( enabled=true  , color=#000 , offX=10 , offY=10 , positive=true ) ; }
</style>

</head>
<body>
    
<form id="form1" runat="server">
        
<div class="shadwDiv" >
            
<div style="padding-top: 30px; border: solid 2px #BBBBBB; background-color: #EFEFEF;
                width: 600px; height: 500px;"
>
                
<asp:VmlLabel ID="labTest3" runat="server" XWidth="400" YHeight="400" YPosition="50"
                    IsDrawVML
="True" BgDistance="10" EnableViewState="False" ZhuWidth="20" YItemWidth="40"
                    CssClass
="changeBg" BorderColor="White" BorderWidth="0px" LineColor="#4CAFFE"
                    XSign
="(年份)" YSign="(万/单位)" XPosition="50" IsZhuOrBing="Zhu" Redii="80"></asp:VmlLabel>
            
</div>
        
</div>
    
</form>
</body>
</html>

这里面的.<html>标签里的” xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office"

. v":*{ behavior: url(#default#VML) }

o":*{ behavior: url(#default#VML) }

是必不可少的.

下面是CS代码部分
private int xposition;
        
private int yposition;
        
private int xwidth;
        
private int yheight;
        
private bool isdrawVML;
        
private int bgdistance;//背景距离(斜线之间的垂直距离)
        private int zhuWidth;
        
private string lineColor;
        
private int yItemWidth;
        
private string xSign;//X轴标记
        private string ySign;//Y轴标记
        private List<VmlDataSource> datasource;
        
private IszhuOrBing isZhuOrBing;
        
private int radii;//画饼图要用的半径

        
public IszhuOrBing IsZhuOrBing
        {
            
get { return isZhuOrBing; }
            
set { isZhuOrBing = value; }
        }
        
#region 属性的定义
  
        
/// <summary>
        
/// 画饼图的半径
        
/// </summary>
        public int Redii
        {
            
get { return radii; }
            
set { radii = value; }
        }
        
/// <summary>
        
/// X轴的位置
        
/// </summary>
        public int XPosition
        {
            
get { return xposition; }
            
set { xposition = value; }
        }

        
/// <summary>
        
/// Y轴位置
        
/// </summary>
        public int YPosition
        {
            
get { return yposition; }
            
set { yposition = value; }
        }
        
//
        /// <summary>
        
/// X轴宽度
        
/// </summary>
        public int XWidth
        {
            
get { return xwidth; }
            
set { xwidth = value; }
        }
        
//
        /// <summary>
        
/// Y轴高度
        
/// </summary>
        public int YHeight
        {
            
get { return yheight; }
            
set { yheight = value; }
        }
        
/// <summary>
        
/// 是否是画VML图
        
/// </summary>
        public bool IsDrawVML
        {
            
get { return isdrawVML; }
            
set { isdrawVML = value; }
        }
        
/// <summary>
        
/// 背景距离(斜线之间的垂直距离)
        
/// </summary>
        public int BgDistance
        {
            
get
            {
                
return bgdistance;
            }
            
set
            {
                
if (value <= 0)
                    bgdistance 
= 10;
                
else
                    bgdistance 
= value;
            }
        }

        
/// <summary>
        
/// 柱子的宽度
        
/// </summary>
        public int ZhuWidth
        {
            
get { return zhuWidth; }
            
set { zhuWidth = value; }
        }

        
/// <summary>
        
/// 柱子的颜色
        
/// </summary>
        public string LineColor
        {
            
get { return lineColor; }
            
set { lineColor = value; }
        }
 
        
/// <summary>
        
/// Y轴方向的间距
        
/// </summary>
        public int YItemWidth
        {
            
get { return yItemWidth; }
            
set { yItemWidth = value; }
        }
        
/// <summary>
        
/// X轴标识
        
/// </summary>
        public string XSign
        {
            
get { return xSign; }
            
set { xSign = value; }
        }
        
/// <summary>
        
/// Y轴标识
        
/// </summary>
        public string YSign
        {
            
get { return ySign; }
            
set { ySign = value; }
        }
        
/// <summary>
        
/// 数据源(柱子高度)
        
/// </summary>
        public List<VmlDataSource> DataSource
        {
            
get { return datasource; }
            
set { datasource = value; }
        }

        
#endregion
判断是画柱图还是饼图.
   
//画VML图
        StringBuilder sbText = new StringBuilder();
        
string strHeight = "";
        
string strTop = "";

        
public void DrawVml()
        {

            
if (isdrawVML == true)
            {
//
                if (isZhuOrBing== IszhuOrBing.Bing)
                {
                    DrawBingImage();
                }
                
else if (isZhuOrBing == IszhuOrBing.Zhu)
                {
                    DrawZhuImage();
                }
            }
        }    
***IsZhuOrBing是一个自定义的枚举类型.

    
//柱子一升的脚本
        private string RegisterScript()
        {
            
string m_strScript = " <script language='javascript' type='text/javascript'>"
                    
+"var gao=0;"
                    
+"var ProcID=0;"
                    
+"var ProcName=null;"
                    
+"var i=0;"
                    
+"var HeightContent=document.getElementById('heightDataSource').value;"
                    
+"var arrHeight=new Array();"
                    
+"var arrTop=new Array();"
                    
+"arrHeight=HeightContent.split(',');"
                    
+"var showBar=null;"
                    
+"function AllPlay()"
                    
+"{"
                    
+"    i++;"
                        
                    
+"    gao=0;"
                    
+"   var procBarID='DataZhu'+i;"//;    
                    +"    Play(procBarID);    "
                    
+"}"
                    
+"function Play(procBarID)"
                    
+"{    "
                    
+"  var procBar=document.getElementById(procBarID);"
                    
+"  if(procBar!=null)"
                    
+"  {"
                      
+"      ProcName=procBar;"
                        
+"    PlaySub();"
                        
+"}"
                    
+"}"
                    
+"function PlaySub()"
                    
+"{"
                      
+"      var procBar=ProcName;"
                        
+"    showBar=document.getElementById('ShowData'+i);"
                          
+"  showBar.style.display='block';"
                            
+"gao++;"
                            
+"var ygao=parseInt(procBar.style.height);"
                            
+"var ytop=parseInt(procBar.style.top);"
                            
+"if(ygao<arrHeight[i-1])"
                            
+"{    "
                             
+"   procBar.style.top=ytop-gao;"
                              
+"  procBar.style.height=ygao+gao;"
                               
+" showBar.style.top=ytop-gao-25;"
                                
+"setTimeout(PlaySub,20);"
                            
+"}"
                            
+"else"
                            
+""
                              
+"  AllPlay();"
                            
+"}"
                    
+"}"
                    
+"</script>";
            
return m_strScript;
        }
上面的那段代码是要向客户端注册的JS脚本用于,画柱图时渐长效果的.
开始画VML图
//画柱图.
  public void DrawZhuImage()
        {
                 
base.Text = "";
                
//画Div
                
//sbText.Append("<div style=' border:solid 1px red;width:" + (xwidth+10) + "; height:" + (yheight+10) + ";;filter : progid:DXImageTransform.Microsoft.DropShadow ( enabled=true  , color=#66333333 , offX=4 , offY=4 , positive=true );'>");
                sbText.Append("<div id='PicDiv'  style='left:0;position:relative; top:0; width:" + xwidth + "; height:" + yheight + ";text-align:left;'>");
                
//base.Style.Value = "left:" + xposition + ";position:relative; top:" + yposition + "; width:" + (xwidth+20) + "; height:" + (20+yheight) + ";'";
                
//画X轴
                sbText.Append("<v:line id='Xzhou' from='0," + yheight + "' to='" + xwidth + "," + yheight + "'>"
                    
+ "<v:stroke  EndArrow='Classic'/></v:line><v:TextBox style='position:absolute; width:100px;top:" + (yheight + 10+ ";left:" + (xwidth) + "'>" + xSign + "</v:TextBox>");
                
//画X轴方向的线条
                int HaveData = ComputeX(xwidth);
                
//if (HaveData == 0)
                
//{
                
//    sbText.Append("</div>");
                
//    base.Text = sbText.ToString(0, sbText.Length);
                
//    return;
                
//}
                
//画Y轴
                sbText.Append("<v:line id='Yzhou' from='0," + yheight + "' to='0,0'><v:stroke  EndArrow='Classic'/><v:TextBox style=' width:100px;padding-left:10px;'>" + ySign + "</v:TextBox></v:line>");
                
//画Y轴方向的线条
                ComputeY(yheight);

                
//画DIV结束标记
                sbText.Append("</div>");
                
//画隐形文本框
                  sbText.Append("<input type='hidden' value='" + strHeight.Substring(0, strHeight.Length - 1+ "' name='heightDataSource' id='heightDataSource'/>");
                    sbText.Append(
"<input type='hidden' value='" + strTop.Substring(0, strTop.Length - 1+ "' name='topDataSource' id='topDataSource'/>");
                    
//给Lable控件的Text赋值
                base.Text = sbText.ToString(0, sbText.Length);
                Page.RegisterStartupScript(
"zhuup", RegisterScript());
        }
        
//
        private int ComputeX(int XWidth)
        {  
//柱子颜色数组
            string[,] ZColor = new string[62];
            ZColor[
00= "#666699"; ZColor[01= "#d9d9e5";
            ZColor[
10= "#00ff00"; ZColor[11= "#d1ffd1";
            ZColor[
20= "#ff0000"; ZColor[21= "#ffbbbb";
            ZColor[
30= "#ff9900"; ZColor[31= "#ffe3bb";
            ZColor[
40= "#33cccc"; ZColor[41= "#cff4f3";
            ZColor[
50= "#993300"; ZColor[51= "#ffc7ab";
            
            XWidth 
-= 10;//箭头下面的长度
            
            
int ColorIndex = 0;
            
double height = 0;
            
int Zhuposition = 0;
            
int Count = this.DataSource==null?XWidth:this.DataSource.Count;
            
int num=1;
            
//Count = 0;
            
//if (Count == 0)
            
//{
            
//    sbText.Append("<div style='font-size:14px;color:red;'>暂无数据</div>");
            
//    return 0;
            
//}
            int UnitLength = XWidth / Count;//计算单位长度
            foreach (VmlDataSource var in this.datasource)
            {
                
//画X轴下标
                sbText.Append("<v:line  from='" + num * UnitLength + "," + yheight + "' to='" + num * UnitLength + "," + (yheight + bgdistance) + "'>"
                    
+ "</v:line><v:shape style='left:" + (num * UnitLength -50+ ";top:" + yheight + ";'><v:textbox>" + var.Key + "</v:textbox></v:shape>");

                height 
= var.value;
                Zhuposition 
= ((num - 1* UnitLength + UnitLength / 2- (zhuWidth / 2);

                
if (ColorIndex >= ZColor.Length / 2)
                {
                    ColorIndex 
= 0;
                }
                
//sbText.Append("<v:rect id='DataZhu"+num+"' style='z-index:1; position:absolute; top:" + (yheight - height) + ";"
                  sbText.Append("<v:rect id='DataZhu"+num+"' style='z-index:1; position:absolute; top:" + yheight + ";"
                     
+ "left:" + Zhuposition + ";width:" + zhuWidth + ";height:0;' fillcolor='" + ZColor[ColorIndex, 0+ "'>"
                     
+ "<v:fill color2='" + ZColor[ColorIndex, 1+ "' rotate='t' type='gradient'/>"
                     
+ "<v:extrusion v:ext='view' backdepth='" + (2 * bgdistance) + "' color='" + ZColor[ColorIndex, 0+ "' brightness='0.2' on='t'/></v:rect>"
                     
+ "<v:TextBox id='ShowData"+num+"' style='position:absolute;display:none;z-index:2;top:" + (yheight - 20+ "; left:" + Zhuposition + "'>" + height + "</v:TextBox>");
                strHeight 
+= height + ",";
                strTop 
+= (yheight - height) + ",";
                ColorIndex
++
                num
++;
            }
            
return 1;
        }
        
//
        private void ComputeY(int YHeight)
        {
            YHeight 
-= 10;//箭头下面的长度
            if (yItemWidth > 0)
            {
                
int Units = YHeight / yItemWidth;//计算单位长度
                for (int i = 0; i <= Units; i++)
                {
                    
int areaLenght = yheight - (i * yItemWidth);
                    sbText.Append(
"<v:line from='0," + areaLenght + "' to='" + bgdistance + "," + (areaLenght - bgdistance) + "' color='" + lineColor + "'><v:stroke color='" + lineColor + "'/></v:line>"
                                 
+ "<v:line from='0," + areaLenght + "' to='-10," + areaLenght + "'>"
                                 
+ "</v:line><v:shape style='top:" + (areaLenght-10+ ";left:-50;'><v:TextBox>" + i * yItemWidth + "</v:TextBox></v:shape>"
                                 
+ "<v:line from='" + bgdistance + "," + (areaLenght - bgdistance) + "' to='" + xwidth + "," + (areaLenght - bgdistance) + "'><v:stroke color='" + lineColor + "'/></v:line>");
                }
                
//画Y轴平行的线
                sbText.Append("<v:line id='YzhouPX' from='" + bgdistance + "," + (yheight - bgdistance) + "' to='" + bgdistance + "," + ((yheight - (Units * yItemWidth)) - bgdistance) + "'><v:stroke color='" + lineColor + "'/></v:line>");
            }
        }
        
#endregion
        
#region 画饼图
        
private void DrawBingImage()
        {
            
base.Text = "";
            
//定义颜色
            string[] Colors = new string[6];
            Colors[
0= "#666699";
            Colors[
1= "#00ff00";
            Colors[
2= "#ff0000";
            Colors[
3= "#ff9900";
            Colors[
4= "#33cccc";
            Colors[
5= "#993300"
            
//开始标记
            
//sbText.Append("<div id='pic' style='position:absolute;'>");
            
//
            double TotalCount = 0;
            
foreach (VmlDataSource vd in datasource)
            {
                TotalCount 
+= vd.value;
            }
            
//计算单位数字的角度
            double UnitAngle = 360 / TotalCount;
            
//存储画下弧度的起点
            double TempUnitArc=0.00;
            
//存储前面已有的角度
            double TempUnitAngle=0.00;
            
int i = 0;
            
string ColorInfo = "";
            sbText.Append(
"<div id='PicDiv'  style='left:0;position:relative; top:0; width:" + xwidth + "; height:" + yheight + ";text-align:left; font-size:12px; border:solid 1px red;'>");
            sbText.Append(
"<div style='left:" + (radii+xposition) + ";position:relative; top:" + (radii+yposition) + ";'>");
            sbText.Append(
"<v:TextBox id='msgShow' inset='5pt,5pt,5pt,5pt' style='width:" + Colors[i] + ";position:absolute;z-index:2;display:none;background-color:red; font-size:12px;'></v:TextBox>");
            
foreach (VmlDataSource vd in datasource)
            {

                
//计算弧度
                double UnitArc = (vd.value * UnitAngle + TempUnitAngle) * Math.PI / 180;
                
//计算开始和结束位置
                double sx = Math.Round(Math.Cos(TempUnitArc) * radii);//Math.Sign(
                double sy = Math.Round(Math.Sin(TempUnitArc) * radii);
                
double ex = Math.Round(Math.Cos(UnitArc) * radii);
                
//double aa = Math.Sin(UnitArc) * radii;
                
//double bb = Math.Round(Math.Sin(UnitArc) * radii,4);
                double ey = Math.Round(Math.Sin(UnitArc) * radii);
                
//所占百份比
                double rate = Math.Round(vd.value * 100 / TotalCount, 2);
                
//开始画图
                sbText.Append("<v:shape ID='ccc' style='position:absolute;width:200px; height:200px;'coordsize='200,200' fillcolor='#" + Colors[i] + "' filled='t' strokecolor='#FFFFFF'");
                sbText.Append(
" path='m0,0 l" + sx + "," + sy + " ar-" + radii + ",-" + radii + "," + radii + "," + radii + "," + ex + "," + ey + "," + sx + "," + sy + " l0,0 x e'");
                sbText.Append(
"title='名称:" + vd.key + ""r数量:" + vd.value + ""r所占比例:" + rate + "%'></v:shape>");
                
//给临时变量赋值
                TempUnitAngle += vd.value * UnitAngle;
                TempUnitArc 
= UnitArc;
                
//显示颜色说明框
                ColorInfo += "<div style='position:relative;padding-top:3px;padding-bottom:3px;' title='所占比例:" + rate + "%' onMouseOver='this.style.border=""solid 1px #D38F61""' onMouseOut='this.style.border=""none""'>"
                    
+ "  <v:rect style='position:relative;width:20px;height:20px;' fillcolor='" + Colors[i] + "'/>" + vd.key + "" + vd.value + "  (" + rate + "%)</div>"
                    ;
                
//如果颜色遍历完了,还有数据要显示那就重新再一次遍历。占:" + (Math.Round(vd.value * 100 / TotalCount, 2)) + "%
                i = (i >= Colors.Length ? 0 : ++i);
            }
            sbText.Append(
"</div>");
            sbText.Append(
"<div style='border:solid 1px #000;padding:10px;margin-right:10px;position:relative;left:" + (2 * radii + 20 + xposition) + "px;top:10px;width:" + (xwidth - (2 * radii + 40 + xposition)) + ";'>");
            
//sbText.Append("<div style='padding-left:10px;width:" + (xwidth - (2 * radii +30+ xposition)) +";height:auto;'>");
            
//总数
            sbText.Append("<div style='background-color:#000;color:#fff;'>总数据:"+TotalCount+"</div>");
            sbText.Append(ColorInfo);
            sbText.Append(
"</div></div>");
            
this.Text = sbText.ToString();
        }
        
#endregion
        
/// <summary>
        
/// 控件加载时画图
        
/// </summary>
        
/// <param name="e"></param>
        protected override void OnLoad(EventArgs e)
        {
            
base.OnLoad(e); // Page.RegisterStartupScript("HelloWord", "<script>alert('BBB');</script>");
             DrawVml();
            
//Page.RegisterStartupScript("", "<script>alert('aaa');</script>");
        }
        
protected override void OnPreRender(EventArgs e)
        {
            
base.OnPreRender(e);
            Page.RegisterStartupScript(
"""<script>AllPlay();</script>");
        }
    }
*注意里面的数据源类型和枚举在下面定义.
  
/// <summary>
    
/// 画图时用的数据源
    
/// </summary>
    public class VmlDataSource
    {
        
public VmlDataSource()
        {

        }

        
public string key;
        
public double value;
        
/// <summary>
        
/// 键值
        
/// </summary>
        public string Key
        {
            
get { return key; }
            
set { key = value; }
        }
        
public double Value
        {
            
get
            {
                
if (this.value.GetType() != Type.GetType("System.Int32"))
                    
return 0;
                
else
                    
return this.value;
            }
            
set
            {
                
this.value = value;
            }
        }

    }
    
/// <summary>
    
/// 枚举画图的类型
    
/// </summary>
    public enum IszhuOrBing
    {
        
//
        Zhu=0,
        
//
        Bing=1
    }
}
整个过程就是这样了,不过这只是个粗略的过程里面还很多的BUG没有解觉掉.画数据统计的图表完成了.
效果图如下.

饼图为:

posted on 2008-08-19 16:36  草原和大树  阅读(792)  评论(0编辑  收藏  举报