65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 ASP.NET Charting 和 MVC 中的图像映射

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.73/5 (10投票s)

2011年12月13日

CPOL

10分钟阅读

viewsIcon

82899

downloadIcon

2939

本文介绍了使用“ASP.NET Charting”工具和图像映射在浏览器中创建交互式图表的一个示例。它还演示了如何通过重用相同的代码将相同的图表添加到 PDF 文档中。

引言

本文介绍了使用“ASP.NET Charting”工具和“图像映射”在浏览器中创建交互式图表的一个示例。它还演示了如何通过重用相同的代码将相同的图表添加到 PDF 文档中。

背景

在开发 Web 应用程序时,您可以轻松找到许多帮助您在应用程序中添加图表的工具。其中大多数工具都使用客户端 JavaScript。在这些工具中,您应该很容易注意到“基于 jQuery 的图表库”和 Telerik Charting Library。它们要么基于 HTML5 Canvas,要么基于 SVG 图形。这些图表库可以将高度高级的交互式图表带入 Web 浏览器。但有时您的项目中的常见需求可能包括

  • 在 Web 浏览器中创建交互式图表;
  • 在其他地方显示相同的图表,例如在 Microsoft Word 或 PDF 文档中。

要重用相同的客户端 JavaScript 代码来满足这两个要求,这并非易事。在本文中,我将尝试使用 ASP.NET Charting 工具和 图像映射MVC 中满足这两个要求。与前面提到的图表库不同,由 ASP.NET Charting 工具生成的图表是图像。这些图像图表可以像其他库创建的图表一样美观,但它们在浏览器中支持用户交互的能力有限。本文中的示例应用程序将向您演示以下内容

  • 当与 图像映射 结合使用时,由 ASP.NET Charting 工具创建的图表应该提供一些合理的交互能力。 ASP.NET Charting 工具使我们能够轻松生成与图表图像相关的“图像映射”。您将从示例应用程序中找到如何生成 图像映射 并将它们与图表图像关联起来。
  • 除了在浏览器中显示图表外,示例应用程序还将向您展示如何使用完全相同的代码创建要嵌入到 PDF 文档中的图表。

附带的简单 MVC 2 Web 应用程序是在 Visual Studio 2010 中开发的。由于它是作为 MVC 应用程序开发的,因此我假设读者对如何使用 MVC 有一些基本了解。

SolutionExplorer.jpg

在解决方案资源管理器中所示,此 Web 应用程序的主要构建块如下

  • Models\BrowserShareModel.cs 文件是应用程序的数据模型。我们将使用此数据模型中的数据,使用 ASP.NET Charting 工具来生成图表。
  • Utilities\ChartUtilities 文件夹中的两个文件 MyChartBase.csBrowserShareChart.cs 实现将用于生成图表的类。 MyChartBase.cs 定义了图表的基本外观和感觉,而 BrowserShareChart.cs 继承自 MyChartBase 类并重写了一些方法来插入图表数据。
  • Utilities\PdfUtility.cs 文件实现了一个简单的类,用于使用上述两个类创建的图表图像来生成 PDF 文档。
  • Controllers\HomeController.cs 文件是这个简单的 MVC 应用程序的控制器,而 Views\Home\Index.aspx 文件是应用程序的视图。

在本文中,我将首先介绍应用程序的数据模型。然后,我将介绍实用工具类,并展示如何在 MVC 应用程序中使用这些类。

数据模型

应用程序的数据模型实现在 Models\BrowserShareModel.cs 文件中

using System.Collections.Generic;
using System.IO;
using MVCChart.Utilities.ChartUtilities;
 
namespace MVCChart.Models
{
    public class BrowserInformation
    {
        public string Name { get; set; }
        public double Share { get; set; }
        public string Url { get; set; }
        public string ToolTip
        {
            get
            {
                return Name + " " + Share.ToString("#0.##%");
            }
        }
    }
 
    public class BrowserShareChartData
    {
        public string Title { get; set; }
        public int Width { get; set; }
        public int Height { get; set; }
        public List<BrowserInformation> ShareData { get; set; }
 
        public MemoryStream ChartImageStream()
        {
            var chart = new BrowserShareChart(this);
            return chart.GetChartImage(Width, Height);
        }
 
        public string ChartImageMap(string name)
        {
            var chart = new BrowserShareChart(this);
            return chart.GetChartImageMap(Width, Height, name);
        }
    }
 
