一、    引言

在许多B/S结构的信息管理软件中,利用在线统计图帮助用户观察、分析各种各样的数据,要比纯粹依靠统计表格更直观,更形象。然而,与窗体类程序相比,在WEB中实现统计图功能存在着一些差别,如果利用现有的ActiveX统计图控件,将其直接嵌入网页前端脚本内,那么必须要求客户端下载安装相应的控件资源。而且容易导致系统安装维护、分布式数据访问等性能上的问题。区别于这种“胖客户”的方法,另外一种做法是,在服务器端通过数据运算,动态生成统计图像后,再向前端发布。显然,后一种方法是一种较好的模式,它对客户端的软件配置要求很低,只要前端安装浏览器即可。

目前,已经有一些ASP.NET技术文章中探讨了web图表问题,一般都是先运用GDI+绘制统计图表,再利用Response对象将图像流发送到客户端浏览器。但笔者认为,对于大多数普通编程人员来说,这种开发方式对开发者的图形编程技术功底要求很高,而且费时费力。因而,在短时间内很难开发出表现力强,具有一定的通用性的专业WEB控件。那么有没有一些近在咫尺的技术可以让我们很快解决这一问题呢?笔者认为OWC组件是一个很好的选择。OWC (Office Web Componsents)是一组随微软OFFICE一起发布的COM控件的集合,其中Chart(图表)组件类似于Excel中的图表制作功能,能支持几十种图表类型,其非凡之处在于它们不仅可以在诸如WEB页面、VB表单等控件容器中使用,而且也可以在内存中作为不可见对象使用。下文笔者将综合几种技术阐述如何利用OWC组件开发WEB统计图表控件。

二、    设计原理

实现的基本思路是将OWC Chart组件运行于服务器上,运用其丰富的编程接口导入数据源,设置图表格式和类型后,用ExportPicture方法导出临时GIF文件,再结合ASP.NET技术,将此GIF图像返回至客户端浏览器中规定位置,最后,删除临时GIF文件,回收资源。

笔者在开发过程中发现,真正的难点并不在于怎么使用OWC的接口,而在于怎样将图形发送至客户端浏览器?怎样将以上思路转换成自定义Web Control?又如何删除过期的临时GIF文件?下面我们将一步一步的构建自定义WebChart控件。

三、    WebChart控件的开发步骤

1.      建立工程WebChart

运行Visual Studio.net,选择文件->新建->项目->Visual C#项目->Web控件库,如图1,建立工程名为WebChart,建立类文件WebChart.cs, 选择菜单项目->添加引用弹出对话框如图2。

图1 新建WebChart组件工程

图2 引用OWC资源

按“浏览”按钮找到OWC10.DLL后,选择“确定”,完成WebChart对OWC资源的引用。值得注意的是,OWC组件的各个版本有所不同,为了帮助正确安装和使用,现将其罗列于下表:

组件主要文件

对应的Office版本

机器上的一般安装位置

MSOWC.DLL

OFFICE 2000

/Program Files/Microsoft Office/Office

OWC10.DLL

OFFICE XP

/Program Files/Common Files/Microsoft Shared/

Web Components/10

OWC11.DLL

OFFICE 2003

/Program Files/Common Files/Microsoft Shared/

Web Components/11

2.      继承WebControl基类,对ChartSpace类进行封装

双击WebChart.cs查看代码,进入编辑状态,建立命名空间WebChartCtl,编辑如下代码,并参看其中注释内容:

using  System;
using  System.Web.UI;
using  System.Web.UI.WebControls;
using  System.ComponentModel;
using  System.Drawing;
using  System.Drawing.Imaging;
using  System.Web;
using  System.Xml ;
using  System.IO;
using  OWC = Microsoft.Office.Interop.Owc11;   
using  System.Runtime.InteropServices;
using  System.Data;
using  System.Data.OleDb;


namespace  WebChartCtl

