MSChart for .NET 使用指南






4.87/5 (22投票s)
使用 Microsoft MSChart 的指南。
引言
附带的应用程序尝试演示 MSChart 控件在典型的中小企业业务应用程序中的使用。它涵盖了金字塔图、饼图和柱状图的使用,正如它们通常在企业管理类应用程序中所使用的那样。
背景
我们公司专注于定制业务应用程序。基本上,我们开发一个通用应用程序,然后根据特定客户的需求对其进行定制。我们每四年重写这些通用应用程序,今年恰逢重大重写年份,我正在研究和测试我们可能在升级中使用的各种组件和技术。以前,我们一直使用内部开发的图表控件,但多年来,该控件已经变得相当笨拙且臃肿,因此我们希望寻找替代品。Dundas 等商业图表控件的价格超出预算,因此我偶然发现了 Microsoft 免费的图表控件 MSChart for .NET。
要求
要运行示例应用程序,您需要在计算机上安装 MSChart 控件,可以从 MSChart 下载。此控件要求您的 VS 2008 安装已更新到 SP1。最好安装 VS 2008 的 VS 插件,可以在 MSChart Add-On For VS 2008 找到。此插件要求 .NET Framework 更新到 .NET 3.5 SP1。应用程序的数据库包含在 VS 项目中,您只需在 My.Settings
中设置 ConString
设置,使其指向正确的文件位置。
应用程序详情
由于本文是关于使用 MSChart 控件而不是应用程序本身,我将简要介绍该应用程序及其工作原理。该应用程序打开时会显示一个金字塔图,显示公司总销售额按 6 个不同产品类别划分的百分比。打开时,应用程序默认显示本月的数据。左侧是两个选择面板和一个 DataGrid
。顶部选择面板允许用户选择查看哪个月的图表数据,第二个面板允许用户选择是以总销售额的百分比还是以总零售价值来查看类别。DataGrid
显示根据各种选择的实际数据。右侧是一个垂直菜单,允许用户将图表复制到剪贴板、将图表另存为图像或打印显示图表的报表。

当用户将鼠标悬停在图表的某个数据点或图例项上时,图表会突出显示特定类别。如果用户单击选定的数据点,图表将更改为甜甜圈图,显示所选类别内每个产品的百分比,第二个选择面板将提供额外的选项,显示单位销售额以及零售价值和销售额百分比。数据网格显示类别中每个产品的数值。会出现一个滑块,使用户能够按组显示百分比值小于所选金额的产品。再次,图表可以被复制、另存为图像或打印为报表。一个按钮也会出现,允许用户返回到类别视图。
在饼图中选择一个产品后,会打开第二个窗体,其中会显示一个柱状图,显示该产品过去 3 个完整月份以及当前月份的日销售额。左上角是一个信息面板,显示该产品的各种数据,左下角是一个小型柱状图,显示这些月份中该产品的总单位销售额。如果用户单击小图表中的任何月份,主图表将更改为仅显示该特定月份的数据。将出现一个按钮,允许用户将主图表重置为默认状态。再次,用户可以复制、保存或打印报表。

