Skip to main content
黑话筒

Office Web Component(OWC)中的PivotTable(透视表)使用XML数据源

以前使用OWC的PivotTable都是为该对象设定一个连接串,然后在指定一条sql语句,这个PivotTable就能连接到数据库上,并把内容显示出来了.

这是在局域网上.但如果在互联网上呢,你还敢把连接串放到网页里面吗?即使放了,你的数据库服务器也很有可能在内网中,网关不做端口转发,你仍旧访问不到.那该怎么办呢?

别怕,我们用XML作为其数据源.

步骤是这样的:

  1. 客户端利用Ajax请求服务器的一个页面(datasource.aspx)
  2. datasource.aspx将访问数据库,并将读到的结果放到一个DataTable对象中
  3. 利用自己写的一个函数将DataTable的XML转换成RecordSet XML
  4. 返回该XML
  5. 客户端声明一个XML对象,将字符串转换成XML Document
  6. 客户端声明一个RecodSet对象,将XMLDocument作为其数据源,并打开数据源
  7. 指定PivotTable的数据源为上面的RecordSet对象,从而完成了对数据源的加载
  8. 后面再根据需要操作PivotTable将某些列添加到行/列/过滤/内容区域

总体顺序是这样的,我把部分代码贴上来,供大家参考.

/// 
/// 将DataTable转换成RecordXML函数
/// 
/// 要转换的DataTable
/// 转换好的XML字符串
public static string ConvertDataTableToXML(DataTable dt)
{
    /*DataTable支持的数据类型
        system.int64
        system.byte[]
        system.boolean
        system.string
        system.datetime
        system.decimal
        system.double
        system.int32
        system.single
        system.int16
        system.object
        system.byte
        system.guid
     */
    string TempValue;

    for(int i = ; i < dt.Columns.Count; i++)
    {
        TempValue = dt.Columns[i].ColumnName;
        Regex rex = new Regex("\\W");
        TempValue = rex.Replace(TempValue, "");

        rex = new Regex("^\\d");
        if(rex.Replace(TempValue, "").Length != TempValue.Length)
        {
            TempValue = "N" + TempValue;
        }
        dt.Columns[i].ColumnName = TempValue;
    }

    StringBuilder sb = new StringBuilder();

    sb.Append("");
    sb.Append("");
    sb.Append("\t");

    for (int i = ; i < dt.Columns.Count; i++)
    {
        string DataType;
        switch (dt.Columns[i].DataType.ToString().ToLower())
        {
            case "system.boolean":
                DataType = "string";
                break;

            case "system.byte":
            case "system.int16":
            case "system.int32":
            case "system.int64":
                DataType = "int";
                break;

            case "system.decimal":
            case "system.double":
            case "system.single":
                DataType = "float";
                break;

            case "system.string":
                DataType = "string";
                break;
            case "system.datetime":
                DataType = "datetime";
                break;

            default:
                throw new ArgumentException();
        }
        sb.Append("\t\t");
        sb.Append("\t\t\t");
        sb.Append("\t\t");
    }

    sb.Append("\t\t");
    sb.Append("\t");
    sb.Append("");
    sb.Append("");

    for (int i = ; i < dt.Rows.Count; i++)
    {
        sb.Append("\t", "").Replace("'", "").Replace("\"", "");

            if (dt.Columns[j].DataType.ToString().ToLower() == "system.datetime" && TempValue == string.Empty)
            {
                TempValue = INVALIDDATE.ToString(DATEMASK);
            }
            sb.Append(dt.Columns[j].ColumnName + "='" + TempValue + "' ");
        }
        sb.Append("/>");
    }

    sb.Append("");
    sb.Append("");

    return sb.ToString();
}

通过这个函数就能将一个DataTable转换成RecordSet XML

下面是完整的客户端页面,包括详尽的注释(牵扯到公司有关的代码都被我Cut掉了,但不影响这个页面)

<html>