    public class BrowserShareRepository
    {
        public static BrowserShareChartData GetBrowserShares()
        {
            var chartData = new BrowserShareChartData()
                                {
                                    Title = "Browser usage on Wikipedia October 2011",
                                    Width = 450,
                                    Height = 300,
                                    ShareData = new List<BrowserInformation>()
                                };
 
            // The following data is the true data from Wikipedia
            chartData.ShareData.Add(new BrowserInformation()
                           {
                               Name = "IE",
                               Share = 0.342,
                               Url = "http://en.wikipedia.org/wiki/Internet_Explorer"
                           });
 
            chartData.ShareData.Add(new BrowserInformation()
                           {
                               Name = "Firefox",
                               Share = 0.236,
                               Url = "http://en.wikipedia.org/wiki/Firefox"
                           });
 
            chartData.ShareData.Add(new BrowserInformation()
                           {
                               Name = "Chrome",
                               Share = 0.206,
                               Url = "http://en.wikipedia.org/wiki/Google_Chrome"
                           });
 
            chartData.ShareData.Add(new BrowserInformation()
                           {
                               Name = "Safari",
                               Share = 0.112,
                               Url = "http://en.wikipedia.org/wiki/Safari_(web_browser)"
                           });
 
            chartData.ShareData.Add(new BrowserInformation()
                           {
                               Name = "Other",
                               Share = 0.104,
                               Url = null
                           });
 
            return chartData;
        }
    }
}

此文件实现了三个类

  • BrowserInformation 定义了图表中的一个数据点。
  • BrowserShareChartData 类定义了图表的数据。它持有一个“List”对象(其中包含“BrowserInformation”对象)的引用,用于创建图表的“Series”。它还定义了图表的宽度、高度和标题。
  • BrowserShareRepository 类实例化一个 BrowserShareChartData 对象,并为其分配了我从 Wikipedia 获取的关于 2011 年 10 月浏览器市场份额的真实数据。

在本文中,我将向您展示如何使用 ASP.NET Charting 工具创建图表来可视化浏览器市场份额信息。图表将在浏览器和 PDF 文档中显示。在浏览器中显示时,我将使用 图像映射 向图表添加工具提示和超链接,以使图表具有交互性。您应该能够使用 图像映射 创建更高级的交互,但我只会向您展示如何添加工具提示和超链接,以使本文保持简单。

图表实用工具类

Utilities\ChartUtilities\MyChartBase.cs”定义了示例应用程序中图表的基本外观和感觉

using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Web.UI.DataVisualization.Charting;
 
namespace MVCChart.Utilities.ChartUtilities
{
    public class MyChartBase
    {
        protected List<Series> ChartSeriesData { get; set; }
        protected string ChartTitle { get; set; }
 
        // This is the method to get the chart image
        public MemoryStream GetChartImage(int width, int height)
        {
            var chart = InitiateChart(width, height);
            chart.RenderType = RenderType.BinaryStreaming;
 
            var ms = new MemoryStream();
            chart.SaveImage(ms, ChartImageFormat.Png);
 
            return ms;
        }
 
        // This is the method to get the chart image map
        public string GetChartImageMap(int width, int height, string mapName)
        {
            var chart = InitiateChart(width, height);
            chart.RenderType = RenderType.ImageMap;
            chart.SaveImage(Stream.Null);
 
            return chart.GetHtmlImageMap(mapName);
        }
 
        // Override this method to add title to the chart
        protected virtual void AddChartTitle()
        {
            ChartTitle = null;
        }
 
        // Override this method to add data to the chart
        protected virtual void AddChartSeries()
        {
            ChartSeriesData = new List<Series>();
        }
 
        // Initiate the chart to be rendered
        private Chart InitiateChart(int width, int height)
        {
            var chart = new Chart();
            chart.Width = width;
            chart.Height = height;
            chart.BorderSkin.BackColor = System.Drawing.Color.Transparent;
            chart.BorderSkin.PageColor = System.Drawing.Color.Transparent;
            chart.BackColor = System.Drawing.Color.FromArgb(211, 223, 240);
            chart.BorderlineDashStyle = ChartDashStyle.Solid;
            chart.BackSecondaryColor = System.Drawing.Color.White;
            chart.BackGradientStyle = GradientStyle.TopBottom;
            chart.BorderlineWidth = 1;
            chart.Palette = ChartColorPalette.BrightPastel;
            chart.BorderlineColor = System.Drawing.Color.FromArgb(26, 59, 105);
            chart.BorderSkin.SkinStyle = BorderSkinStyle.Emboss;
            chart.AntiAliasing = AntiAliasingStyles.All;
            chart.TextAntiAliasingQuality = TextAntiAliasingQuality.Normal;
 
            AddChartTitle();
            if (ChartTitle != null)
            {
                chart.Titles.Add(CreateTitle());
            }
            chart.Legends.Add(CreateLegend());
 
            AddChartSeries();
            foreach (var series in ChartSeriesData)
            {
                chart.Series.Add(series);
            }
             
            chart.ChartAreas.Add(CreateChartArea());
            return chart;
        }
 
