dotnetCHARTING DefaultElement.URL

.netCHARTINGという、ASP.NETまたはWindows Formでチャートを作成するためのコンポーネントの話。

本家
http://www.dotnetcharting.com/

グラフを扱うアプリケーション作ってると、グラフ画像をクリックして、その要素の詳細を表示する処理を実行したり、別ページに遷移したりっていうことを実現したいことがよくあります。

.netCHARTINGでは、あらかじめ詳細データもChartのコントロールにセットしておき、コンポーネントの機能のみでドリルダウン動作をさせることもできるみたいですが、なんか思った通りに動かすのが難しそうなので私はあんまり使ってません。

逆に、自前で制御する場合に一番簡単なのはChart.DefaultElement.URL = ”?X=%Name&Y=%SeriesName”とかを設定してクリックした箇所の選択値を取得することですが、 ASP.NETでこいつを実装した時、グラフ画像をクリックした時に自ページのURLにQueryStringでパラメータをくっつけてGETリクエストしてしまうので、ViewStateが破棄されます。
ということは、ASP.NETで普通にページを作った場合、他のサーバコントロールと併用できないわけです。

どうにかASP.NETらしく、PostBackでグラフ選択要素をサーバ側で取得する手段 を、本家のドキュメントも探してみたんですが結局見つかっていません。
なので、ちょっと無理矢理、擬似的にPostBackしたような感じでグラフ選択値を取得するサンプルを作ってみました。

完全なソースはここに置いときます。
http://homepage.mac.com/mikaduki27/ITpanda/src/dNC.zip

ポイントは以下のような感じ。
Default.aspx

<%@ Page Language="C#" AutoEventWireup="true"  
CodeFile="Default.aspx.cs" Inherits="_Default" %>
<%@ Register TagPrefix="dotnet"  Namespace="dotnetCHARTING" 
Assembly="dotnetCHARTING"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>dNC Sample</title>
グラフ要素クリックで実行するクライアントスクリプト
    <script type="text/javascript">
        function chart_postback(_id,hidID,btn){
            //隠しフィールドにクリックした箇所の値をセット
            document.getElementById(hidID).value = _id;
            //submitボタンをクリックし、PostBack
            document.getElementById(btn).click();
        }
    </script>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <dotnet:chart id="Chart1" runat="server"></dotnet:chart>

疑似PostBackのための隠しフィールドとボタン。表示はしない。display:none;
        <div style="display:none;">
            <asp:HiddenField ID="hidID" runat="server" />
            <asp:Button ID="btn1" runat="server" Text="隠しボタン" OnClick="Button1_Click" />
        </div>
       
    </div>
    ID:<asp:Label ID="lblID" runat="server" Text=""></asp:Label><br />
    X:<asp:Label ID="lblX" runat="server" Text=""></asp:Label><br />
    Y:<asp:Label ID="lblY" runat="server" Text=""></asp:Label><br />
    </form>
</body>
</html>

Default.aspx.cs

 /// <summary>
    /// チャート表示データ
    /// </summary>
    public DataSet ChartData
    {
        set { ViewState["ChartData"] = value; }
        get { return (DataSet)ViewState["ChartData"]; }
    }

    protected void Page_Load(object sender, EventArgs e)
    {

        if (!IsPostBack)
        {
            //初回のみデータ取得
            this.ChartData = dummydata.getData1();
        }

        //いろいろ表示設定
        Chart1.Title = "My Chart";
        Chart1.YAxis.Scale = Scale.Stacked;
        Chart1.ShadingEffect = true;
        //グラフ画像の生成フォルダ
        Chart1.TempDirectory = "ChartImage";

//読み込んだDataSetには「_id」という列(連番が振ってある)があり、 DataFieldsに 「index=_id」とか書くと、後でそれをカスタム属性として設定できます。
        //DataFieldsの設定
        //"index"というカスタムパラメータを作成
        string DataFields = "xAxis=X,yAxis=Value,splitby=Y,index=_id";

        //表示するDataSetをDataEngineに読み込む
        DataEngine de = new DataEngine();
        de.Data = this.ChartData;
        de.DataFields = DataFields;
       
        SeriesCollection sc = de.GetSeries();
       
        //二重引用符がデータに含まれるとこれは使えない
        //Chart1.DefaultElement.ToolTip = "%SeriesName";

//Element.URLに「javascript: ********」と書くと、グラフ画像クリック時にクライアントスクリプトを実行できます。
//クライアントスクリプトへの引数に%SeriesName や%Nameを使用することはできますが、ブラウザで表示した際のJavascriptのエスケープを考慮しないといけないので、めんどくさいです。なので今回はあらかじめ集計結果データに連番を振り、%indexをサーバ側に返すように構成しています。
        //Elementクリック時にクライアントスクリプトを実行させる設定
        //DataEngineのDataFieldsに設定した"index"を使う -> %index
        Chart1.DefaultElement.URL = "javascript:chart_postback(\'%index\', \'" + hidID.ClientID +  "\', \'" + btn1.ClientID + "\')";

//これはオマケ。 「DefaultElement.ToolTip = “%SeriesName”」などとした場合、系列データに二重引用符が含まれると、ToolTipの表示が途切れるのを回避する
        //ToolTipの二重引用符をエスケープする
        sc = ElementConvert(sc);

        //系列を追加
        Chart1.SeriesCollection.Add(ElementConvert(sc));
    }

    /// <summary>
    /// グラフクリックにより呼び出されるボタンクリックイベント
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    protected void Button1_Click(object sender, EventArgs e)
    {
        //クライアントスクリプトで設定したindex値を取得し、ラベルに表示
        lblID.Text = hidID.Value;

//Chartに表示した元データのDataSetのindex列の値と比較して、クリックされた要素を特定する。
        //indexからグラフ表示に使用したDataSetを検索し、X/Y選択値をラベルに表示
        DataView dv = this.ChartData.Tables[0].DefaultView;
        dv.RowFilter = "_id = \'" + hidID.Value + "\'";
        if (dv.Count == 1)
        {
            lblX.Text = (string)dv[0].Row["X"];
            lblY.Text = (string)dv[0].Row["Y"];
        }

    }

テストデータの作成コード

//Chartに表示するDataSetの定義はこんな感じ。普通はDBSQLを投げると思うので、結果セットがこういう風になるようにSQLを書けばいい。
        DataSet ds = new DataSet();
        DataTable dt = new DataTable();
        dt.Columns.Add("_id", typeof(int));       
        dt.Columns.Add("X", typeof(string));
        dt.Columns.Add("Y", typeof(string));
        dt.Columns.Add("Value", typeof(int));
        ds.Tables.Add(dt);

        DataRow dr = dt.NewRow();
        dr["_id"] = "1";//この列に連番を入れていく。
        dr["X"] = "E1";
        dr["Y"] = "Series1";
        dr["Value"] = 3;
        dt.Rows.Add(dr);

       DataRow dr = dt.NewRow();
        dr["_id"] = "2";
        dr["X"] = "E1";
        dr["Y"] = "Series2";
        dr["Value"] = 5;
        dt.Rows.Add(dr);
 ・
 ・
 ・

これでグラフ画像をクリックするとPostBackし、ラベルにYとXの選択値を表示できました。
この方法だと、要素の名称に 引用符とか改行とかの記号が入っていても問題なく取得できます。