WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

MVC で Chart を利用する方法

by WebSurfer 2018年5月23日 16:51

ASP.NET Web Forms アプリ用のサーバーコントロールである Chart を MVC アプリで利用する方法を書きます。

MVC アプリで Chart 利用

一般的に、Web Forms 用のサーバーコントロールは、MVC でも ViewState とポストバックを使わない範囲であれば利用できます。

Chart の場合、要求を受けてグラフの画像ファイルを生成するという操作には ViewState とポストバックは不要ですので、MVC でも利用できます。

問題は Chart が生成した画像ファイルを、どのようにブラウザが取得して表示できるかということです。

ASP.NET Web Forms アプリの場合、Chart が生成した画像ファイルはサーバーの c:\TempImageFiles フォルダに一時保存されます。そして、.aspx ページ上で Chart を配置した位置に img 要素がレンダリングされ、その src 属性に画像ファイルを取得するための HTTP ハンドラ設定されます。

ブラウザがページを受信すると、ブラウザは img 要素の src 属性に設定された HTTP ハンドラをサーバーに要求します。要求を受けたサーバーは HTTP ハンドラを使って一時保存された画像ファイルを取得してブラウザに送信し、ブラウザは受信した画像を img 要素の位置に表示するという仕組みになっています。詳しくは、先の記事「Chart」を見てください。

MVC アプリでも .aspx 形式の View を使う場合は Web Forms アプリと同等なことができると思いますが(未検証・未確認です)、最近の主流らしい Razor 形式の View の場合は img 要素のレンダリングが問題です。

ではどうするかと言うと、Web Forms アプリのような HTTP ハンドラは利用せず、Controller のアクションメソッドで画像をダウンロードするようにし、img 要素は自分で View に書いて、その src 属性にアクションメソッドの url を設定してやります。

アクションメソッドでは、動的に Chart を生成し、Chart.SaveImage メソッドを使って画像データを MemoryStream に保存し、Controller.File メソッド (Byte[], String) で画像のバイト列を応答ストリームに書き出すようにします。

先の記事「Chart」と同じ SQL Server のデータから、同じグラフ画像を生成してダウンロードさせるアクションメソッドのサンプルを以下に書いておきます。このアクションメソッドの url を img 要素の src 属性に設定した結果が上の画像です。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using AdventureWorksLT;
using Mvc5App.Extensions;
using System.ComponentModel.DataAnnotations;

// Chart を利用するコードのため以下を追加する
using System.Web.UI.DataVisualization.Charting;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.IO;

namespace Mvc5App.Controllers
{
  public class HomeController : Controller
  {        
    public ActionResult Chart()
    {
      SqlDataSource sds = new SqlDataSource()
      {
        ID = "SqlDataSource1",
        ConnectionString = @"接続文字列",
        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"
      };

      Chart chart = new Chart()
      {
        ID = "Chart1",
        Width = 600,
        DataSource = sds.Select(DataSourceSelectArguments.Empty)
                
        // 注:以下のようにするとエラー
        // DataSourceID = "SqlDataSource1"
      };

      Legend legend = new Legend()
      {
        DockedToChartArea = "ChartArea1",
        IsDockedInsideChartArea = false,
        Name = "Legend1"
      };
      chart.Legends.Add(legend);

      Series series = new Series()
      {
        Name = "三吉",
        ChartType = SeriesChartType.StackedColumn,
        CustomProperties = "DrawingStyle=Cylinder",
        IsValueShownAsLabel = true,
        Label = "#PERCENT{P1}",
        Legend = "Legend1",
        XValueMember = "Month",
        YValueMembers = "三吉"
      };
      chart.Series.Add(series);

      series = new Series()
      {
        Name = "春日",
        ChartType = SeriesChartType.StackedColumn,
        CustomProperties = "DrawingStyle=Cylinder",
        IsValueShownAsLabel = true,
        Label = "#PERCENT{P1}",
        Legend = "Legend1",
        XValueMember = "Month",
        YValueMembers = "春日"
      };
      chart.Series.Add(series);

      series = new Series()
      {
        Name = "東雲",
        ChartType = SeriesChartType.StackedColumn,
        CustomProperties = "DrawingStyle=Cylinder",
        IsValueShownAsLabel = true,
        Label = "#PERCENT{P1}",
        Legend = "Legend1",
        XValueMember = "Month",
        YValueMembers = "東雲"
      };
      chart.Series.Add(series);

      series = new Series()
      {
        Name = "府中",
        ChartType = SeriesChartType.StackedColumn,
        CustomProperties = "DrawingStyle=Cylinder",
        IsValueShownAsLabel = true,
        Label = "#PERCENT{P1}",
        Legend = "Legend1",
        XValueMember = "Month",
        YValueMembers = "府中"
      };
      chart.Series.Add(series);

      series = new Series()
      {
        Name = "広島",
        ChartType = SeriesChartType.StackedColumn,
        CustomProperties = "DrawingStyle=Cylinder",
        IsValueShownAsLabel = true,
        Label = "#PERCENT{P1}",
        Legend = "Legend1",
        XValueMember = "Month",
        YValueMembers = "広島"
      };
      chart.Series.Add(series);

      ChartArea chartArea = new ChartArea()
      {
        Name = "ChartArea1",
        AxisY = new Axis() { Title = "売上高" },
        AxisX = new Axis() { Title = "売上月" }
      };
      chart.ChartAreas.Add(chartArea);

      using (var ms = new MemoryStream())
      {
        chart.SaveImage(ms, ChartImageFormat.Png);

        // キャッシュを許可するか否か、許可する場合は有効期限を
        // 指定しておくべき。
        // 以下のコードはキャッシュを許可しない場合の例。応答ヘ
        // ッダーは次のようになる。
        //    Cache-Control: no-cache
        //    Pragma: no-cache
        //    Expires: -1
        Response.Cache.SetCacheability(HttpCacheability.NoCache);
        Response.Cache.SetExpires(DateTime.Now.ToUniversalTime());
        Response.Cache.SetMaxAge(new TimeSpan(0, 0, 0, 0));

        return File(ms.ToArray(), "image/png");

        // File の第 3 引数を以下のように設定すると応答ヘッダに
        // Content-Disposition: attachment; filename=chart.png
        // が含まれるようになる。
        // return File(ms.ToArray(), "image/png", "chart.png");
      }
    }
  }
}

先の記事「Chart」で書きました Web Forms アプリでは、Visual Studio によって自動的に web.config に HTTP ハンドラの定義や appSettings の設定が追加されますが、それらは一切不要です。

ただし、System.Web.DataVisualization の参照への追加が必要です。using 句を追加するだけではダメです。

参照の追加

Tags:

MVC

About this blog

2010年5月にこのブログを立ち上げました。その後 ブログ2 を追加し、ここは ASP.NET 関係のトピックス、ブログ2はそれ以外のトピックスに分けました。

Calendar

<<  2018年6月  >>
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

View posts in large calendar