... {
     [DefaultProperty(""),   ToolboxData("<{0}:WebChart runat=server></{0}:WebChart>")]
    public class WebChart : System.Web.UI.WebControls.WebControl   

    ...{      
       private ChartSpaceClass m_ChartSpace;
       //构造函数,调用基类构造函数来指定服务器控件应该在浏览器输出一个<IMG></IMG>标记
   public WebChart():base("IMG")
       ...{

           try...{

              m_ChartSpace=new ChartSpaceClass();
              m_ChartSpace.DisplayFieldButtons=false;
              m_ChartSpace.DisplayFieldList=false;
           }catch...{}

       }

       //显露OWC组件的图形空间对象
   public ChartSpaceClass ChartSpace
       ...{
           get...{return m_ChartSpace;}
       }

       //导出临时GIF图形文件

    private string ExportGifFile()
       ...{
           string filename=GetTemp();//产生唯一的文件名
     //导出GIF文件
     object o;

         if (m_ChartSpace.DataSource==null)// || m_ChartSpace.ConnectionString==null || m_ChartSpace.ConnectionString=="")

              filename="__NULL";

           else

           ...{

               o=this.m_ChartSpace.GetPicture("gif",(int)this.Width.Value,(int)this.Height.Value);

              this.Page.Cache[filename]=o;

           }
 

           this.m_ChartSpace.Clear();//释放图表对象使用的资源
     if (m_ChartSpace!=null)  
           ...{

              if (m_ChartSpace.DataSource!=null)
                  Marshal.ReleaseComObject(m_ChartSpace.DataSource);
              Marshal.ReleaseComObject(m_ChartSpace);
           }

 
           this.m_ChartSpace=null;
           GC.Collect();
           GC.WaitForPendingFinalizers();

           return filename;

       }

       //产生唯一的文件名
   private string GetTemp()
       ...{

           System.DateTime T=System.DateTime.Now;
            return "T"+T.Date.ToShortDateString()+T.Hour.ToString()+T.Minute.ToString()+T.Second.ToString()+T.Millisecond.ToString()+".gif";T"+T.Date.ToShortDateString()+T.Hour.ToString()+T.Minute.ToString()+T.Second.ToString()+T.Millisecond.ToString()+".gif";

       }

}//end of namespace

在以上的命名空间WebChartCtl中,首先利用基类WebControl的缺省构造函数确定该控件在前端将显示为<IMG>元素,接着封装ChartSpace对象属性是为了将OWC Chart组件的编程接口委派给WebChart组件,从而保证可以再利用OWC所有编程特性,ExportGifFile是一私有方法,用于将ChartSpace中图表输出成临时的图形文件,为后面向浏览器传递图形做好准备。接下来,覆盖基类AddAttributesToRender方法,为前端<img>控件设置图形源属性Src。

在同一命名空间WebChartCtl中插入以下代码:

     protected   override   void  AddAttributesToRender(HtmlTextWriter writer)
        ... {
           base.AddAttributesToRender (writer);
           string src,filename;
           try
           ...{   
              filename=this.ExportGifFile();//产生临时图形文件
      //设置<img>图形源属性Src,利用参数filename传递临时图形文件名
        src="handler.aspx?filename=" +this.Page.Server.UrlEncode(filename);
              writer.AddAttribute(HtmlTextWriterAttribute.Src,src );
           }
          catch...{}
       }

以上代码中handler.aspx是临时图形文件的逻辑载体,需要根据URL参数表中filename的取值,负责处理请求,产生图形流,并发送至前端。

3.      利用IHttpHandler 接口捕获Http请求

以上过程通过设置src属性向WEB服务器请求处理,那么,是否必须要设计一个handler.aspx文件,并在其中写入代码来处理客户端提交的Http请求呢?其实,并不需要这样做,否则我们的封装行为就毫无意义了。ASP.NET提供了IHttpHandler接口,可以自定义 HTTP 处理程序来处理特定的、预定义类型的 HTTP 请求,IHttpHandler位于命名空间System.web中。

使用IhttpHandler必须实现ProcessRequest方法、IsReusable属性,在namespace WebChartCtl中建立公有类RequestHandler用于捕获Http请求,插入代码如下:

     public   class  RequestHandler:IHttpHandler
     ... {

       //处理请求
    public void ProcessRequest(HttpContext context)

        ...{

             string filename=context.Request.QueryString["filename"];
            context.Response.ContentType="image/gif";

            if (filename=="__NULL")
               DrawBmp(context.Response.OutputStream);
            else
            ...{
               object o=context.Cache[filename];
               byte[] s=(byte[])o;
               context.Response.BinaryWrite(s);
               context.Cache.Remove(filename);
            }     

        }

        private void DrawBmp(System.IO.Stream st)

       ...{
           Bitmap bitmap = new Bitmap(1,1);
           Graphics graphics = Graphics.FromImage(bitmap);
           graphics.Clear(Color.White);
//         graphics.DrawString("版权所有  (C) 2001-2020",new Font("Courier New",11,FontStyle.Underline),Brushes.Black,new PointF(5,5));
           bitmap.Save(st,ImageFormat.Gif);
       }

       //获取一个值,该值指示其他请求是否可以使用 IHttpHandler 实例
    public bool IsReusable

        ...{

            get...{return true;}

        }

    }

在ProcessRequest中,利用WriteFile将ExportPicture方法生成的临时GIF文件直接写入 HTTP 内容输出流。

4.      对Web.config文件的设置

编译以上程序生成WebChart.dll,为了能让IhttpHandler起作用,还必须在测试工程的web.config文件中加入以下配置信息:

<configuration>

<system.web>

<httpHandlers>

<add verb="*" path="handler.aspx" type="WebChartCtl.RequestHandler,WebChart" />

</httpHandlers>

</configuration>

其中path属性值为请求文件名,type属性值由组件命名空间WebChartCtl、IhttpHandler接口实现类名RequestHandler、组件文件名WebChart组成的字符串:"WebChartCtl.RequestHandler,WebChart"。显然,这种配置行为给组件WebChart的使用带来了一些不便,考虑到web.config是XML文档,我们完全可以用System.xml来自动完成对web.config的配置,在类WebChart中插入代码如下:

        // 在WEB.CONFIG中配置HttpHandler

        private   void  RegesterHttpHandler()

        ... {

           string spath="handler.aspx";

           string stype="WebChartCtl.RequestHandler,WebChart";

           string filename=this.Page.Request.PhysicalApplicationPath + @"web.config";

 

           XmlNode root, e,HttphandlerNode;

           XmlDocument   xmldoc= new XmlDocument();

           xmldoc.Load(filename);

 

           root=FindElement(xmldoc.DocumentElement,"system.web");

           if (root==null) return;

 

           e=FindElement(root,"httpHandlers");

           if (e==null)

           ...{

              HttphandlerNode=xmldoc.CreateElement("httpHandlers");

    this.CreateAddElem(xmldoc,(XmlElement)HttphandlerNode,spath,stype);

              root.InsertBefore(HttphandlerNode,root.FirstChild);

           }

           else

              SeekAddElemAndCreateIt(xmldoc,e,spath,stype);

           xmldoc.Save(filename);

       }

 

        // 子过程,寻找指定的节点

        private  XmlNode FindElement(XmlNode elem, string  elemName)

        ... {

           XmlNode findit=null;

           foreach(XmlNode e in elem.ChildNodes)

           ...{

              if(e.Name.ToLower()==elemName.ToLower())

              ...{

                  findit=e;

              }

           }

           return findit;

       }

 

        // 查看是否包含<add>节,如果配置信息不正确修正它

        private   void  SeekAddElemAndCreateIt(XmlDocument xmldoc,  XmlNode HttphandlerNode, string  spath, string  stype)

        ... {

           XmlNode findit=null;

           bool b=false;

 

           foreach(XmlNode e in HttphandlerNode.ChildNodes)

           ...{

              if(e.Name.ToLower()=="add")

              ...{

                  b=(e.Attributes["verb"].InnerXml=="*")&&

    (e.Attributes["path"].InnerXml.ToLower()==spath.ToLower())&&

    (e.Attributes["type"].InnerXml.ToLower()==stype.ToLower());

                  if (b) ...{findit=e;break;}

              }

           }

           if (!b)    CreateAddElem(xmldoc,(XmlElement)HttphandlerNode,spath,stype);

       }

 

        // 创建<add>节的配置信息

        private   void  CreateAddElem(XmlDocument xmldoc,XmlElement HttphandlerNode, string  spath, string  stype)

        ... {

           XmlElement subnode;

           subnode=xmldoc.CreateElement("add");

           subnode.SetAttribute("verb","","*");

           subnode.SetAttribute("path","",spath);

           subnode.SetAttribute("type","",stype);

           HttphandlerNode.InsertBefore(subnode,HttphandlerNode.FirstChild);

       }

 

        // 在Web页初始化时,对web.config中填入正确的设置

        protected   override   void  OnInit(EventArgs e)

        ... {

           base.OnInit (e);

           this.RegesterHttpHandler();

       }

四、    WebChart控件的应用实例

至此,已完成所有组件代码的编写,将工程WebChartCtl编译为WebChart.dll,即可测试应用。步骤如下

1)      在磁盘上建立名为TestChart的文件夹,并利用IIS将其设为虚拟目录,如图3,在Visual Studio.net 开发环境中选择文件->新建->项目->Visual C#项目->ASP.NET Web应用程序,并和WebChartCtl一样,引用OWC10.DLL(如图2),选右键菜单,将测试表单WebForm1设为“起始页”,并确保Web.Config文件可读写。

