一、概述
在开发应用程序时,经常会遇到必须提供交互式图表的情况。例如,你可能在开发一个管理销售和产品数据的应用程序,数据保存在
SQL Server
数据库上,应用程序允许用户添加数据、更新现有数据,但除了这些功能之外,客户还要求应用程序能够用饼图、柱形图或
XY
散点图的形式直观地描述数据。
在
Windows
桌面应用程序中,这类要求从来不成为问题,可供选用的图形库和绘图组件实在太多了。但对于
Web
应用程序,问题就变得复杂多了。要在
Web
应用中绘制图表,可供选择的办法包括:
■
客户端:
利用各种
ActiveX
组件,
Web
浏览器内完全有可能达到
“
丰富
”Windows
客户程序那样的功能。缺点是客户端的设置复杂化,要求发布客户端软件,通常按照每客户端的方式计算许可证费用。另外,非
MS Windows/IE
的客户端一般难以运行。
■
服务器端:
利用
Web
服务器上运行的服务器端代码,动态地生成图表,然后以
GIF
或
JPG
图形的形式发送给客户端。这种办法的优点是,客户端只需要一个标准的浏览器。与客户端技术相比的缺点是,图形的交互能力差(除非向服务器提交新的请求,否则就不能缩放、滚动)。许多地图网站(例如
Mapquest.com
)大量地运用了这一技术。注意,地图图形不是保存在
Web
服务器上,而是用户发出一个请求时动态从地图数据库生成。
本文主要讨论如何利用服务器端的图表绘制技术在
ASP.NET Web
页面中提供图形功能
二、设置图表引擎
如果要在
ASP.NET
应用程序中绘制图表,必须要有一个合适的图表引擎。
ASP.NET
有一个内建的图形工具库,即
System.Drawing
名称空间的
GDI+
,可以用来创建简单的饼图、柱形图、折线图等,不过它属于低级的
API
,算不上绘制图表的引擎,特别是不适合绘制复杂的图表。
本文要讨论的主角是
OWC
,即
Office Web Components
,或者
“Office Web
组件
”
。按照微软的定义,
OWC
是一种
“
将类似
Office
的功能扩展到
Web
的微软技术
”
。它可以在客户端使用,例如我们将
Excel
工作表保存为
Web
页面时就要用到,利用它可以方便地将交互式电子表格和图表发布到
Web
页面。同时,
OWC
也是一个优秀的服务器端图表引擎,具有与
MS Excel
同样强大的图表绘制能力。
三、
OWC
的许可证问题
如果你曾经用过版本较早的
OWC
,可能已经遇到过微软的许可证问题。以前这个问题相当令人烦恼,微软不仅要求服务器上必须有
Office
许可证,而且每一台客户
PC
上也同样要有。
实际上,这相当于将
OWC
的用途局限到了
Intranet
之内,只有
Intranet
之内才可以保证客户
PC
上都安装了
Office
许可证。不过现在微软的态度有所放缓
――
服务器上仍旧要安装
Office
许可证,但只要图表是
“
非交互式
”
用途,例如本文的服务器端图表绘制,客户端就不必再装
Office
许可证。实际上,就连服务器端也不必安装完整的
Office
许可证,
Excel 2002
或
FrontPage 2002
的许可证就已足够,从而使
OWC
变成了价廉物美的服务器端图表引擎。
那么,在服务器上安装
MS Office
?不,没有必要。虽然从许可证条件看,
OWC
应该是
Office
的一部分,但从技术上说,
OWC
是一个独立的产品。
Web
服务器上只需安装
OWC
软件包,不必安装整个
Office
。
OWC
首次出现于
Office 2000
,即
OWC 9.0
。在
Office XP
中,
OWC
的编程模式已作了修改,这使得
OWC XP
(也就是
OWC 10
)不能与
OWC 9.0
完全兼容。
OWC 10
要求在
ASP.NET
环境中运行,所以
OWC 10
软件包必须安装到
ASP.NET
服务器上。
接下来,很自然的一个问题是:哪里可以下载
OWC 10
软件包?令人惊奇的是,它可以从微软的网站免费下载,地址是
http://office.microsoft.com/downloads/2002/owc10.aspx
,
(
中文地址:
http://www.microsoft.com/downloads/details.aspx?displaylang=zh-cn&FamilyID=982B0359-0A86-4FB2-A7EE-5F3A499515DD
网际浪子注
)
但要注意的是,
Web
服务器上必须安装了某种
Office 2002
的许可证才能合法地使用
OWC 10
。
四、
OWC
的运行机制
OWC
是一组
COM
(
ActiveX
)控件的集合,涵盖电子表格、图表、数据透视表等功能。它经常被当作客户端技术使用,这时
COM
控件就安装在客户端
PC
上。如果在服务器端使用,人们主要感兴趣的是它的图表绘制功能。
有了
OWC
,我们可以在
ASP.NET Web
服务器上动态创建一个图表,然后将图表以
GIF
图形的形式发送到客户端。客户端看到的仅仅是一个普通的图形文件,但在
“
背后
”
,图形文件实际上是由服务器上
ASP.NET
回应客户请求时动态生成的。因此,这种技术对客户端没有特殊的要求,只要能够显示
GIF
图形就可以了,即使
Netscape
和
Opera
也不存在任何问题。
既然如此,为什么在
ASP.NET
开发领域中,
OWC
这一优秀的微软技术尚未被广泛采用呢?微软根本不为
OWC
作市场宣传,再加上令人迷惑的许可证问题,当然令许多开发者望而却步。也许微软认为该产品还没有完全成熟,即将到来的
Office 2003
将会带来
OWC 11
,它的编程模式还会有所改变。另外,还有一种可能是微软担心
OWC
技术的广泛采用会影响
Office
的销售。
再者,关于
OWC
的编程实例很少。微软知识库有几个客户端的例子和
“
传统
”ASP
的服务器端例子,但找不到在
ASP.NET
环境中使用
OWC 10
的例子。
OWC
的新闻组,
microsoft.public.office.developer.web.components
,主要讨论的也是客户端的应用。如果你要在
ASP.NET
环境中使用
OWC 10
,主要还是靠自己摸索。正是因为这些原因,所以本文从相当广泛的角度探讨了该技术的实际应用。
五、在
Web
服务器上安装
OWC 10
要想在
ASP.NET Web
服务器上用
OWC
绘制图表,首先应当安装必要的软件和修改一些配置。
第一,
Web
服务器上当然应该有
ASP.NET
运行环境。除了
.NET Framework Redistributable
,还要有
GACUTIL
程序(属于
.NET
框架
SDK
)来配置
OWC
控件,也就是说,还要安装
.NET Framework SDK
工具。如果把
.NET Framework 1.1 Redistributable
和
SDK
安装到了默认目录,
PATH
环境变量的内容应当包含:
C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322;C:\Program Files\Microsoft.NET\SDK\v1.1\Bin
。
接下来再在
Web
服务器上安装
OWC 10
。
OWC
可以从微软免费下载,安装时只要采用所有默认选项即可。
由于
OWC 10
是一种
COM
技术,为了让
.NET
代码使用
OWC 10
组件,还必须安装
Office XP
的
Primary Interop Assembly
(
PIA
),
PIA
可以从微软网站下载(
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnoxpta/html/odc_oxppias.asp
)。
(
网际浪子发现的地址是:
http://www.microsoft.com/downloads/details.aspx?FamilyId=C41BD61E-3060-4F71-A6B4-01FEBA508E52&displaylang=en)
下载得到的
OXPPIA.exe
是一个压缩文件,现在把它解压缩到服务器上的一个目录,假设是
C:\oxppia
,然后启动一个命令窗口(注意,确保
PATH
环境变量已正确设置
[
网际浪子注:可以用我作的
SETPATH.BAT
运行一下
]
),转到
c:\oxppia
目录,运行
REGISTER.bat
。
这个命令把
Office XP PIA
导入到全局程序集缓冲区,修改注册表设置。注意观察
REGISTER.bat
命令的输出,确信
GACUTIL
命令确实在运行。如果
PATH
环境变量设置有误,
PIA
不可能正确导入。
README
文档说应当用
VS.NET
命令行环境,但
Web
服务器上可能没有安装
VS.NET
,这时就要手工修改
PATH
环境变量了(效果一样)。
最后,还要把下面这行代码加入
Web
服务器的
machine.config
文件的
<assemblies>
节,对于
.NET Framework 1.1
,
machine.config
文件可以在
C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\CONFIG
目录下找到:
<add assembly="Microsoft.Office.Interop.OWC, Version=10.0.4504.0,
Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
浪子注明:如果发生不能编译的错误,可能要再次运行注册步骤!我这里这样,大家如果不一样,可以自己试试。
六、
OWC
编程模式
要生成图表的数据称为数据原,
Chart Component
组件支持的数据源有:实现
IDataSource
接口的任何数据源;
ADO
Recordset
对象;
XML
文件;数组或者一定格式的文本字符串。在
ASP
中,我们可以用
ADO Recordset
对象;在
.NET
的
ADO.NET
中,由于
ADO.NET
没有实现
IDataSource
,
.NET
也没有提供
ADO.NET DataSet
对象向
ADO Recordset
对象的直接转换,如果你有一个
DataSet
对象,你要么转换成
XML
文件,要么生成特殊格式的字符串才可以使用。下面就是本例子的结果:
[
浪子注明:如果出错,可能是文件夹权限的问题,我的就是,要将虚拟目录的
everyone
的全部权限加上!
]
下面是实现这种功能的
VB.NET
版本的
ASP.NET
例子与代码:
OWC.aspx:
<%@ Page Language="vb" AutoEventWireup="false" Codebehind="OWC.aspx.vb" Inherits="aspxWeb.OWC"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<HTML>
<HEAD>
<title>WebForm1</title>
<meta name="GENERATOR" Content="Microsoft Visual Studio 7.0">
<meta name="CODE_LANGUAGE" Content="C#">
<meta name="vs_defaultClientScript" content="javascript">
<meta name="vs_targetSchema" content="http://schemas.microsoft.com/intellisense/ie5";>
</HEAD>
<body MS_POSITIONING="GridLayout">
<form id="Form1" method="post" runat="server">
<asp:placeholder id="ChartHolder" runat="server"></asp:placeholder>
</form>
</body>
</HTML>
OWC.aspx.vb
:
Imports System
Imports OWC
Imports System.Web.UI
Public Class OWC
Inherits System.Web.UI.Page
Protected WithEvents ChartHolder As System.Web.UI.WebControls.PlaceHolder
#Region " Web
窗体设计器生成的代码
"
'
该调用是
Web
窗体设计器所必需的。
<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
End Sub
Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init
'CODEGEN:
此方法调用是
Web
窗体设计器所必需的
'
不要使用代码编辑器修改它。
InitializeComponent()
End Sub
#End Region
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'
在此处放置初始化页的用户代码
'
创建
ChartSpace
对象来放置图表
Dim objCSpace As ChartSpace = New ChartSpaceClass()
'
在
ChartSpace
对象中添加图表,
Add
方法返回
chart
对象
Dim objChart As WCChart = objCSpace.Charts.Add(0)
'
指定图表的类型。类型由
OWC.ChartChartTypeEnum
枚举值得到
objChart.Type = ChartChartTypeEnum.chChartTypeColumnClustered
'
指定图表是否需要图例
objChart.HasLegend = True
'
给定标题
objChart.HasTitle = True
objChart.Title.Caption = "1-6
说数据分布图
"
'
给定
x,y
轴的图示说明
objChart.Axes(0).HasTitle = True
objChart.Axes(0).Title.Caption = "Y
轴
:
数量
"
objChart.Axes(1).HasTitle = True
objChart.Axes(1).Title.Caption = "X
轴:
月份
"
'
计算数据
'*categories
和
values
可以用
tab
分割的字符串来表示
*
Dim strSeriesName As String = "
图例
1"
Dim strCategory As String = "1" + ControlChars.Tab + "2" + ControlChars.Tab _
+ "3" + ControlChars.Tab + "4" + ControlChars.Tab + "5" + ControlChars.Tab _
+ "6" + ControlChars.Tab
Dim strvalue As String = "9" + ControlChars.Tab + "8" + ControlChars.Tab _
+ "4" + ControlChars.Tab + "10" + ControlChars.Tab + "12" + ControlChars.Tab _
+ "6" + ControlChars.Tab
'
添加一个
series
objChart.SeriesCollection.Add(0)
'
给定
series
的名字
objChart.SeriesCollection(0).SetData(ChartDimensionsEnum.chDimSeriesNames,_
ChartSpecialDataSourcesEnum.chDataLiteral, strSeriesName)
'
给定分类
objChart.SeriesCollection(0).SetData(ChartDimensionsEnum.chDimCategories,_
ChartSpecialDataSourcesEnum.chDataLiteral, strCategory)
'
给定值
objChart.SeriesCollection(0).SetData(ChartDimensionsEnum.chDimvalues,_
ChartSpecialDataSourcesEnum.chDataLiteral, strvalue)
'
输出成
GIF
文件
.
Dim strAbsolutePath As String = (Server.MapPath(".")) + "\Images\test.gif"
objCSpace.ExportPicture(strAbsolutePath, "GIF", 600, 350)
'
创建
GIF
文件的相对路径
.
Dim strRelativePath As String = "Images/test.gif"
'
把图片添加到
placeholder.
Dim strImageTag As String = "<IMG SRC='890_files/" + strrelativepath + "'/>"
ChartHolder.Controls.Add(New LiteralControl(strImageTag))
End Sub
End Class
下面是
C#
版本的
OWC.asp.cs
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using Microsoft.Office.Interop;
namespace Microsoft.Office.Interop.OWC
使用指南
{
/// <summary>
/// WebForm1
的摘要说明。
/// </summary>
public class WebForm1 : System.Web.UI.Page
{
protected System.Web.UI.WebControls.PlaceHolder ChartHolder;
private Microsoft.Office.Interop.OWC.ChartChartTypeEnum GetChartType(int typeIndex)
{
int i;
Microsoft.Office.Interop.OWC.ChartChartTypeEnum myTE;
i=typeIndex;
switch(i)
{
case 0:
myTE=Microsoft.Office.Interop.OWC.ChartChartTypeEnum.chChartTypeColumnClustered;
return myTE;
case 1:
myTE=Microsoft.Office.Interop.OWC.ChartChartTypeEnum.chChartTypePie;
return myTE;
case 2:
myTE=Microsoft.Office.Interop.OWC.ChartChartTypeEnum.chChartTypeSmoothLine;
return myTE;
case 3:
myTE=Microsoft.Office.Interop.OWC.ChartChartTypeEnum.chChartTypeArea;
return myTE;
case 4:
myTE=Microsoft.Office.Interop.OWC.ChartChartTypeEnum.chChartTypeRadarLine;
return myTE;
default:
myTE=Microsoft.Office.Interop.OWC.ChartChartTypeEnum.chChartTypeColumnClustered;
return myTE;
}
}
private void Page_Load(object sender, System.EventArgs e)
{
//
在此处放置用户代码以初始化页面
//
创建
ChartSpace
对象来放置图表
Microsoft.Office.Interop.OWC.ChartSpace objCSpace = new Microsoft.Office.Interop.OWC.ChartSpaceClass();
//
在
ChartSpace
对象中添加图表,
Add
方法返回
chart
对象
Microsoft.Office.Interop.OWC.ChChart objChart = objCSpace.Charts.Add (0);
//
指定图表的类型。类型由
Microsoft.Office.Interop.OWC.ChartChartTypeEnum
枚举值得到
//objChart.Type = Microsoft.Office.Interop.OWC.ChartChartTypeEnum.chChartTypeColumnClustered;
//
上面的是画棒图的方法
//objChart.Type = Microsoft.Office.Interop.OWC.ChartChartTypeEnum.chChartTypeSmoothLine;
//
上面的是画平滑曲线的方法
objChart.Type =Microsoft.Office.Interop.OWC.ChartChartTypeEnum.chChartTypeRadarLine;
//
上面的是画雷达线的方法
//objChart.Type =Microsoft.Office.Interop.OWC.ChartChartTypeEnum.chChartTypeArea;
//
上面的是画区域线的方法
//objChart.Type =Microsoft.Office.Interop.OWC.ChartChartTypeEnum.chChartTypePie;
//
上面的是画饼图的方法,但是要关掉一些参数,比如
/*//objChart.Axes[0].HasTitle = true;
//objChart.Axes[0].Title.Caption = "Y
:
数量
";
//objChart.Axes[1].HasTitle = true;
//objChart.Axes[1].Title.Caption = "X
:
月份
";
* */
//
指定图表是否需要图例
objChart.HasLegend = true;
//
给定标题
objChart.HasTitle = true;
objChart.Title.Caption= "
上半年分布图
";
//
给定
x,y
轴的图示说明
objChart.Axes[0].HasTitle = true;
objChart.Axes[0].Title.Caption = "Y
:
数量
";
objChart.Axes[1].HasTitle = true;
objChart.Axes[1].Title.Caption = "X
:
月份
";
//
计算数据
/*categories
和
values
可以用
tab
分割的字符串来表示
*/
string strSeriesName = "
图例
1";
string strCategory = "1" + '\t' + "2" + '\t' + "3" + '\t'+"4" + '\t' + "5" + '\t' + "6" + '\t';
string strvalue = "9" + '\t' + "8" + '\t' + "4" + '\t'+"10" + '\t' + "12" + '\t' + "6" + '\t';
//
添加一个
series
objChart.SeriesCollection.Add(0);
//
给定
series
的名字
objChart.SeriesCollection[0].SetData (Microsoft.Office.Interop.OWC.ChartDimensionsEnum.chDimSeriesNames,
+ (int)Microsoft.Office.Interop.OWC.ChartSpecialDataSourcesEnum.chDataLiteral, strSeriesName);
//
给定分类
objChart.SeriesCollection[0].SetData (Microsoft.Office.Interop.OWC.ChartDimensionsEnum.chDimCategories,
+ (int)Microsoft.Office.Interop.OWC.ChartSpecialDataSourcesEnum.chDataLiteral, strCategory);
//
给定值
objChart.SeriesCollection[0].SetData
(Microsoft.Office.Interop.OWC.ChartDimensionsEnum.chDimvalues,
(int)Microsoft.Office.Interop.OWC.ChartSpecialDataSourcesEnum.chDataLiteral, strvalue);
//
输出成
GIF
文件
.
string strAbsolutePath = (Server.MapPath(".")) + "\\test.gif";
objCSpace.ExportPicture(strAbsolutePath, "GIF", 600, 350);
//
创建
GIF
文件的相对路径
.
string strRelativePath = "./test.gif";
//
把图片添加到
placeholder.
string strImageTag = "<IMG SRC='" + strRelativePath + "'/>";
ChartHolder.Controls.Add(new LiteralControl(strImageTag));
}
#region Web Form Designer generated code
override protected void OnInit(EventArgs e)
{
//
// CODEGEN
:该调用是
ASP.NET Web
窗体设计器所必需的。
//
InitializeComponent();
base.OnInit(e);
}
/// <summary>
///
设计器支持所需的方法
-
不要使用代码编辑器修改
///
此方法的内容。
/// </summary>
private void InitializeComponent()
{
this.Load += new System.EventHandler(this.Page_Load);
}
#endregion
}
}
如果用
ADO.NET
的
DataSet
对象,可以生成以
TAB
分割的字符串:
strvalue += (nodes.Item(j).ChildNodes.Item(0).InnerText + '\t');
strCategory += (nodes.Item(j).ChildNodes.Item(1).InnerText + '\t');
Microsoft.Office.Interop
名称空间指向
Office XP PIA
,
PIA
应该事先安装到
Web
服务器上。编译源代码时要用到
Office XP PIA OWC
的
DLL
文件。如果用
VS.NET
编译,只要加入一个
Microsoft.Office.Interop.Owc.dll
文件的引用即可(位于解开
Office XP PIA
文件的目录),如果从命令行编译,必须按照下列方式使用
/r:
参数:
vbc /t:library /out:bin\getchart.dll /r:System.dll /r:System.Web.dll
/r:System.Data.dll
/r:C:\oxppia\Microsoft.Office.Interop.Owc.dll getchart.aspx.vb
上面的代码有许多值得一提的地方。首先,我们假定数据源位于
MSSQL
数据库
OWCDEMO
,该数据库有一个
OWCDATA
表,
OWCDATA
表有两个数值列,分别是
X
和
Y
。
getchart.aspx
的目标就是从数据库获取记录,然后用散点图(
XY
)描述这些数据。
OWC
图表的数据点无法直接从
ASP.NET
的
DataSet
获取,因此,我们首先要把数据库的数据装入数组,然后用数组的数据填写
OWC
图表的数据点。如果要对本例作改进的话,最好开发一个
ASP.NET
服务器控件,它能够从抽象的数据源(包括
DataSet
对象、
XML
文件或数组)获取数据并生成
XY
散点图。
DataReader
要比
DataSet
快速、高效,不过,我们首先要确定数据库中的记录数量,根据记录数量来调整数组的大小。为此,我们先用一个
SQL Select count(*)
查询获取记录数量,然后定义数组大小,最后用第二个
SQL SELECT
查询获取数据库记录。
如果我们要让散点图的各个点用折线连接起来,记录必须依照
X
轴排序,这通过一个
SQL ORDER BY
子句实现。
OWC
的图表建立在
“
绘图空间
”
上。一个绘图空间可以包含一个或多个图表,每一个图表可以有一个或多个数据系列。在生成
OWC
图表时,我们首先创建一个绘图空间,将一个图表加入到绘图空间,设置图表的类型,添加数据系列,最后用数组的数据填写数据系列。
另外,我们还可以设置(可选)各种布局参数,例如颜色、坐标标题、图表标题、图例,等等。
OWC
提供了数百个布局参数,我们可以随心所欲地调整图表。当然,对于不同的图表类型,绘图模式也略有不同,例如,饼图和散点图的参数设置方法是不同的。在
OWC 10
安装包中有
OWC
帮助文件,里面详细说明了
OWC
图表模型。
最后,
Response.BinaryWrite
参数指定了要输出的图形类型(
GIF
),以及图形的宽度、高度(以像素为单位)。在这里,我们可以根据需要缩放从
OWC
图表生成的图形。
对了,忘了说代码的位置了,在
FTP
里的论坛问题解答里,有一个
OWC
使用指南的目录,下面有使用
OWC
的全部文件和源码。