<head>
  <script language="javascript" type="text/javascript">
    window.onload = LoadPage;
    var SEP = "/,/";
    var SEP2 = "/,,/";

    //初始化报告参数
    //TODO:这里为了做成通用的报告,变量名称需要变更
    //需要添加到行区域的字段名称集合,以Common.SEP分割
    var strRowFieldNames = "<%=Server.UrlDecode(Request.QueryString["RowFieldNames"])%>";
    //需要添加到列区域的字段名称集合,以Common.SEP分割
    var strColumnFieldNames = "<%=Server.UrlDecode(Request.QueryString["ColumnFieldNames"])%>";
    //过滤字段名称集合,以Common.SEP分割
    var strFilterFieldNames = "<%=Server.UrlDecode(Request.QueryString["FilterFieldNames"])%>";
    //需要进行分段汇总的字段名称
    //格式为:字段名/,,/分组类型/,,/起始值/,,/间隔值/,/第二个分组
    //其中/,,/为Common.SEP2, /,/为Common.SEP
    var strGroupFieldNamesAndValues = "<%=Server.UrlDecode(Request.QueryString["GroupFieldNamesAndValues"])%>";

    //图表类型
    var intChartType = parseInt("<%=Server.UrlDecode(Request.QueryString["ChartType"])%>");

    //声明变量,用来存储页面上的OWC Object
    //透视表对象及透视表的常量对象
    var pvt, pvtconstants;

    //图表对象及图标的常量对象
    var cht, chtconstants;

    function LoadPage() {
      //初始化透视表
      InitPivotTable();

      //初始化图表
      InitChartSpace();
    }

    /**
     * 初始化透视表
     */
    function InitPivotTable() {
      //声明RecordSet对象
      var adors = new ActiveXObject("ADODB.Recordset");

      //声明XMLDocument对象
      //TODO:msxml2.domdocument有可能是msxml3.domdocument或msxml4.domdocument,有待证明
      var xmldoc = new ActiveXObject("msxml2.domdocument");

      //服务器端返回的XML字符串,用来构造RecordSet
      var str = "<%=XMLString%>";

      //XMLDocument对象加载XML字符串
      xmldoc.loadXML(str);

      //RecordSet设定数据源为上面的XMLDocument对象,并打开
      adors.Open(xmldoc);

      pvt = document.pvtTable;

      pvtconstants = pvt.Constants;
      //设定透视表的数据源为上面的RecordSet对象
      pvt.DataSource = adors;

      //设定透视表的一些属性,具体请参考OWC For VBA
      pvt.AutoFit = true;
      pvt.DisplayToolbar = true;
      pvt.DisplayExpandIndicator = true;
      pvt.DisplayAlerts = true;
      pvt.DisplayBranding = true;
      pvt.DisplayDesignTimeUI = true;
      pvt.DisplayFieldList = true;
      pvt.DisplayOfficeLogo = false;
      pvt.DisplayPropertyToolbox = false;
      pvt.DisplayScreenTips = true;

      pvt.ActiveView.TitleBar.Visible = true;
      pvt.ActiveView.TitleBar.Caption = "<%=Title%>";
      pvt.ActiveView.TitleBar.Font.Name = "arial";
      pvt.ActiveView.TitleBar.Font.Size = 10;

      pvt.ActiveView.TotalBackColor = "CornSilk";
      pvt.ActiveView.TotalFont.Name = "arial";
      pvt.ActiveView.TotalFont.Size = 8;
      pvt.ActiveView.FieldLabelFont.Name = "arial";
      pvt.ActiveView.FieldLabelFont.Size = 8;
      pvt.ActiveView.HeaderFont.Name = "arial";
      pvt.ActiveView.HeaderFont.Size = 8;
      pvt.ActiveView.PropertyCaptionFont.Name = "arial";
      pvt.ActiveView.PropertyCaptionFont.Size = 8;
      pvt.ActiveView.PropertyValueFont.Name = "arial";
      pvt.ActiveView.PropertyValueFont.Size = 8;
      pvt.ActiveView.ExpandMembers = pvtconstants.plExpandNever;
      pvt.ActiveView.ExpandDetails = pvtconstants.plExpandNever;

      //设定每一个字段的字体,字号,颜色等
      for (var i = ; i <= pvt.ActiveView.FieldSets.Count - 1; i++) {
        pvt.ActiveView.FieldSets(i).Fields(0).DetailFont.Name = "arial";
        pvt.ActiveView.FieldSets(i).Fields(0).DetailFont.Size = 8;
        pvt.ActiveView.FieldSets(i).Fields(0).GroupedFont.Name = "arial";
        pvt.ActiveView.FieldSets(i).Fields(0).GroupedFont.Name = "arial";
        pvt.ActiveView.FieldSets(i).Fields(0).GroupedFont.Size = 8;
        pvt.ActiveView.FieldSets(i).Fields(0).SubtotalLabelFont.Name = "arial";
        pvt.ActiveView.FieldSets(i).Fields(0).SubtotalLabelFont.Size = 8;
        pvt.ActiveView.FieldSets(i).Fields(0).SubtotalFont.Name = "arial";
        pvt.ActiveView.FieldSets(i).Fields(0).SubtotalFont.Size = 8;
        pvt.ActiveView.FieldSets(i).Fields(0).SubtotalBackColor = "LightSteelBlue";
      }

      //将所有字段都添加到"过滤"区域
      for (var i = ; i < pvt.ActiveView.FieldSets.Count; i++) {
        pvt.ActiveView.FilterAxis.InsertFieldSet(pvt.ActiveView.FieldSets(i));
      }

      /*
       * 添加一个"合计"字段
       * AddTotal方法原型: AddTotal(Name, Field, Function)
       * Name合计字段的名字
       * Field需要进行合计的列对象
       * Function合计方法
       */
      var ctotal = pvt.ActiveView.AddTotal("汇总", pvt.ActiveView.FieldSets(0).Fields(0), pvtconstants.plFunctionCount);

      /*
       * 将"合计"字段添加到"数据区域"
       * InsertTotal方法原型: InsertTotal(Total, Before)
       * Total是一个PivotTotal类型的对象,也就是上面声明的对象
       * Bebore是指定要在其之前插入总计的总计索引。如果不指定该参数,则总计插入到集合的末尾。
       */
      pvt.ActiveView.DataAxis.InsertTotal(ctotal, 0);

      //设定行区域
      SetRowFields();

      //设定列区域
      SetColumnFields();

      //设定明细区域
      SetDataFields();

      //设定分组
      SetGroupFields();

      /*
       * 隐藏指定对象的明细单元格。
       * 如果指定对象为 PivotData 对象,将隐藏所有明细单元格。
       * 如果指定对象为 PivotRowMember 对象,将隐藏该行的所有明细单元格。
       * 如果指定对象为 PivotColumnMember 对象,将隐藏该列的所有明细单元格。
       */
      pvt.ActiveData.HideDetails();
    }

    //设定行区域
    function SetRowFields() {
      if (strRowFieldNames == "") {
        return;
      }

      var OrgCollection = strRowFieldNames.split(SEP);

      //循环要显示在行上的字段
      for (var i = ; i < OrgCollection.length; i++) {
        pvt.ActiveView.FieldSets(OrgCollection[i]).Fields(0).IsIncluded = true;
        pvt.ActiveView.RowAxis.InsertFieldSet(pvt.ActiveView.FieldSets(OrgCollection[i]));
      }
    }

    //设定列区域
    function SetColumnFields() {
      if (strColumnFieldNames == "") {
        return;
      }

      var ColumnFieldsCollection = strColumnFieldNames.split(SEP);

      for (var i = ; i < ColumnFieldsCollection.length; i++) {
        pvt.ActiveView.FieldSets(ColumnFieldsCollection[i]).Fields(0).IsIncluded = true;
        pvt.ActiveView.ColumnAxis.InsertFieldSet(pvt.ActiveView.FieldSets(ColumnFieldsCollection[i]));
      }
    }

    //设定数据明细区域
    function SetDataFields() {
      var ColumnFieldsCollection = strColumnFieldNames + SEP + strRowFieldNames + SEP + strFilterFieldNames + SEP;
      for (var i = ; i < pvt.ActiveView.FieldSets.Count; i++) {
        if (ColumnFieldsCollection.indexOf(pvt.ActiveView.FieldSets(i).Name + SEP) == -1) {
          pvt.ActiveView.DataAxis.InsertFieldSet(pvt.ActiveView.FieldSets(i));
        }
      }
    }

    //设定字段分组
    function SetGroupFields() {
      //得到分组字段定义的数组,数组的每个元素是字段分组的一个完整定义
      var GroupCollection = strGroupFieldNamesAndValues.split(SEP);

      for (var i = ; i < GroupCollection.length; i++) {
        if (GroupCollection[i] == "") {
          break;
        }

        //得到其中的一个字段分组定义
        var OneGroupDefination = GroupCollection[i].split(SEP2);

        if (OneGroupDefination.length != 4) {
          break;
        }

        //字段名称
        var FieldName = OneGroupDefination[];
        //分组类型
        var GroupMethod = parseInt(OneGroupDefination[1]);
        //基数
        var BaseAmount = parseFloat(OneGroupDefination[2]);
        //间隔值
        var IntervalAmount = parseFloat(OneGroupDefination[3]);

        if (IntervalAmount == 0) {
          pvt.ActiveView.FieldSets(FieldName).Fields(0).GroupOn = pvtconstants.plGroupOnEachValue;
        }
        else {
          pvt.ActiveView.FieldSets(FieldName).Fields(0).GroupOn = GroupMethod;
          pvt.ActiveView.FieldSets(FieldName).Fields(0).GroupInterval = IntervalAmount;
          pvt.ActiveView.FieldSets(FieldName).Fields(0).GroupStart = BaseAmount;
        }
      }
    }

    //初始化图表
    function InitChartSpace() {
      //设定图表对象
      cht = document.chtSpace;

      //图表常量
      chtconstants = cht.Constants;

      //设定图表数据源
      cht.DataSource = pvt;

      //设置图表的一些属性
      cht.DisplayFieldButtons = false;
      cht.object.Border.Color = -2;
      cht.DisplayToolbar = true;
      cht.DisplayFieldButtons = false;
      cht.DisplayFieldList = false;
      cht.DisplayOfficeLogo = false;
      cht.HasChartSpaceLegend = true;
      cht.HasPassiveAlerts = false;

      //如果是柱状图(0)或3D柱状图(46)
      if (intChartType ==  || intChartType == 46) {
        cht.style.width = "100%";
      }

      //设定图表类型
      cht.Charts(0).Type = intChartType;
    }
  </script>