图3 建立测试工程

图4 向工具箱中添加WebChart.dll

2)      从工具箱选项夹“WEB窗体”中拖动Lable,向WebForm1中添加控件Lable1, Text属性设为“WebChart运行实例”;

3)      在工具箱上,右键选择“添加/移除项”,弹出对话框如图4,选择“浏览”找到“…./WebChartCtl/bin/Debug/WebChart.dll”,将控件WebChart显示于工具箱中,拖动WebChart图标,为WebForm1中添加控件WebChart1

4)      双击WebForm1,编辑以下代码:

using  OWC10;

private   void  Page_Load( object  sender, System.EventArgs e)
       {
            // 创建图表空间实例
      OWC10.ChartSpaceClass Chs = this .WebChart1.ChartSpace;

// 设置数据库连接串以及SQL数据命令
      Chs.ConnectionString = @" Provider=SQLOLEDB;Data source=(local);Initial Catalog=northwind;User Id=sa;Password= " ;
           Chs.CommandText = @" SELECT  YEAR(ShippedDate) AS YEAR, MONTH(ShippedDate) as month,SUM(Subtotal) AS Total FROM [Summary of Sales by Year]  GROUP BY YEAR(ShippedDate), MONTH(ShippedDate) " ;

// 设置图表的系列,分类轴,数据轴 
      Chs.SetData(ChartDimensionsEnum.chDimSeriesNames, 0 , " YEAR " );     
           Chs.SetData(ChartDimensionsEnum.chDimCategories, 0 , " MONTH  " );    
           Chs.SetData(ChartDimensionsEnum.chDimValues, 0 , " Total " );

// 设置标题
      Chs.HasChartSpaceLegend = true ;
           Chs.HasChartSpaceTitle = true ;
           Chs.ChartSpaceTitle.Caption = " Summary of Sales by Year " ;
           ChChart Cht = Chs.Charts[ 0 ];

// 设置图表类型,缺省为直方图,显现以下一行代码即为立体饼图
       // Cht.Type=ChartChartTypeEnum.chChartTypePie3D;
            // 当图表类型为ChartChartTypeEnum.chChartTypePie3D时,

// 以下代码必须去除,否则代码无法执行
ChAxis X,Y;
Y = Cht.Axes[ChartAxisPositionEnum.chAxisPositionLeft];
           Y.HasTitle = true ;
           Y.Title.Caption = " Total " ;
           Y.NumberFormat = " Currency " ;

X = Cht.Axes[ChartAxisPositionEnum.chAxisPositionCategory];
           X.HasTitle = true ;
           X.Title.Caption = " Year " ;
       }

