ASP.NET Web Forms アプリでグラフを表示するのに使う Microsoft 製の Chart コントロールについて調べたことで、備忘録として残しておいたほうがよさそうなことを書いておきます。
グラフを表示する仕組みは一般的な画像の場合と同じで、html タグの img 要素を用います。
異なるのは、プログラムで指定された内容・形式のグラフの画像(デフォルトで png 形式)を作成し、src 属性に画像データの取得方法を指定して img 要素をレンダリングすることを、Chart コントロールが動的に行う点です。
その後は一般的な静的画像を表示する場合と同じで、ブラウザが src 属性に指定された方法で画像をサーバーに要求し、サーバーから応答として受けた画像を img 要素の位置に表示します。
src 属性に指定される画像の取得方法には、以下に述べる 3 つのオプションがあります。
-
HTTP ハンドラ: Chart コントロールが自動的に HTTP ハンドラを定義し、それを src 属性に指定して img 要素に含めてブラウザに送ります。画像データは、サーバーの特定のフォルダ、メモリまたは Session に一時的に保存されます。ブラウザから画像データの要求を受けると、HTTP ハンドラがそれを取得してブラウザに送信します。
-
静的ファイル: Web サーバーのファイルシステム下にあるフォルダに静的画像ファイルを作って保存します。img 要素の src 属性にその画像のパス/ファイル名を指定してブラウザに送ります。
-
バイナリデータ: Chart コントロールが生成したバイナリ画像データを直接応答として返す aspx ページを作成し、それを img 要素の src に指定します。
HTTP ハンドラを用いる方法がデフォルトですので、それについて書いておきます。なお、HTTP ハンドラを用いる方法がベストというわけではなく、3 つのオプションには一長一短があるので、ケースバイケースで決めるべきだそうです。
詳しくは、Using Microsoft's Chart Controls In An ASP.NET Application: Rendering the Chart を参照してください。
Visual Studio で Chart コントロールを ASP.NET ページにドラッグ&ドロップすると、web.config にデフォルトで ChartImageHandler という名前の HTTP ハンドラが定義されます。.NET 4 の場合は以下のようになります。
<system.web>
<httpHandlers>
<add path="ChartImg.axd"
verb="GET,HEAD,POST"
type="System.Web.UI.DataVisualization.Charting.ChartHttpHandler,
System.Web.DataVisualization,
Version=4.0.0.0,
Culture=neutral,
PublicKeyToken=31bf3856ad364e35"
validate="false" />
</httpHandlers>
・・・中略・・・
</system.web>
<system.webServer>
・・・中略・・・
<handlers>
<remove name="ChartImageHandler" />
<add name="ChartImageHandler"
preCondition="integratedMode"
verb="GET,HEAD,POST"
path="ChartImg.axd"
type="System.Web.UI.DataVisualization.Charting.ChartHttpHandler,
System.Web.DataVisualization,
Version=4.0.0.0,
Culture=neutral,
PublicKeyToken=31bf3856ad364e35" />
</handlers>
</system.webServer>
上記のように system.web 要素内と system.webServer 要素内の両方で HTTP ハンドラが定義されますので、IIS のア���リケーションプールのマネージパイプラインモードが Integrated mode の場合はエラーになります。対処方法は、先の記事 IIS7 Integrated mode での BlogEngine の実行 で述べたとおりです。
ただし、.NET 3.5 では <validation validateIntegratedModeConfiguration="false"/> がデフォルトの web.config に定義されているので何もしなくてもエラーは出ないはずです。.NET 4 の場合はそれが定義されていませんので対応が必要になります。
さらに、web.config には、一時的に画像データを保存する方法、場所等が定義されます。デフォルトで以下のようになるはずです。
<appSettings>
<add key="ChartImageHandler"
value="storage=file;timeout=20;dir=c:\TempImageFiles\;" />
</appSettings>
value 属性のパラメーターの詳しい説明は MSDN ライブラリの イメージ ファイルの管理 を参照してください。重要な(と個人的に思う)パラメータのみ以下に説明します。
パラメーター storage は画像データを保存する方法を指定するもので、file, memory, session の 3 つのオプションがあります。読んで字のごとく、それぞれ、サーバーの特定のフォルダ(デフォルト)、実行中のプロセスのメモリ空間、セッション変数になります。
パラメーター dir は、storage=file の場合の、イメージ保存場所の絶対ディレクトリパス(上記の例では c:\TempImageFiles)です。web ファームの場合は、ネットワークパスを使用して共有ファイルサーバーのフォルダとすることができます。
なお、当然ですが、IIS のワーカープロセスにはイメージ保存場所の絶対ディレクトリに対する読み取り/書き込み権限を与える必要があります。
dir の代わりに url というパラメータを使って、アプリケーションルート下のフォルダを指定することも可能です。c:\TempImageFiles などとできない、レンタル共有サーバーの場合この方法が使えます。ただし、他のユーザーもアクセスできてしまう場所に保存するのは好ましくなさそうです。そもそも、HTTP ハンドラを用いる目的は、ユーザーが直接アクセスできない場所に画像を保存するためですので。
その他、ASP.NET ページの @ Register ディレクティブ、または web.config 内で pages の controls の add 要素に、Chart コントロールを指定する tagPrefix、namespace、および assembly の各属性を定義する必要があります。これは Visual Studio で Chart コントロールをドラッグ&ドロップすると、自動的にコードが生成されるはずです。
HTTP ハンドラーを使用する場合は Chart.ImageStorageMode プロパティを UseHttpHandler に設定しますが、これはデフォルトでそうなっています。上に述べた「静的ファイル」のオプションを選択した場合は、UseImageLocation に設定する必要があります。
以上の設定をして、グラフを作る Chart コントロールを含んだページを要求すると、以下のような img 要素がレンダリングされ、ブラウザに送信されるはずです。
<img id="Chart1"
src="/ChartImg.axd?i=chart_0_0.png&g=6d177535e0d24eb8b9a60872279889e8"
alt=""
style="height:300px;width:600px;border-width:0px;" />
ブラウザが src 属性に指定されたページをサーバーに要求すると、サーバーは HTTP ハンドラを使って画像データを取得してブラウザに返し、それを受けたブラウザは img 要素の位置に画像を表示します。
参考に、上に表示したグラフを作ったコードをアップしておきます。内容は @IT の記事「チャート・コントロールで積み上げ棒グラフを作成するには?」そのものです。
<%@ Page Language="C#" %>
<%@ Register Assembly="System.Web.DataVisualization,
Version=4.0.0.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35"
Namespace="System.Web.UI.DataVisualization.Charting"
TagPrefix="asp" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<%-- .NET 3.5 のときは無くても OK だった N プレフィックスが何故か必要 --%>
<asp:SqlDataSource ID="SqlDataSource1" runat="server"
ConnectionString="<%$ ConnectionStrings:MyDB %>"
SelectCommand="SELECT
Month,
SUM(CASE WHEN Name = N'三吉' THEN Sales ELSE 0 END) AS 三吉,
SUM(CASE WHEN Name = N'春日' THEN Sales ELSE 0 END) AS 春日,
SUM(CASE WHEN Name = N'東雲' THEN Sales ELSE 0 END) AS 東雲,
SUM(CASE WHEN Name = N'府中' THEN Sales ELSE 0 END) AS 府中,
SUM(CASE WHEN Name = N'広島' THEN Sales ELSE 0 END) AS 広島
FROM Shop GROUP BY Month">
</asp:SqlDataSource>
<asp:Chart ID="Chart1"
runat="server"
DataSourceID="SqlDataSource1"
Width="600px">
<Legends>
<asp:Legend DockedToChartArea="ChartArea1"
IsDockedInsideChartArea="False"
Name="Legend1">
</asp:Legend>
</Legends>
<Series>
<asp:Series Name="三吉"
ChartType="StackedColumn"
CustomProperties="DrawingStyle=Cylinder"
IsValueShownAsLabel="True"
Label="#PERCENT{P1}"
Legend="Legend1"
XValueMember="Month"
YValueMembers="三吉">
</asp:Series>
<asp:Series Name="春日"
ChartArea="ChartArea1"
ChartType="StackedColumn"
CustomProperties="DrawingStyle=Cylinder"
IsValueShownAsLabel="True"
Label="#PERCENT{P1}"
Legend="Legend1"
XValueMember="Month"
YValueMembers="春日">
</asp:Series>
<asp:Series Name="東雲"
ChartArea="ChartArea1"
ChartType="StackedColumn"
CustomProperties="DrawingStyle=Cylinder"
IsValueShownAsLabel="True"
Label="#PERCENT{P1}"
Legend="Legend1"
XValueMember="Month"
YValueMembers="東雲">
</asp:Series>
<asp:Series Name="府中"
ChartArea="ChartArea1"
ChartType="StackedColumn"
CustomProperties="DrawingStyle=Cylinder"
IsValueShownAsLabel="True"
Label="#PERCENT{P1}"
Legend="Legend1"
XValueMember="Month"
YValueMembers="府中">
</asp:Series>
<asp:Series Name="広島"
ChartArea="ChartArea1"
ChartType="StackedColumn"
CustomProperties="DrawingStyle=Cylinder"
IsValueShownAsLabel="True"
Label="#PERCENT{P1}"
Legend="Legend1"
XValueMember="Month"
YValueMembers="広島">
<EmptyPointStyle IsVisibleInLegend="False" />
</asp:Series>
</Series>
<ChartAreas>
<asp:ChartArea Name="ChartArea1">
<AxisY Title="売上高">
</AxisY>
<AxisX Title="売上月">
</AxisX>
</asp:ChartArea>
</ChartAreas>
</asp:Chart>
</div>
</form>
</body>
</html>