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

一个灵活的 .NET 图表库

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.70/5 (1106投票s)

2003 年 11 月 13 日

30分钟阅读

viewsIcon

10787107

downloadIcon

180629

正在寻找一种用 C# 绘制 2D 折线图的方法?这是又一个图表类库,具有高度的可配置性,并且易于使用。

引言

ZedGraph 是一个类库、Windows 窗体用户控件和 ASP Web 可访问控件,用于为任意数据集创建 2D 折线图、条形图和饼图。这些类提供了高度的灵活性——图表的几乎所有方面都可以由用户修改。同时,通过为所有图表属性提供默认值,类的使用保持简单。这些类包括根据绘制数据的范围值来选择适当的比例范围和步长大小的代码。此外,ZedGraph 与 .NET 2.0 和 VS .NET 2005 兼容。

ZedGraph 作为开源开发项目托管在 SourceForge 上。该网站包括一个 项目 Wiki文档临时(CVS)更新,以及所有 发布版本

Wiki 上也提供了一组 示例图表,并附带源代码(许多示例包含 C# 和 VB 代码)。

ZedGraph 现在支持 .NET 2.0 和 .NET 1.1。

  • 对于 .NET 2.0,请使用 ZedGraph 版本 5.0+。
  • 对于 .NET 1.1,请使用 ZedGraph 版本 4.5+。

背景

市面上有很多绘图库,但似乎没有一个符合我的需求。我发现 MSChart 太过古怪,而许多其他选项又缺乏我实现精美外观所需的灵活性。当然,大多数商业软件包都能做到,但我需要免费的东西。于是,ZedGraph 就诞生了。

这些类将在窗体或网页上生成各种折线图、条形图或饼图,给定一个矩形位置和一些数据点。ZedGraph 支持 2D 折线/散点图、水平/垂直条形图、堆积条形图、百分比堆积条形图、误差条形图、开盘-最高-最低-收盘图、日式蜡烛图和饼图——但尚未支持 2.5D 或 3D 曲面或图表。图表可以通过坐标轴标签和标题、图例、文本标签和箭头(如上例所示)、图像等进行美化。

ZedGraph Wiki》和《在线类文档》提供了许多有用的提示和描述。有关更多详细信息,请参阅它们——ZedGraph 拥有大量在本教程中未涵盖的选项。

VB 用户

本文仅在示例中使用 C#,但是,教程的所有代码示例以及其他示例都可以在 ZedGraph Wiki 示例图表部分 中找到 Visual Basic 版本。

将 ZedGraph 用作 Web 控件

ZedGraph 现在包含一个派生自 Control 类的类,该类便于从 ASPX 网页代码访问。上面的演示项目下载演示了此功能。此外,《ZedGraph Wiki》提供了两种渲染模式下 Web 控件使用的详细示例:RawImageImageTag

将 ZedGraph 用作用户控件

ZedGraph 可以作为控件从 Visual Studio .NET 的控件工具箱中访问。要访问 ZedGraph,请先启动 Visual Studio .NET,然后创建一个新的 Windows 应用程序(窗体)项目。打开窗体设计,使其出现在当前窗口中。使用“视图/工具箱”菜单命令查看工具箱。右键单击工具箱中的“常规”或“组件”子窗格,然后选择“选择项...”选项。单击“浏览...”,然后导航到 zedgraph.dll 文件。添加此文件后,您应该会在工具箱中看到 ZedGraphControl 选项。

  1. 在您的项目中,在项目菜单下,选择“添加引用...”选项。使用浏览按钮查找 ZedGraph.dll,然后单击“确定”。对 ZedGraph.Web.dll 重复此操作。这将把 ZedGraph 的所有功能包含到您的项目中。
  2. 在主窗体代码中添加一个 using ZedGraph; 条目。
  3. 在窗体设计器中,将 ZedGraphControl 从工具箱拖放到窗体上,并根据需要进行拖放和调整大小。现在您的窗体中就有了一个 ZedGraph 控件。
  4. ZedGraph 的所有功能都可以通过 ZedGraphControl.MasterPane 属性进行访问。还提供了一个 ZedGraphControl.GraphPane,它只是引用 MasterPane 列表中的第一个 GraphPane(稍后会解释)。
  5. 在窗体设计器中,双击窗体(而不是 ZedGraphControl)。这将在您的代码文件中放置一个 Form1_Load() 模板。
  6. 再次,在窗体设计器中,选择窗体,转到“属性”,选择黄色的闪电图标以查看事件,将光标放在 Resize 事件右侧的框中,然后按 Enter 键为您的代码文件添加一个 Form1_Resize 事件方法。
  7. 修改 Form1_Load()Form1_Resize 方法,并添加 CreateGraph()SetSize() 方法,如下所示(此代码假定您的控件名称为 'zedGraphControl1')。<注意:包含此代码的完整解决方案可从 ZedGraph Wiki 下载
    // Respond to the form 'Resize' event
    private void Form1_Resize( object sender, EventArgs e )
    {
       SetSize();
    }
    
    // SetSize() is separate from Resize() so we can 
    // call it independently from the Form1_Load() method
    // This leaves a 10 px margin around the outside of the control
    // Customize this to fit your needs
    private void SetSize()
    {
       zedGraphControl1.Location = new Point( 10, 10 );
       // Leave a small margin around the outside of the control
       zedGraphControl1.Size = new Size( ClientRectangle.Width - 20,
                               ClientRectangle.Height - 20 );
    }
    
    // Respond to form 'Load' event
    private void Form1_Load( object sender, EventArgs e )
    {
       // Setup the graph
       CreateGraph( zedGraphControl1 );
       // Size the control to fill the form with a margin
       SetSize();
    }
    
    // Build the Chart
    private void CreateGraph( ZedGraphControl zgc )
    {
       // get a reference to the GraphPane
       GraphPane myPane = zgc.GraphPane;
    
       // Set the Titles
       myPane.Title.Text = "My Test Graph\n(For CodeProject Sample)";
       myPane.XAxis.Title.Text = "My X Axis";
       myPane.YAxis.Title.Text = "My Y Axis";
    
       // Make up some data arrays based on the Sine function
       double x, y1, y2;
       PointPairList list1 = new PointPairList();
       PointPairList list2 = new PointPairList();
       for ( int i = 0; i < 36; i++ )
       {
          x = (double)i + 5;
          y1 = 1.5 + Math.Sin( (double)i * 0.2 );
          y2 = 3.0 * ( 1.5 + Math.Sin( (double)i * 0.2 ) );
          list1.Add( x, y1 );
          list2.Add( x, y2 );
       }
    
       // Generate a red curve with diamond
       // symbols, and "Porsche" in the legend
       LineItem myCurve = myPane.AddCurve( "Porsche",
             list1, Color.Red, SymbolType.Diamond );
    
       // Generate a blue curve with circle
       // symbols, and "Piper" in the legend
       LineItem myCurve2 = myPane.AddCurve( "Piper",
             list2, Color.Blue, SymbolType.Circle );
    
       // Tell ZedGraph to refigure the
       // axes since the data have changed
       zgc.AxisChange();
    }

    请注意,每当您添加或更改数据时,都必须调用 AxisChange() 方法。这会告诉 ZedGraph 继续重新计算所有坐标轴范围。(注意AxisChange() 只做这些——您可以随时调用它,它会根据当前数据集重新计算坐标轴范围。如果您不希望重新缩放坐标轴,也可以避免调用 AxisChange()。)

上面的代码将生成以下输出

增强图表

ZedGraph 允许您以多种方式修改图表属性。图表的每个部分都封装在类结构中,该结构具有可修改的属性来控制输出。以下是 ZedGraph 中提供的一些类(请注意,这些类已进行 XML 文档注释。有关每个类的详细信息,请参阅 ZedGraph 文档)。

描述
MasterPane 一个用于管理多个 GraphPane 对象的类,它继承自 PaneBaseMasterPane 类的使用是可选的,因为 GraphPane 类可以直接用于单个窗格。它还提供了用于布局、排列和管理单个 GraphPane 对象的函数。
GraphPane 图表的主要类,继承自 PaneBase。它包含所有其他类作为属性。它还控制窗格标题、窗格边框和坐标轴边框、背景等。
XAxisYAxisY2Axis Axis 类的子类。这些类包含坐标轴显示的许多方面,包括刻度线、网格、颜色、画笔、字体、标签和样式。
Scale Axis 类维护的类实例。它包含比例范围、步长大小、格式和比例的显示选项。它有适用于 LinearLogTextDateOrdinalExponentLinearAsOrdinalDateAsOrdinal 比例的变体。
图例 描述用于绘制图例的位置、字体、颜色等的类。
CurveItem 一个 abstract 基类,其中包含单个曲线的数据。LineItemBarItemHiLowBarItemErrorBarItemPieItemStickItemOHLCBarItemJapaneseCandleStickItem 都派生自此类。
CurveList 一个集合类,用于维护 CurveItem 对象列表。列表中曲线的顺序控制着绘制的 Z 顺序。列表中的最后一条曲线将出现在所有其他曲线后面。
GraphObj 一个 abstract 基类,其中包含绘图上各种附加图形对象的定位信息。TextObjImageObjLineObjArrowObjEllipseObjBoxObjPolyObj 派生自 GraphObj
GraphObjList 一个集合类,用于维护 GraphObj 对象列表。对象在列表中的顺序以及 ZOrder 属性控制着绘制的 Z 顺序。列表中的最后一个对象将出现在具有相同 ZOrder 值的其他所有对象后面。
FontSpec 一个实用类,包含有关图表上文本的字体系列、颜色、角度、大小、样式、边框和背景填充的信息。每个包含文本信息的类都将包含一个或多个 FontSpec 对象来专门描述关联的字体。
Fill 一个实用类,包含背景颜色填充的特性。每个具有颜色填充功能的都会包含一个或多个 Fill 对象来专门描述关联的颜色填充。
Border 一个包含对象边框特性的实用类。每个具有边框功能的都会包含一个或多个 Border 对象来专门描述关联的边框颜色和线属性。
Location 一个用于处理绘图上图形对象位置的通用类。
PointPair 一个数据 struct,它封装了一对 double 值,代表一个 (X,Y) 数据点。这是 CurveItem 中值数组的内部数据存储格式。
PointPairList 一个集合类,用于维护 PointPair 对象列表。
XDate 此类封装了单个日期时间值(存储为 System.Double),以及大量方法,用于在 XL 日期、天文儒略日、公历日期、小数年等之间进行转换。有关详细信息,请参阅下面关于日期时间坐标轴的讨论。

通过访问上述每个类中的属性来修改图表。例如,如果您在 CreateGraph() 方法中包含以下代码行(在前面显示的示例代码之后),图表将相应地进行修改

// Change the color of the title
myPane.Title.FontSpec.FontColor = Color.Green;

// Add gridlines to the plot, and make them gray
myPane.XAxis.MajorGrid.IsVisible = true;
myPane.YAxis.MajorGrid.IsVisible = true;
myPane.XAxis.MajorGrid.Color = Color.LightGray;
myPane.YAxis.MajorGrid.Color = Color.LightGray;

// Move the legend location
myPane.Legend.Position = ZedGraph.LegendPos.Bottom;

// Make both curves thicker
myCurve.Line.Width = 2.0F;
myCurve2.Line.Width = 2.0F;

// Fill the area under the curves
myCurve.Line.Fill = new Fill( Color.White, Color.Red, 45F );
myCurve2.Line.Fill = new Fill( Color.White, Color.Blue, 45F );

// Increase the symbol sizes, and fill them with solid white
myCurve.Symbol.Size = 8.0F;
myCurve2.Symbol.Size = 8.0F;
myCurve.Symbol.Fill = new Fill( Color.White );
myCurve2.Symbol.Fill = new Fill( Color.White );

// Add a background gradient fill to the axis frame
myPane.Chart.Fill = new Fill( Color.White,
    Color.FromArgb( 255, 255, 210 ), -45F );

// Add a caption and an arrow
TextObj myText = new TextObj( "Interesting\nPoint", 230F, 70F );
myText.FontSpec.FontColor = Color.Red;
myText.Location.AlignH = AlignH.Center;
myText.Location.AlignV = AlignV.Top;
myPane.GraphObjList.Add( myText );
ArrowObj myArrow = new ArrowObj( Color.Red, 12F, 230F, 70F, 280F, 55F );
myPane.GraphObjList.Add( myArrow );

最终“装饰”好的图表将如下所示

可以修改许多其他的图表属性,但在本文中列出所有属性会很繁琐。请参阅在线文档以获取更多详细信息。另请参阅 Wiki 上的演示图表以获取代码示例。

有趣的细节

绘制折线图并非难事,但绘图类中有一些方面被证明很有趣。GDI+ 绘图库的变换矩阵提供了极大的灵活性,非常酷。这使得可以使用相同的代码来绘制所有三个坐标轴。坐标系统被变换以适应坐标轴的位置和方向。在每种情况下,坐标系统都会被平移和旋转,以便坐标轴沿着 X 方向定向,并且原点位于坐标轴的左边缘(从标签侧看)。为了处理 X 和 Y2 轴的“左”侧是最小值,“左”侧是 Y 轴最大值的事实,需要一些“if”异常。

选择比例

范围和步长大小

ZedGraph 设置为根据曲线的数据值范围自动选择合适的比例最小值、最大值和步长大小。或者,您可以手动设置任何或所有值,比例选择逻辑将尝试为留在自动模式下的其余参数选择适当的值。比例选择逻辑基于一个假设,即最易于人类理解的步长大小是 10 的偶数除数。也就是说,步长大小应为 1、2 或 5 乘以 10 的某个幂。比例选择逻辑的核心在 CalcStepSize() 方法中。

protected double CalcStepSize( double range, double targetSteps )
{
    // Calculate an initial guess at step size
    double tempStep = range / targetSteps;

    // Get the magnitude of the step size
    double mag = Math.Floor( Math.Log10( tempStep ) );
    double magPow = Math.Pow( (double) 10.0, mag );

    // Calculate most significant digit of the new step size
    double magMsd =  ( (int) (tempStep / magPow + .5) );

    // promote the MSD to either 1, 2, or 5
    if ( magMsd > 5.0 )
        magMsd = 10.0;
    else if ( magMsd > 2.0 )
        magMsd = 5.0;
    else if ( magMsd > 1.0 )
        magMsd = 2.0;

    return magMsd * magPow;
}

步长大小的初始猜测是使用默认步数 targetSteps(此值默认为 7)计算的,这是坐标轴上您想要的典型主要步数。实际步数通常会等于此数字或更多。初始步长大小的量级是通过 Floor( Log10( stepSize ) ) 计算的。此量级会减少到最显著的数字,然后该数字会提升到 1、2 或 5,以使比例成为 10 的偶数除数。总的来说,我认为此逻辑在根据数据范围选择比例方面非常出色。

零杠杆

比例选择的另一个方面是比例是否应扩展以包含零值。例如,如果比例范围是 1 到 10,您通常会希望将其从零开始。这是通过 ZeroLever 默认参数实现的。ZeroLever 是比例范围可以扩展以包含零值的允许分数。这适用于正比例的比例范围下方,或负比例值的比例范围上方。例如,如果 ZeroLever 为 0.25 且数据范围为 2.0 到 12.0,则比例将扩展到 0.0 到 12.0 的范围,因为零值位于实际数据范围(在允许的 25% 以内)的 20% 之外。

Grace

最后,ZedGraph 包含“grace”(余量)属性,允许您在最小和最大数据值之前和之后包含额外的空间。这样做的原因是为了避免最小/最大数据点正好落在坐标轴上。请注意,余量值仅适用于非序数坐标轴类型(AxisType.LogAxisType.LinearAxisType.Date)。余量值由 Axis.Scale.MinGraceAxis.Scale.MaxGrace 控制。这些值表示为总数据范围的分数。例如,如果 MinGraceMaxGrace 都设置为 0.1,则会在实际数据范围之前和之后填充 10% 的数据范围。例如,如果数据值从 50.0 到 150.0,则数据范围为 100.0。此范围的 10% 是 10.0。将此添加到范围之前和之后,有效数据范围为 40.0 到 160.0。余量属性不会导致范围跨越零点。也就是说,如果最小和最大数据值都大于或等于零,则无论余量设置如何,坐标轴都不会扩展到负值。MinGraceMaxGrace 的默认值分别为 Scale.Default.MinGraceScale.Default.MaxGrace。这两个值最初都设置为 0.1。

坐标轴类型

每个坐标轴现在都有一个名为 Type 的属性,可以设置为以下八个值之一

  • AxisType.Linear:这是默认类型,就是普通的笛卡尔坐标轴。
  • AxisType.Log:这是以 10 为底的对数刻度。
  • AxisType.Exponent:这是指数刻度。
  • AxisType.Date:这是一个日期时间坐标轴,其中对应的值是 XDate 类型。请参阅下面的日期时间坐标轴。
  • AxisType.Text:这是一种序数类型,其中此坐标轴的数据值实际上未使用。所有点或条形图在图表上都会均匀分布。第一个点的值为 1.0,第二个为 2.0,依此类推。实际的值标签将由 Axis.TextLabels 中的字符串数组确定。请参阅下面的文本坐标轴
  • AxisType.Ordinal:这是另一种序数类型,其中此坐标轴的数据值未使用。所有点或条形图在图表上都会均匀分布。第一个点的值为 1.0,第二个为 2.0,依此类推。实际的值标签仅由数字序数值(1.0、2.0 等)确定。
  • AxisType.LinearAsOrdinal:这是另一种序数类型,本质上与 AxisType.Ordinal 相同,但坐标轴刻度值将根据数据值显示(尽管坐标轴仍将被视为序数,第一个标签为 1.0,第二个为 2.0 等)。
  • AxisType.DateAsOrdinal:这是另一种序数类型,本质上与 AxisType.Ordinal 相同,但坐标轴刻度值将根据数据值显示为日期。当您有工作日值并且想跳过周末而不留下数据间隙时,此模式是一个不错的选择。

日期时间坐标轴

ZedGraph 包含处理日期时间坐标轴的功能,例如,坐标轴标签可以基于编码的日期时间值,显示从秒到年的任何内容。日期时间坐标轴的核心是 XDate struct,它捕获时间值,并处理与各种日期时间格式的相互转换。您可能想知道为什么我在这里重新发明了轮子,而不是直接使用内置的 DateTime 类(或等效类)。我创建自己的类的主要原因是 XDate 将其日期时间信息存储为 System.Double 值。因此,XDate 值可以像传递给 ZedGraph 的任何其他数据数组一样,存储在普通的 double 数组中。XDate struct 和格式在Wiki 中有详细描述。如果您使用 DateTime 结构来存储数据,您可以通过使用 DateTime.ToOADate() 方法直接转换为与 ZedGraph 兼容的 double

要使用 ZedGraph 中的日期时间坐标轴,您需要进行如下更改为 AxisType.Date

myPane.XAxis.Type = AxisType.Date;

ZedGraph 将假定 X 数组 double 值实际上是 XDate 值。XAxis.Scale.MinXAxis.Scale.Max 也将是 XDate 值,但 XAxis.Scale.MajorStepXAxis.Scale.MinorStep 的单位将取决于 XAxis.Scale.MajorUnitXAxis.Scale.MinorUnit 的设置。XAxis.Scale.MajorUnitXAxis.Scale.MinorUnit 可以设置为以下值之一:DateUnit.YearDateUnit.MonthDateUnit.DayDateUnit.HourDateUnit.MinuteDateUnit.SecondDateUnit.Millisecond。请再次注意,XDate 值实际上是以天为单位(实际上,是以自参考日期以来的天数)。但是,如果 XAxis.Scale.MajorUnit = DateUnit.Year,那么 XAxis.Scale.MajorStep 的值 0.25 表示步长是年的一四分之一。还有一个额外的属性用于日期时间坐标轴:Scale.Format。这是一个字符串,用于定义坐标轴主要刻度标签的格式。例如,XAxis.Scale.Format = "dd-MMM-yy" 将产生类似“12-Dec-95”的标签。有广泛的格式化选项——有关详细信息,请参阅Wiki。请注意,格式化基于 DateTimeFormatInfo,这与 DateTime struct 相同。

假设 XAxis.Scale.MinAutoXAxis.Scale.MajorStepAutoXAxis.Scale.MaxAuto 都设置为 true,ZedGraph 将尝试选择一个适合日期范围的坐标轴范围。这意味着自动缩放功能将自动设置以下属性:Axis.Scale.MinAxis.Scale.MaxAxis.Scale.MajorStepAxis.Scale.MinorStepAxis.Scale.MajorUnitAxis.Scale.MinorUnitAxis.Scale.Format

如果您想尝试一下,以下是一个示例图表的代码,该图表包含从 1995 年 1 月 1 日开始的 30 个月的每个月初的一个数据点

// Build the Chart
private void CreateGraph( ZedGraphControl zg1 )
{
   // Get a reference to the GraphPane
   GraphPane myPane = zg1.GraphPane;
   
   // Set the titles
   myPane.Title.Text = "My Test Date Graph";
   myPane.XAxis.Title.Text = "Date";
   myPane.XAxis.Title.Text = "My Y Axis";

   // Make up some random data points
   double x, y;
   PointPairList list = new PointPairList();
   for ( int i=0; i<36; i++ )
   {
      x = (double) new XDate( 1995, 5, i+11 );
      y = Math.Sin( (double) i * Math.PI / 15.0 );
      list.Add( x, y );
   }

   // Generate a red curve with diamond
   // symbols, and "My Curve" in the legend
   CurveItem myCurve = myPane.AddCurve( "My Curve",
         list, Color.Red, SymbolType.Diamond );

   // Set the XAxis to date type
   myPane.XAxis.Type = AxisType.Date;

   // Tell ZedGraph to refigure the axes since the data 
   // have changed
   zg1.AxisChange();
}

上面的代码生成了以下图表

在这种情况下,ZedGraph 选择了一个六天的主要步长和一个一天的次要步长。您可以根据需要轻松调整所选格式。例如,如果您想使用一个月作为次要步长,只需在 AxisChange() 调用之前设置 myPane.XAxis.Scale.MinorStep = 1.0myPane.XAxis.Scale.MinorUnit = DateUnit.Month。如果您更改 XAxis.Scale.MajorStep 值,您还必须手动设置 XAxis.Scale.MajorUnitXAxis.Scale.MinorUnitXAxis.Scale.MinorStepXAxis.Scale.Format。这是因为 XAxis.Scale.MajorStepAuto == false,因为您已选择手动设置步长。

文本坐标轴

ZedGraph 还支持文本坐标轴。这是一种坐标轴,其刻度标签是用户提供的任意文本字符串,而不是值标签。内部,文本坐标轴像普通坐标轴一样使用序数进行处理。在这种情况下,第一个主要标签的值为 1.0,第二个主要标签的值为 2.0,依此类推。如果您想在标签之间放置点,可以使用小数。

要创建文本坐标轴,请将 Axis.Type = AxisType.Text。这会告知 ZedGraph 使用用户在 Axis.Scale.TextLabels 中提供的标签。标签的数量将决定坐标轴范围。也就是说,10 个标签意味着坐标轴将从 1.0 范围到 10.0。可选地,当您将曲线添加到 ZedGraph 时,可以跳过与文本坐标轴关联的任何值数组。将生成一个默认的序数值数组。例如,如果 XAxis 的类型为 Text 且有 10 个标签,您可以添加一条曲线,将 X 数组留空 null,并且将内部生成一个从 1.0 到 10.0 的 X 数组。下面的条形图演示了 AxisType.Text 的用法。

条形图

ZedGraph 支持垂直和水平条形图、堆积条形图、百分比堆积条形图、叠加条形图、误差条形图、高低条形图、开盘-高-低-收盘条形图和日式蜡烛条形图。创建条形图与创建折线图类似,但您使用 GraphPane.AddBar()GraphPane.AddErrorBar()GraphPane.AddHiLowBar() 来创建条形实例。可以通过简单地添加不同类型的条形图来混合条形图、折线图和符号。

条形图可以水平或垂直放置,通过将“基准”坐标轴设置为 X 或 Y 坐标轴来实现。根据 ZedGraph 的术语,“基准”坐标轴决定条形图的位置,而“值”坐标轴决定条形图的高度。

通常,条形图会使用 XAxis.Type = AxisType.TextXAxis.Type = AxisType.Ordinal(这两种类型都使用序数),以便条形图在“基准”坐标轴上以整数值绘制,从 1 开始(例如,第一个条形簇位于 1.0,第二个位于 2.0,依此类推)。但是,序数坐标轴类型不是条形图的必需项。通过提供 X 值并使用 AxisType.Linear,可以创建不均匀分布的条形图(在这种情况下,您可能需要使用 GraphPane.ClusterScaleWidth 属性来告诉 ZedGraph 条形的宽度应该是多少。有关详细信息,请参阅此 Wiki 页面)。对于条形图,刻度线通常位于条形簇之间,这可以通过 Axis.MajorTic.IsBetweenLabels 属性来实现。但是,此属性仅适用于 AxisType.Text 坐标轴。

ZedGraph 实际上有六种不同的条形图类型,任何一种都可以是水平或垂直的

  • BarItem——用于常规条形图、堆积条形图、百分比堆积条形图、叠加条形图和排序叠加条形图。
  • ErrorBarItem——用于误差条形图,这是带有用户定义的上限和下限值的“I 型”条形图。
  • HiLowBarItem——用于具有用户定义的上限和下限值的矩形条形图。

其他“类似条形图”的类型也可用

  • OHLCBarItem——用于开盘-高-低-收盘股票图表,类似于误差条形图,并显示每日的开盘、收盘、最高和最低价格。
  • JapaneseCandleStickItem——另一种股票图表类型,显示一条狭窄的垂直线,表示当日的价格高低范围,以及一个彩色条形,显示开盘价和收盘价,上涨和下跌的日子颜色不同。
  • StickItem——在每个值处有一条狭窄的线,一直延伸到 X 轴。

以下是 JapaneseCandleStickItem 图表的示例,用于显示股市的高-低-开-收数据

方向(水平或垂直)和 BarItem 条形的尺寸由 GraphPane.BarSettings.Base 和其他 GraphPane.BarSettings 属性全局确定。因此,所有 BarItem 条形图都将具有相似的属性,并且条形尺寸会自动缩放以填充可用空间。相比之下,ErrorBarItem 条形图和 HiLowBarItem 条形图的尺寸由每个条形项目的单独属性控制,例如 ErrorBarItem.Bar.Size。这些条形图类型实际上类似于符号,因为条形宽度以点(1/72 英寸)为单位指定。单个绘图可以有各种不同的 ErrorBarItemHiLowBarItem,具有不同的尺寸。

GraphPane 类中包含两个属性来控制 BarItem 条形图之间的间隙;GraphPane.BarSettings.MinBarGap(默认值 = 0.2)是每个条形图在一个条形簇(共享相同 X 值的多个条形图)内的最小间隙大小,而 GraphPane.BarSettings.MinClusterGap(默认值 = 1.0)是条形簇之间的最小间隙大小。这两个参数都表示为单个条形大小的分数,即值为 1.0 将使间隙与条形大小相同。请注意,这些属性仅适用于 BarItem 条形图(不适用于 ErrorBarItemHiLowBarItem 条形图)。BarItem 类中添加了一个新的 Bar 类来控制条形的属性。这个 Bar 类具有 FillFrameColorFrameWidthIsFramed 属性。以下示例生成了一个简单的条形图

// Build the Chart
private void CreateGraph( ZedGraphControl zg1 )
{
   // get a reference to the GraphPane
   GraphPane myPane = zg1.GraphPane;
   
   // Set the Titles
   myPane.Title.Text = "My Test Bar Graph";
   myPane.XAxis.Title.Text = "Label";
   myPane.YAxis.Title.Text = "My Y Axis";
   
   // Make up some random data points
   string[] labels = { "Panther", "Lion", "Cheetah", 
                      "Cougar", "Tiger", "Leopard" };
   double[] y = { 100, 115, 75, 22, 98, 40 };
   double[] y2 = { 90, 100, 95, 35, 80, 35 };
   double[] y3 = { 80, 110, 65, 15, 54, 67 };
   double[] y4 = { 120, 125, 100, 40, 105, 75 };
   
   // Generate a red bar with "Curve 1" in the legend
   BarItem myBar = myPane.AddBar( "Curve 1", null, y, 
                                               Color.Red );
   myBar.Bar.Fill = new Fill( Color.Red, Color.White, 
                                               Color.Red );
   
   // Generate a blue bar with "Curve 2" in the legend
   myBar = myPane.AddBar( "Curve 2", null, y2, Color.Blue );
   myBar.Bar.Fill = new Fill( Color.Blue, Color.White, 
                                               Color.Blue );
   
   // Generate a green bar with "Curve 3" in the legend
   myBar = myPane.AddBar( "Curve 3", null, y3, Color.Green );
   myBar.Bar.Fill = new Fill( Color.Green, Color.White, 
                                               Color.Green );
   
   // Generate a black line with "Curve 4" in the legend
   LineItem myCurve = myPane.AddCurve( "Curve 4",
         null, y4, Color.Black, SymbolType.Circle );
   myCurve.Line.Fill = new Fill( Color.White, 
                         Color.LightSkyBlue, -45F );
   
   // Fix up the curve attributes a little
   myCurve.Symbol.Size = 8.0F;
   myCurve.Symbol.Fill = new Fill( Color.White );
   myCurve.Line.Width = 2.0F;
   
   // Draw the X tics between the labels instead of 
   // at the labels
   myPane.XAxis.MajorTic.IsBetweenLabels = true;
   
   // Set the XAxis labels
   myPane.XAxis.Scale.TextLabels = labels;
   // Set the XAxis to Text type
   myPane.XAxis.Type = AxisType.Text;
   
   // Fill the Axis and Pane backgrounds
   myPane.Chart.Fill = new Fill( Color.White,
         Color.FromArgb( 255, 255, 166), 90F );
   myPane.Fill = new Fill( Color.FromArgb( 250, 250, 255) );
   
   // Tell ZedGraph to refigure the
   // axes since the data have changed
   zg1.AxisChange();
}

上面的代码生成了以下图表

条形图类型

ZedGraph 可以根据 GraphPane.BarSettings.Type 属性绘制各种类型的 BarItem 条形图。它可以是以下值之一

BarType 描述
BarType.Cluster 这是正常格式,其中各种条形系列在每个基准值处分组为簇(如上面的第一个示例图)。
BarType.ClusterHiLow 此格式在簇格式中绘制高低条形图(条形图具有用户定义的顶部和底部),因此多个高低条形图可以在每个基准值处分组。
BarType.Overlay 在此格式中,条形图绘制在彼此之上,第一个 BarItem 绘制在后面,最后一个 BarItem 绘制在前面。
BarType.SortedOverlay 这类似于 Overlay,但条形图按值排序,最高值绘制在后面,最低值绘制在前面。
BarType.Stack 条形图堆叠在一起,值累积。
BarType.PercentStack 条形图堆叠在一起,并按百分比绘制,总高度始终为 100%。

以下示例显示了水平和堆积条形图类型

饼图

饼图的创建方式与常规方式相同,使用 GraphPane.AddPieSlice(),它返回一个 PieItem。为饼图的每个扇区添加一个 PieItem。请注意,与其他派生自 CurveItem 的类不同,PieItem 不使用 PointPairList 来存储数据值。由于饼图只有一个数据值,因此它存储在 PieItem.PieValue 中。饼图以典型的 ZedGraph 方式支持文本标签、图例、颜色填充等。

虽然在技术上可以将饼图与折线图组合在同一个 GraphPane 上,但并不推荐这样做。如果某个 GraphPane 只包含 PieItem 对象,那么 AxisChange() 方法将通过将 Axis.IsVisible 属性设置为 false 来自动使坐标轴不可见。

以下是 ZedGraph 饼图的示例

MasterPane

MasterPane 用于方便地处理页面上的多个 GraphPane 对象。它通过维护一个 GraphPane 对象列表,并提供布局、渲染、鼠标点定位等实用函数。有关 MasterPane 类的详细信息,请参阅ZedGraph Wiki。以下是基于 MasterPane 类的图表示例

平移/缩放功能

ZedGraphControl 类现在提供了一些用于缩放和平移的交互式功能。要缩放,请在 AxisRect 区域内单击鼠标左键,然后拖出一个新矩形以指示要缩放的比例区域。要平移,请单击鼠标中键或按住 Shift 键,然后单击鼠标左键拖动图表(缩放/平移组合键也可用户修改)。您还可以为控件添加滚动条,通过 IsShowHScrollBarIsShowVScrollBar 实现。鼠标滚轮也可用于缩放。还有一个上下文菜单,允许您取消缩放和取消平移到之前的状态,将比例恢复为完全自动模式,显示点值工具提示,并将图表(位图格式)复制到剪贴板。提供了事件选项以允许自定义平移、缩放、点值等。

Fill 类

Fill 类是 ZedGraph 的一个重要补充,因此值得特别提及。该类处理窗格背景、坐标轴背景、图例背景、所有文本背景、符号填充以及填充线条下区域的实色、线性渐变和纹理填充。每个此类都将包含一个或多个 Fill 类来控制填充属性(例如,CurveItem.Line.Fill 控制曲线下区域的填充)。Fill 类有许多构造函数,使其灵活且易于使用

Fill( Color ) 使用指定颜色创建实色填充。
Fill( Color, Color ) 创建从 color1color2 的线性渐变填充,渐变角度为 0 度。
Fill( Color, Color, float angle ) 创建从 color1color2 的线性渐变填充,渐变角度由指定。
Fill( Image, WrapMode ) 指定用于填充的图像。
Fill( Brush ) 直接使用指定的画笔,缩放画笔以填充目标对象边界框。
Fill( Brush, bool isScaled ) 使用指定的画笔,允许您禁用缩放。
Fill( Brush, AlignH, AlignV ) 使用指定的画笔,不进行缩放,但指定源画笔的对齐方式。

内部,Fill 类维护一个 FillType 枚举(Type 属性)来确定使用的填充类型,如下所示

  • FillType.None:不进行填充——背景将是透明的。
  • FillType.Solid:使用 SolidBrush 类和 Fill.Color 值进行实色填充。
  • FillType.Brush:将使用用户提供的画笔进行精美填充,该画笔在 Fill.Brush 中指定。如果 Fill.Brushnull,则会自动创建一个 LinearGradientBrush,使用 Fill.ColorColor.White 作为渐变色,渐变角度为零度。
  • FillType.GradientByX:此模式 intended to be used with Symbol.FillFill 类中维护一个线性渐变,每个符号点都用一个实色填充,该实色根据该点的 X 数据值从内部渐变中获取。本质上,您会得到一个散点图,其中每个数据点根据其 X 值着色。颜色分配的数据范围由用户使用 Fill.RangeMinFill.RangeMax 属性分配。
  • FillType.GradientByY:与 GradientByX 相同,只是使用 Y 数据值而不是 X 数据值。
  • FillType.GradientByZ:与 GradientByX 相同,只是使用 Z 数据值而不是 X 数据值。
  • FillType.GradientByColorValue:与 GradientByX 相同,只是使用 'ColorValue' 属性值而不是 X 数据值。

下面的图表是 FillType.GradientByZ 用法的示例,其中看似分散的数据使用颜色属性获得了视觉连贯性

下图显示了使用图像填充条形图的条形的示例

Z-Order

显示中的图形项包含在两个列表中;GraphPane.CurveListGraphPane.GraphObjListCurveList 包含所有曲线,包括条形图、折线图等。GraphObjList 包含文本项、图像、形状、箭头等。在这两个列表中,Z 顺序由列表中对象的顺序控制;列表中的第一个对象出现在后面的对象前面。您可以使用集合类中的 Move() 方法修改任何对象相对于同一集合中其他对象的顺序。此外,GraphObj 类有一个 ZOrder 属性,它控制每个 GraphObj 相对于其他非 GraphObj 对象的深度。ZOrder 是一个枚举类型,具有以下值

ZOrder 描述
ZOrder.A_InFront 最顶层的深度,位于所有其他对象的前面。
ZOrder.B_BehindLegend 绘制在 Legend 对象后面。
ZOrder.C_BehindChartBorder 绘制在 Chart 边框后面。
ZOrder.D_BehindAxis 绘制在 Axis 对象后面(在刻度标签等后面)。
ZOrder.E_BehindCurves 绘制在所有 CurveItem 对象后面。
ZOrder.F_BehindTitle 绘制在 GraphPane 标题后面。
ZOrder.G_BehindAll 绘制在 Chart 矩形填充后面(但仍在窗格矩形填充前面)。

实用方法

ZedGraph 被用作交互式父应用程序中的类库,这些应用程序需要有关图表的信息。以下是一些值得提及的实用方法(这些方法在 ZedGraph.chm 文档文件中进行了记录)

  • FindPane()MasterPane 类的一个方法,它根据鼠标点位置返回鼠标下方的 GraphPane 对象。
  • FindAxisRect()MasterPane 类的一个方法,它根据鼠标点位置返回鼠标点所在的 AxisRectGraphPane 对象。
  • FindNearestObject()GraphPane 类的一个方法,它根据鼠标点位置返回最近的对象和对象的相应索引号。此方法可能返回 Curve 点、GraphPaneGraphItem、坐标轴、图例等,具体取决于单击了什么。
  • FindNearestPoint()GraphPane 类的一个方法,它根据鼠标点位置返回最近的 CurveItem 以及该 CurveItem 中最近点的索引号。此例程仅考虑距离指定鼠标点位置 Def.Pane.NearestTol 像素内的点。默认容差为 7 像素。
  • ReverseTransform()GraphPane 类的一个方法,它根据鼠标点位置返回与该位置对应的 X、Y 和 Y2 坐标轴值。
  • GeneralTransform()GraphPane 类的一个方法,它根据 (X,Y) 值对返回相应的 (X,Y) 屏幕像素坐标。 (X,Y) 值对可以表示为各种坐标系统,如下所示
    • CoordType.AxisXYScale:坐标值来自 X 和左 Y 坐标轴。
    • CoordType.AxisXY2Scale:坐标值来自 X 和右 Y (Y2) 坐标轴。
    • CoordType.ChartFraction:坐标值表示为 Chart.Rect 宽度和高度的比例(0.5 是 Chart.Rect 的中心,1.1 只是在 AxisRect 的右侧或上方)。
    • CoordType.PaneFraction:坐标值表示为 PaneRect 宽度和高度的比例。值应在 0.0 到 1.0 之间,因为超出此范围的值将被裁剪。
    • XChartFractionYPaneFraction:X 坐标是 Chart.Rect 的比例,Y 坐标是 Pane.Rect 的比例。
    • XPaneFractionYChartFraction:X 坐标是 Pane.Rect 的比例,Y 坐标是 Chart.Rect 的比例。
    • XScaleYChartFraction:X 坐标是 X 刻度值,Y 坐标是 Chart.Rect 的比例。
    • XChartFractionYScale:X 坐标是 Chart.Rect 的比例,Y 坐标是 Y 刻度值。
    • XChartFractionY2Scale:X 坐标是 Chart.Rect 的比例,Y 坐标是 Y2 刻度值。
  • CalcChartRect()GraphPane 类的一个方法,它根据当前配置计算图表矩形(屏幕坐标)。如果出于某种原因,您需要手动设置图表矩形(使其与其他项目完美对齐等),您可以使用此方法获取默认矩形,按需修改它,然后设置 GraphPane.Chart.Rect = yourRect。更改 Chart.Rect 将自动将 GraphPane.Chart.IsRectAuto 设置为 false,以便 Chart.Rect 保持您设置的状态。

致谢

我想感谢所有测试代码、报告问题和为 ZedGraph 项目贡献代码的人。我尤其要感谢使 ZedGraph 成为可能的志愿者开发人员的贡献:Jerry R. Vos、Bob Kaye、Darren Martz 和 Benjamin Mayrargue。

最新更新

© . All rights reserved.