运行工程,结果如图5,如果根据代码注释内容修改代码,运行结果如图6。以上代码运行环境为Windows 2000 Professional,IIS5.0,Visual Studio.NET 2003, Office XP, SQL Server 2000。

图5 各年度销售业绩对比直方图

图6 1996年各月销售业绩饼图

五、    总结

以上内容综合了自定义 WebControl 技术、 XML 技术、 IHttpHandler 接口使用方法,对 OWC10 进行了重新封装,为实现 WEB 统计图表功能提供了一个非常实用的解决方案。笔者在 WebChart 中只对 OWC10 中的接口 ChartSpace 进行了封装,目的在于不丢失 OWC10 编程时的灵活性 , 有兴趣的读者可以进一步研究 OWC 的帮助文档,设计出贴近个性需求的接口。

ASP.NET中制作WEB统计图控件的捷径相关推荐

  1. 在asp.net中为Web用户控件添加属性和事件

    在90年代初,Microsoft为Web程序员提供的 Active Server Pages(ASP)革命性地改变了Web的编程.它可以利用十分易用的模型在Web服务器上动态生成HTML,并且很容易的 ...

  2. Asp.net中使用WEB编辑控件FCKEditor

    先要下载两个文件: FCKEditor(JS文件),FCKEdito.net(COM+组件 DLL文件) 官方网站:http://sourceforge.net FCKEdiotr的安装与配置: 把F ...

  3. Asp.Net 中使用客户端Activex控件需要注意的事情

    案例:Asp.Net +VB制作的Activex控件 操作系统:Windows2003 +Sp1 现象1:通过     <OBJECT classid="CLSID:132A80F5- ...

  4. ASP.NET中基于ExtJS的控件Ext.net的使用

    ExtNet是国外一家公司开发的基于ExtJS的ASP.NET控件库. 1.新建项目Layout_Anchor 2.引用Ext.Net.dll 3.在Web.config文件中的web节点加入 < ...

  5. 【小沐学C#】WPF中嵌入web网页控件(WebBrowser、WebView2、CefSharp)

    文章目录 1.简介 1.1 WPF简介 1.2 WPF 体系结构 1.3 WPF入门开发 2.WebBrowser 2.1 WebBrowser特点 2.2 WebBrowser常用的属性.方法和事件 ...

  6. Asp.net中开发使用ActiveX控件

    从网上看来一篇网页中使用ActiveX的文章,正好用到.整理了一下以作备用 一 使控件实现 IObjectSafety 接口 1. 从 Visual Basic 6.0 光盘获取 OLE 自动化类型库 ...

  7. ASP.NET中AJAX的UpdatePannel控件的用法

    ScriptManager和UpdatePanel控件联合使用可以实现页面异步局部更新的效果.其中的UpdatePanel就是设置页面中异 步局部更新区域,它必须依赖于ScriptManager存在, ...

  8. html asp textbox,ASP.NET中 TextBox 文本输入框控件的使用方法

    TextBox控件又称文本框控件,为用户提供输入文本的功能. 1.属性 TextBox控件的常用属性及说明如表1所示. 表1 TextBox控件常用属性及说明 属性 说明 AutoPostBack 获 ...

  9. 在ASP.NET中利JavaScript实现控件的聚焦

    在Windows应用程序中很容易控制控件的聚焦,但是在ASP.NET中并没有提供这样的功能,但是我们同样可以实现这样的功能,这篇文章就讲述了通过JaveScript实现在页面上某一特定控件获得焦点的功 ...