        // Create chart title
        private Title CreateTitle()
        {
            return new Title()
                       {
                           Text = ChartTitle,
                           ShadowColor = System.Drawing.Color.FromArgb(32, 0, 0, 0),
                           Font = new System.Drawing.Font("Trebuchet MS", 10, FontStyle.Bold),
                           ShadowOffset = 3,
                           ForeColor = System.Drawing.Color.FromArgb(26, 59, 105)
                       };
        }
 
        // configure chart Legend
        private Legend CreateLegend()
        {
            return new Legend()
            {
                Docking = Docking.Bottom,
                Alignment = StringAlignment.Center,
                BackColor = System.Drawing.Color.Transparent,
                Font = new System.Drawing.Font(new System.Drawing.FontFamily("Trebuchet MS"), 8),
                LegendStyle = LegendStyle.Row
            };
        }
 
        // Configure the chart area - the chart frame x/y axes
        private ChartArea CreateChartArea()
        {
            var area = new ChartArea()
                           {
                               Name = ChartTitle,
                               BackColor = System.Drawing.Color.Transparent,
                           };
 
            area.AxisX.IsLabelAutoFit = true;
            area.AxisX.LabelStyle.Font =
                new System.Drawing.Font("Verdana,Arial,Helvetica,sans-serif",
                                        8F, FontStyle.Regular);
            area.AxisX.LineColor = System.Drawing.Color.FromArgb(64, 64, 64, 64);
            area.AxisX.MajorGrid.LineColor = System.Drawing.Color.FromArgb(64, 64, 64, 64);
            area.AxisX.Interval = 1;
 
 
            area.AxisY.LabelStyle.Font =
                new System.Drawing.Font("Verdana,Arial,Helvetica,sans-serif",
                                        8F, FontStyle.Regular);
            area.AxisY.LineColor = System.Drawing.Color.FromArgb(64, 64, 64, 64);
            area.AxisY.MajorGrid.LineColor = System.Drawing.Color.FromArgb(64, 64, 64, 64);
            
            return area;
        }
    }
}

如果您创建此类的对象并调用 GetChartImage 方法,您将获得一个空图表图像的 MemoryStream。如果您调用 GetChartImageMap 方法,您将获得与图表图像相关的空图像映射 HTML 字符串。我在这里不详细介绍如何使用 ASP.NET Charting 工具。但如果您有兴趣,可以查看文章 ASP.NET MVC Chart Control。这是我在 MVC 环境中开始使用 ASP.NET Charting 工具的地方。

为了有意义地可视化数据,我们需要创建一个新类,该类继承自 MyChartBase 类,并重写 AddChartSeriesAddChartTitle 方法以将数据添加到图表中。以下是创建本文中浏览器市场份额图的类

using System.Collections.Generic;
using System.Web.UI.DataVisualization.Charting;
using MVCChart.Models;
 
namespace MVCChart.Utilities.ChartUtilities
{
    // This class built the browser share chart using the "ChartBase" class
    public class BrowserShareChart : MyChartBase
    {
        private BrowserShareChartData chartData;
 
        public BrowserShareChart(BrowserShareChartData chartData)
        {
            this.chartData = chartData;
        }
 
        // Add Chart Title
        protected override void AddChartTitle()
        {
            ChartTitle = chartData.Title;
        }
 