</head>

<body>
  <table cellspacing="0" cellpadding="0" border="0">
    <tr>
      <td>
        <object id="pvtTable" style="width: 502px; height: 217px" height="217" width="502"
          classid="clsid:0002E552-0000-0000-C000-000000000046" viewastext="VIEWASTEXT">
          <param name="XMLData" value='<xml xmlns:x="urn:schemas-microsoft-com:office:excel">
                                        <x:PivotTable>
                                        <x:OWCVersion>10.0.0.6619     </x:OWCVersion>
                                        <x:DisplayScreenTips/>
                                        <x:CubeProvider>msolap.2</x:CubeProvider>
                                        <x:CacheDetails/>
                                        <x:PivotView>
                                        <x:IsNotFiltered/>
                                        </x:PivotView>
                                        </x:PivotTable>
                                        </xml>'>
        </object>
      </td>
    </tr>
    <tr>
      <td>
        <object id="chtSpace" classid="clsid:0002E556-0000-0000-C000-000000000046" viewastext>
        </object>
      </td>
    </tr>
    <tr height="100%">
      <td>
      </td>
    </tr>
  </table>
</body>

</html>

相关链接:

  1. Office XP Web Component Toolpack

OWC组件的例子,如果里面有代码不能运行,请将HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\ActiveX Compatibility{00000566-0000-0010-8000-00AA006D2EA4}注册表项删除,以启用ADOStream

  1. OWC VBA参考,是一个chm文件,默认情况下在C:\Program Files\Common Files\Microsoft Shared\Web Components\11\2052\OWCVBA11.CHM 要看各个对象的属性什么的,可不能缺少这个.