最新文章

  1. 数据结构之权值(在吊挂中的实际应用)
  2. 数学:统计:基本概念
  3. rsync 端口更换(默认873)
  4. VTK:多块数据集用法实战
  5. Django框架里的MVC思想
  6. 苏州,遇见NXP痞子衡
  7. mysql 过滤单引号_python实现mysql的单引号字符串过滤方法
  8. python 提取元组中的值_Python中的sqlite3提取与元组中的值相对应的条目 - python
  9. 黑客利用未修复的 Atlassian 服务器攻击电信运营商和 ISP
  10. LeetCode 230. 二叉搜索树中第K小的元素(递归)
  11. 中级职称计算机应用考哪些,中级职称计算机考试大纲
  12. c语言窗口炸弹代码,C语言实现宾果消消乐
  13. python 等值线插值,CartoPy等值线的插值方法
  14. 【毕业设计】树莓派单片机墨水屏电子日历系统 - 物联网 嵌入式
  15. Seagull PHP框架学习教程之二
  16. 这个5.10阿里日不一般!造(躁)起来~淘系的年轻人们!
  17. 智能停车场ARM工控主板应用
  18. 超好看的3D烟花代码(html+css+js)带音乐
  19. SeaJS 是什么?
  20. 时间基础概念及Linux中的时间函数

热门文章

  1. 更多查询模式--mysql学习笔记
  2. 二维码的原理以及怎么生成二维码
  3. 真的防弹吗?AEGIS防弹手机贴膜真枪实测
  4. Java笔记(韩顺平Java基础12-14章)
  5. java jsonresult_java web中统一结果返回封装类JsonResult
  6. java中引用类型和基本类型的区别
  7. 并发编程系列之volatile关键字详解
  8. Python打印各种图形
  9. word(2010)宏使用-批量提取word数据
  10. 在java中班级的表示方法_java班级类起名怎么起-javaclass怎起名