        // Override the AddChartSeries method to provide the chart data
        protected override void AddChartSeries()
        {
            ChartSeriesData = new List<Series>();
            var series = new Series()
            {
                ChartType = SeriesChartType.Pie,
                BorderWidth = 1
            };
 
            var shares = chartData.ShareData;
            foreach (var share in shares)
            {
                var point = new DataPoint();
                point.IsValueShownAsLabel = true;
                point.AxisLabel = share.Name;
                point.ToolTip = share.Name + " " + 
                      share.Share.ToString("#0.##%");
                if (share.Url != null)
                {
                    point.MapAreaAttributes = "href=\"" + 
                          share.Url + "\"";
                }
                point.YValues = new double[] { share.Share };
                point.LabelFormat = "P1";
                series.Points.Add(point);
            }
 
            ChartSeriesData.Add(series);
        }
    }
}

通过继承 MyChartBase 类,BrowserShareChart 类继承了 MyChartBase 中定义的样式。要添加用于可视化的数据

  • 它重写了 AddChartTitle 方法,为图表添加标题。
  • 它还重写了 AddChartSeries 方法,将“Series”数据添加到图表中。

您应该特别注意 AddChartSeries 方法。除了添加图表数据外,它还将工具提示和超链接添加到图表的“Series”中。如果 BrowserShareChart 对象已正确初始化并包含正确的数据,您可以调用基类继承的 GetChartImage 方法以 MemoryStream 的形式获取图表图像。您还可以调用基类中的 GetChartImageMap 来获取用于将工具提示和超链接添加到浏览器中图表的 图像映射。我将在本文后面向您展示如何使用这个 图像映射

PdfUtility 类

为了向您展示如何将图表添加到 PDF 文档中,我在“Utilities\PdfUtility.cs”文件中创建了一个小型类

using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;
 
namespace MVCChart.Utilities
{
    public class PdfUtility
    {
        // Create a simple Pdf document and add an image to it.
        public static MemoryStream GetSimplePdf(MemoryStream chartImage)
        {
            const int documentMargin = 10;
 
            var pdfStream = new MemoryStream();
            var pdfDocument = new Document(PageSize.LETTER);
            pdfDocument.SetMargins(documentMargin, documentMargin, 
                                   documentMargin, documentMargin);
            PdfWriter pdfWriter = PdfWriter.GetInstance(pdfDocument, pdfStream);
 
            Image image = Image.GetInstance(chartImage.GetBuffer());
            image.SetAbsolutePosition(documentMargin
                , pdfDocument.PageSize.Height - documentMargin - image.ScaledHeight);
 
            pdfDocument.Open();
            pdfDocument.Add(image);
            pdfDocument.Close();
            pdfWriter.Flush();
 
            return pdfStream;
        }
    }
}

这个小类用于生成一个非常简单的 PDF 文档。如果您将由 BrowserShareChart 类生成的图表的 MemoryStream 传递给 GetSimplePdf 方法,它将返回一个包含图表的 PDF 文档的 MemoryStream。此类使用 iTextSharp 来创建 PDF 文档。您可以从其网站下载 iTextSharp。我在这里不详细介绍如何使用 iTextSharp。如果您有兴趣,可以查看文章 Export Tabular Data in PDF Format Through the Web。它提供了关于如何使用 iTextSharp 的详细说明。

现在我们已经完成了数据模型和实用工具类,让我们看看如何在 MVC 应用程序中使用它们。

MVC 控制器

MVC 应用程序的控制器实现在 Controllers\HomeController.cs 文件中

using System.Web.Mvc;
using MVCChart.Models;
using MVCChart.Utilities;
 
namespace MVCChart.Controllers
{
    [HandleError]
    public class HomeController : Controller
    {
        [HttpGet]
        public ActionResult Index()
        {
            var chartData = BrowserShareRepository.GetBrowserShares();
            return View(chartData);
        }
 
        [HttpGet]
        public FileResult GetChart()
        {
            var chartData = BrowserShareRepository.GetBrowserShares();
            return File(chartData.ChartImageStream().GetBuffer()
                , @"image/png", "BrowserShareChart.png");
        }
 
        [HttpGet]
        public FileResult GetPdf()
        {
            var chartData = BrowserShareRepository.GetBrowserShares();
            var chartStream = chartData.ChartImageStream();
 
            return File(PdfUtility.GetSimplePdf(chartStream).GetBuffer()
                , @"application/pdf", "BrowserShareChart.pdf");
        }
    }
}