MSChart 控件基础
在您的计算机上安装后,图表需要在您的项目中引用 System.Windows.Forms.DataVisualization
。然后,只需将图表从“工具箱”下的“数据”类别拖到您的窗体上即可。控件基本上可以分为三个部分:图表区域、标题和图例。标题不言自明,这是您告知用户图表内容的区域。可以通过选择“标题”集合并在集合中为每个标题设置各种属性(如 Font
、ForeColor
)来在属性中设置标题。
图表外观
图表的主要外观元素可以使用属性面板进行设置。使用 BackColor
、BackSecondaryColor
和 BackHatchStyle
,您可以设计出相当不错且一致的样式。一个有趣的属性是 BorderSkin
属性,它允许您定义各种边框,并为边框设置各种属性。演示应用程序中的主图表使用了 FrameTitle1
的 SkinStyle
,带有蓝色的 BackColor
和 VerticalCentre
的 BackGradientStyle
。我发现最重要的事情之一是让 BackGradientStyle
等属性在整个设计中保持一致,以获得更好的外观。我发现将 CharttArea.BackColor
和 Legend.BackColor
设置为 Transparent
会获得最佳效果。要选择用于绘制数据点的调色板,您可以使用预定义的调色板,方法是使用 Palette
属性,或者要定义自己的 Palette
,您可以使用 PaletteCustomColor
集合来定义各种颜色,演示应用程序的主图使用一系列蓝色。如果您确实定义了自己的调色板,则需要将 Palette
属性设置为 None
,否则它将使用 Palette
属性作为默认值。有两个属性可用于确定各种元素的质量。文本渲染质量可以使用 TextAntiAliasingQualityProperty
设置,阴影平滑可以使用 IsSoftShadows enum
设置。
图表特性
系列文章
系列是图表控件中一切发生的地方。一个系列包含数据点的集合。一个图表可以有无限数量的系列,每个系列都可以设置为不同的图表类型(在合理范围内),并且可以单独设置其外观属性。系列中的数据点也可以设置其单独的外观属性,在这种情况下,数据点外观优先于所属系列。每个数据点包含一个 X 值,该值决定了数据点在图表上的位置,以及一个或多个 Y 值,具体取决于图表类型。每种类型的图表都有可以设置为 CustomAttributes
属性的自定义属性。在运行时设置自定义属性的示例在 frmChartExample
的 GraphSetUp
函数中显示,我们在其中通过滑动窗体上的滑块来设置组阈值限制。
mscSales.Series(0)("CollectedThreshold") = tbGroupPercentage.Value
有多种方法可以填充您的系列,具体取决于您的数据来源。可能的数据源包括 DataView
、DataReader
、DataSet
、DataRow
、Arrays
、Lists DataAdapters
以及其他来源,实际上任何实现 IEnumerable
接口的对象都可以使用。在演示应用程序中,我使用了两种不同类型的数据源。
在主图中,我创建了一个自定义 DataPoints
列表。然后,我手动遍历列表,在每次迭代中添加每个 X 和 Y 值。
For intDataPoints As Integer = 0 To lstDataPoints.Count - 1
mscSales.Series(0).Points.AddXY(lstDataPoints(intDataPoints).DataKey, _
Format(lstDataPoints(intDataPoints).DataValue, "#,##0.00"))
Dim strRow() As String = {lstDataPoints(intDataPoints).DataKey, _
Format(lstDataPoints(intDataPoints).DataValue, "#,##0.00")}
decGross += lstDataPoints(intDataPoints).DataValue
dgvDetails.Rows.Add(strRow)
Next
在第二个窗体上,我已将主图绑定到从 Product
类函数返回的 DataTable
。您在这里需要做的就是将 DataSource
属性设置为 DataTable
,然后将 X
和 Y ValueMembers
属性设置为 DataTable
中的正确列,并像这样在图表控件上调用 DataBind
。
Dim dtMasterSales As DataTable = prdCurrent.DailySales_
(dteStartDate, dteEndDate, My.Settings.ConString)
mscDailySales.DataSource = dtMasterSales
mscDailySales.Series(0).XValueMember = "Date"
mscDailySales.Series(0).YValueMembers = "UnitsSold"
mscDailySales.DataBind()
图表交互
有许多函数可用于与您的图表进行交互,例如选择、钻取、缩放和滚动。有两种主要事件用于处理这些功能,即 Chart.MouseMove
和 Chart.MouseDown
事件处理程序。这些函数的使用非常相似,即对鼠标位置进行 HitTest
,确定鼠标是否位于 DataPoint
、LegendItem
、Title
等之上,然后根据用户单击的位置执行您的操作。
选择
在演示应用程序中,如果用户将鼠标悬停在任何 DataPoint
或 LegendItem
上,数据点的边框将变为白色并变粗。当用户移开时,数据点会恢复正常。
Private Sub chSales_MouseMove(ByVal sender As Object, _
ByVal e As System.Windows.Forms.MouseEventArgs) Handles mscSales.MouseMove
Dim htrResult As HitTestResult = mscSales.HitTest(e.X, e.Y)
'Go through points setting design elements back to default
For Each dp As DataPoint In mscSales.Series(0).Points
dp.BackSecondaryColor = Color.White
dp.BackHatchStyle = ChartHatchStyle.None
dp.BorderWidth = 0
Next dp
'If users mouse hovers over a datapoint or it's equivalent
'Legend Item then set cursor to hand to indicate that it is a link
'Also we use some design elements to indicate which DataPoint is active
If htrResult.PointIndex >= 0 Then
If htrResult.ChartElementType = ChartElementType.DataPoint _
Or htrResult.ChartElementType = ChartElementType.LegendItem Then
Me.Cursor = Cursors.Hand
Dim dpSelected As DataPoint
dpSelected = mscSales.Series(0).Points(htrResult.PointIndex)
dpSelected.BackSecondaryColor = Color.Black
dpSelected.BorderColor = Color.White
dpSelected.BorderWidth = 1
End If
Else
'Set cursor back to default when leaving selected datapoint
Me.Cursor = Cursors.Default
End If
End Sub
缩放
缩放是通过在系列的每个轴上启用 Zoomable 来实现的。这可以在设计器中完成,也可以在运行时完成。
Chart.ChartAreas(0).AxisX.ScaleView.Zoomable = True
除了设置缩放之外,还有各种选项可以设置,例如确定缩放后滚动条的位置。
Chart.ChartAreas(0).AxisX.ScrollBar.IsPositionedInside = True
钻取
通过确定用户选择的数据点并根据结果做出选择来实现图表的钻取。在演示应用程序中,用户可以选择一个特定的产品类别,并且根据所选类别,将显示一个新的甜甜圈图,显示该类别内产品的相关数据。
Private Sub chSales_MouseDown(ByVal sender As Object, _
ByVal e As System.Windows.Forms.MouseEventArgs) Handles mscSales.MouseDown
Dim htrResult As HitTestResult = mscSales.HitTest(e.X, e.Y)
If htrResult.ChartElementType = _
ChartElementType.DataPoint And htrResult.PointIndex >= 0 Then
SetGraphMonth()
If gsCurrentStage = GraphStage.Pyramid Then
gsCurrentStage = GraphStage.Doughnut
CurrentCategory = lstCategories(htrResult.PointIndex)
tbGroupPercentage.Value = 3
rbtnUnitSales.Visible = True
SetUpGraph()
Else
Dim newProductGraph As New ProductSales(CType_
(lstDataPoints(htrResult.PointIndex).Tag, Product))
newProductGraph.Show()
End If
End If
End Sub
工具提示
工具提示可以设置在图表的各种不同方面,例如数据点、图例项、标题等。这些可以在设计器中设置,也可以在运行时设置,只需检测选择了哪个区域,然后设置工具提示。这一切都在 Chart.GetToolTip
事件处理程序中完成。
Private Sub chSales_GetToolTipText(ByVal sender As Object, _
ByVal e As System.Windows.Forms.DataVisualization.Charting.ToolTipEventArgs) _
Handles mscSales.GetToolTipText
Select Case e.HitTestResult.ChartElementType
Case ChartElementType.DataPoint
If e.HitTestResult.PointIndex >= 0 Then
Dim strKey As String = lstDataPoints_
(e.HitTestResult.PointIndex).DataKey
Dim strValue As String = Format(lstDataPoints_
(e.HitTestResult.PointIndex).DataValue, "#,##0.00")
e.Text = strKey & ", " & strValue
End If
End Select
End Sub
打印
图表控件使用 PrintManager
对象来管理打印,该对象封装在 Chart.Printing
属性中。PrintManager
有几个用于非常基本的打印操作的方法,例如 PageSetup
、PrintPreview
和 Print
。这些都相当直观,尽管应该指出,Print
方法只会在您的纸上打印图表的图片,而不会打印其他任何内容。尽管我为此花了很多时间,但我无法让 Print
方法很好地打印,除非诸如边框等内容非常简单。我发现打印基于图表的报表的最佳方法是设置一个 PrintPage
例程来处理打印,并将一个 Delegate
添加到处理特定图表的 PrintPage
事件。在 PrintPage
例程中,您可以按照自己的喜好设计报表。
Private Sub PrintPage(ByVal sender As Object, ByVal ev As PrintPageEventArgs)
'' Calculate title string position
Dim titlePosition As New Rectangle(ev.MarginBounds.X, ev.MarginBounds.Y, _
ev.MarginBounds.Width, ev.MarginBounds.Height)
Dim stream As New System.IO.MemoryStream() ' Create a memory stream to
' save the chart image
CurrentGraph.SaveImage(stream, System.Drawing.Imaging.ImageFormat.Bmp) ' Save the
' chart image to the stream
Dim bmp As New Bitmap(stream) ' Create a bitmap using the stream
Dim recPic As New Rectangle(ev.MarginBounds.X,
ev.MarginBounds.Y, bmp.Width, bmp.Height)
Dim fontTitle As New Font("Times New Roman", 16)
Dim chartTitle As String = String.Empty
If CurrentGraph.Titles.Count > 0 Then
chartTitle = CurrentGraph.Titles(0).Text
End If
'Dim titleSize As SizeF = ev.Graphics.MeasureString(chartTitle, fontTitle)
' titlePosition.Height = CInt(titleSize.Height)
'' Draw charts title
'Dim format As New StringFormat()
'format.Alignment = StringAlignment.Center
'ev.Graphics.DrawString(chartTitle, fontTitle,
Brushes.Black, titlePosition, format)
'' Calculate first chart position rectangle
Dim chartPosition As New Rectangle(ev.MarginBounds.X, titlePosition.Bottom,
CurrentGraph.Size.Width, CurrentGraph.Size.Height)
'' Align first chart position on the page
Dim chartWidthScale As Single = 1 ' CSng(ev.MarginBounds.Width)
' / 2.0F / CSng(chartPosition.Width)
Dim chartHeightScale As Single = 1 ' CSng(ev.MarginBounds.Height) /
' CSng(chartPosition.Height)
chartPosition.Width = CInt(chartPosition.Width * _
Math.Min(chartWidthScale, chartHeightScale))
chartPosition.Height = CInt(chartPosition.Height * _
Math.Min(chartWidthScale, chartHeightScale))
' Draw first chart on the printer graphics
'CurrentGraph.Printing.PrintPaint(ev.Graphics, chartPosition)
ev.Graphics.DrawImage(bmp, recPic)
End Sub
正如您所见,我在这部分花了很多时间,尝试了各种不同的方法。
将图表保存为图像文件
您可以使用图表控件的 SaveImage
方法将图表保存为各种不同类型的图像,例如位图、TIFF、JPG、PNG 等。此方法接受两个参数,要保存的图像文件名,以及一个 ChartImageFormat enum
,用于指定图像类型,即 BMP、PNG 等。
结论
这个图表控件似乎可以处理大多数简单,甚至一些相当复杂的情况。控件的使用非常简单,大多数属性和方法似乎都相当直观且命名得当。要让一个团队掌握这个控件,真的只需要几天时间,而且我认为结果相当令人满意。考虑到替代品的成本,或者构建自己的控件的成本和时间因素,我认为这是一个相当不错的选择。
历史
- 2010 年 3 月 16 日:初始发布