此 MVC 应用程序的控制器有三个“Action”方法

  • Index 方法是应用程序的入口点,它会显示 Web 应用程序的视图页面。
  • GetChart 方法调用应用程序的数据模型,并间接调用 BrowserShareChart 类中的 GetChartImage 来获取浏览器市场份额图表的 MemoryStream。然后,它将图表图像作为 FileResult 传递给浏览器。
  • GetPdf 方法更进一步。当它获取浏览器市场份额图表的 MemoryStream 时,它将其传递给 PdfUtility 类中的 GetSimplePdf 方法以获取 PDF 文档。它还以“FileResult”的形式将 PDF 文档传递给浏览器。

MVC 视图

MVC 应用程序的视图实现在“Views\Home\Index.aspx”文件中

<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<MVCChart.Models.BrowserShareChartData>" %>
 
<!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>Index</title>
    <link href="<%=Url.Content("~/content/Site.css") %>" rel="stylesheet" type="text/css" />
</head>
 
<body>
    <div style="display: inline-block">
        <div>
            <img usemap="#ChartImageMap" src="<%= Url.Action("GetChart", "Home") %>"
                alt="Asp.Net Charting generated image"
                style="width:450px;height:300px;border-width:0px;" />
            <%  Response.Write(Model.ChartImageMap("ChartImageMap"));%>
        </div>
        <div style="margin-right: 20px; text-align: right">
            <a href="<%= Url.Action("GetPdf", "Home") %>">Get the chart in Pdf</a>
        </div>
    </div>
</body>
</html>

在这个简单的视图页面中,我添加了以下内容

  • 图表图像包含在“img”标签及其“图像映射”中。
  • 我还添加了一个链接来调用控制器中的 GetPdf 方法以下载 PDF 文档。

要将 图像映射 添加到图表中,我们只需将 BrowserShareChart 类中的 GetChartImageMap 方法创建的 图像映射 放在图表图像旁边的 img 标签旁边。将 图像映射 与图表图像关联起来非常简单,但您需要确保 图像映射 的名称与图像的 usemap 属性匹配。

运行应用程序

我们已经完成了这个示例应用程序,现在可以测试运行它了。当应用程序启动时,浏览器市场份额饼图将显示在 Web 浏览器中。

RunApp.gif

如果我们把鼠标悬停在图表上,“图像映射”就会生效,我们就能看到在图表中指定的工具提示。

RunAppToolTip.gif

如果我们点击图表中的某个部分,将根据我们在图表中设置的超链接显示相应的网页。

RunAppHyperlink.gif

点击浏览器中的后退按钮回到我们的网页,然后点击“在 PDF 中获取图表”链接,将生成一个 PDF 文档,其中包含与浏览器中显示的图表相同的图表。

RunAppPdf.gif

关注点

  • 本文介绍了使用 ASP.NET Charting 工具和 图像映射 在浏览器中创建交互式图表的示例。它还演示了如何通过重用相同的代码将相同的图表添加到 PDF 文档中。
  • 有许多图表库,其中大多数可以在浏览器中创建比 ASP.NET Charting 工具更高效、更具交互性的图表。但是它们专注于在浏览器中创建图表,因此很难在其他地方使用相同的图表。当然,如果您的唯一目的是在浏览器中显示图表,我建议您使用客户端图表库,例如 Telerik Charting Library,它绝对可以提供更好的交互性和可能更好的效率。
  • 图像映射 结合使用时,ASP.NET Charting 工具可以在浏览器中创建相当具有交互性的图表。在此示例中,我仅向您展示了如何向图表添加工具提示和超链接,但您绝对可以通过在添加图表“Series”时操作 MapAreaAttributes 来使图表更具交互性。您还可以添加客户端 JavaScript 来使图表更具交互性。
  • 除了在浏览器之外创建图表,ASP.NET Charting 工具还有其他一些优点。在任何可预见的未来,我们可以安全地假设图像始终会在任何操作系统上的任何位置得到支持,因此如果您将图表创建为图像,它们将始终得到支持。
  • 使用 ASP.NET Charting 的另一个优点是,它现在已包含在 .NET Framework 的默认部署中。如果 .NET 是您的主要工作环境,您无需在项目中添加任何额外的 DLL 即可创建所需的图表。
  • 如果您正在开发桌面应用程序,您应该也能够使用 ASP.NET Charting 工具来创建图表并在您的应用程序中显示它们。
  • 尽管该示例是作为 MVC 应用程序开发的,但您可以轻松地使用相同的原理将图表添加到更传统的 ASP.NET 应用程序中。
  • 我希望您喜欢我的文章,希望本文能以某种方式帮助您。

历史

  • 2011/12/13:首次修订。
© . All rights reserved.