A flexible charting library for .NET

ZedGraph是一款灵活且强大的2D绘图库,支持多种图表类型如折线图、柱状图及饼图等。它能够自动生成合适的轴范围,并允许用户高度定制图表样式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

Introduction

ZedGraph is a class library, Windows Forms UserControl, and ASP web-accessible control for creating 2D line, bar, and pie graphs of arbitrary datasets. The classes provide a high degree of flexibility -- almost every aspect of the graph can be user-modified. At the same time, usage of the classes is kept simple by providing default values for all of the graph attributes. The classes include code for choosing appropriate scale ranges and step sizes based on the range of data values being plotted.

ZedGraph is maintained as an open-source development project on SourceForge. On that site, you can access project information, documentation, interim (CVS) updates, and all release versions.

A set of example graphs is also available on SourceForge, complete with source code.

Background

There are many graphing libraries out there, but none seemed to fit what I needed. I found MSChart too quirky, and many of the other options did not have the flexibility I needed to achieve a polished look. Of course, most of the commercial packages would do the trick, but I needed something that was free. So, ZedGraph was born.

These classes will generate a line, bar, or chart on a form, given a location rectangle and some data points. ZedGraph handles 2D line/scatter graphs, horizontal/vertical bar graphs, stacked bar charts, stacked percent bar charts, error bar (candlestick) graphs, and pie charts -- it does not yet handle 3D surfaces or charts. A recent addition is a generalized method for including graphs in ASP.NET web pages. See the ASP document download above for details. The charts can be dressed up with axis labels and titles, a legend, text labels and arrows (as shown in the example above), images, etc. Note that the demo program includes Normski's GDIDB double-buffering class to make resizing flicker-free. Try resizing the window to see the effect.

The documentation file (ZedGraph.chm) included with the source files gives a fairly complete coverage of the class library. Refer to it for more details -- ZedGraph has a tremendous number of options that are not documented in this tutorial. The same documentation is also available online.

Using the code

In its simplest form, a chart is made in a few simple steps. You can access ZedGraph as either a class library (using the GraphPane class), a UserControl in a Windows Form, or an ASPX control on a webpage. This article deals primarily with access via the class library. However, you can access identical functionality through the GraphPane or the MasterPane properties of the ZedGraphControl instance.

Using ZedGraph as a Web Control

ZedGraph now includes a class derived from the Control class that facilitates access from ASPX web page code. The demo project download above demonstrates this functionality. To use the web control, your web page would include an image reference such as:

<img src="graph1.aspx" />

In this example, graph1.aspx is a file that essentially just "declares" the control. The declaration includes a code-behind file called graph1.aspx.cs that actually includes the particulars for drawing the graph. Also, the ZedGraph.dll file must be in a directory called "bin" in the same location as the graph1.aspx file.

Using ZedGraph as a UserControl

ZedGraph is accessible as a control from the control toolbox in Visual Studio .NET. To access ZedGraph, first launch Visual Studio .NET and start up a new Windows Application (Forms) project. Open the form design so that it appears in the current window. View the toolbox using the View/Toolbox menu command. Right-click on the "My User Controls" or "Components" subpane of the tool box, and select the "Add/Remove Items..." option. Click "Browse...", and navigate to the ZedGraph.dll file. Once this file is added, you should see a ZedGraphControl option in the toolbox. Drag this over to the form design, and drag/size it as desired. You now have a ZedGraph control in your form. All of the ZedGraph functionality is accessible through the ZedGraphControl.MasterPane property. A ZedGraphControl.GraphPane is also provided, which simply references the first GraphPane in the MasterPane list (this is explained below). The ZedGraph.dll file works for both the UserControl and as an ordinary class library. Some sample projects (Visual Basic, Visual C#, and Visual C++) have been included in this article to demonstrate the usage of the UserControl from various languages. The rest of this article is focused on the class library access.

Using ZedGraph as a Class Library

To add the class library to your project, follow these steps:

  1. In your project, under the Project menu, select the "Add Reference..." option. Use the Browse button to find ZedGraph.dll, and click OK. This will include all the functionality of ZedGraph into your project.
  2. Add a using ZedGraph; entry to your main form code.
  3. Add a GraphPane field to your Form class with the following statement anywhere inside the class declaration:
    GraphPane   myPane;
  4. Add the following code into your Form1_Load() method:
        // Create a new graph with topLeft at (40,40) and size 600x400
        myPane = new GraphPane( new Rectangle( 40, 40, 600, 400 ),
            "My Test Graph/n(For CodeProject Sample)",
            "My X Axis",
            "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
        myPane.AxisChange( this.CreateGraphics() );

    Note that the AxisChange() method call must be made any time you add or change the data. This tells ZedGraph to go ahead and recalculate all the axis ranges. (Note: This is all AxisChange() does -- you can call it anytime you like, and it will update the axis ranges based on the current set of data points.) The UserControl version of ZedGraph provides a parameter-less AxisChange() so you don't have to make the CreateGraphics() call.

  5. In order to make sure that the graph gets painted properly, add the following line to the Form1_Paint() method:
    myPane.Draw( e.Graphics );

The above code will generate the following output:

Enhancing the Graph

ZedGraph allows you to modify the graph attributes in a wide variety of ways. Each of the parts of the graph is encapsulated in a class structure, which has modifiable properties to control the output. The following are some of the classes provided in ZedGraph (Note that these classes are XML documented. See the ZedGraph documentation for details about each class.):

ClassDescription
MasterPaneA class to manage multiple GraphPane objects, derived from PaneBase. Use of the MasterPane class is optional, as the GraphPane class can be used directly for a single pane. Also provides methods for layout, arrangement, and management of the individual GraphPane objects.
GraphPaneThe primary class for the graph, derived from PaneBase. Includes all other classes as properties. Also controls the pane title, the pane frame and axis frame, backgrounds, etc.
AxisThe parent class for the XAxis, YAxis and Y2Axis classes. This class includes almost all aspects of the axis display, including minimum, maximum and step size values, colors, pens, fonts, labels, and styles.
XAxis, YAxis, Y2AxisThe classes that include specific details about the X axis, the left Y axis, and the right Y axis. These classes inherit from the Axis class.
LegendThe class that describes the location, font, colors, etc. used to draw the legend.
CurveItemAn abstract base class that contains data for a single curve. LineItem, BarItem, HiLowBarItem, ErrorBarItem, PieItem, and StickItem are all derived from this class. The CurveList class maintains a list of CurveItems.
LineItemA class, derived from CurveItem, that is designed for handling a curve consisting of line segments and/or symbols.
BarItemA class, derived from CurveItem, that is designed for handling a horizontal or vertical bar chart.
HiLowBarItemA class, derived from CurveItem, that is designed for handling a horizontal or vertical bar chart in which both the bottom and the top of each bar are defined by user data.
ErrorBarItemA class, derived from CurveItem, that is designed for drawing error bars (vertical or horizontal lines with a symbol at each end, like an "I-Beam") to display data bands, confidence intervals, stock high-low, candlesticks, etc.
PieItemA class, derived from CurveItem, that is designed for drawing pie charts. Each PieItem object is a single slice of the pie.
StickItemA class, derived from CurveItem, that is designed for drawing stick charts (a vertical "stick" or line from each data point down to the X axis).
CurveListA collection class to maintain a list of CurveItem objects. The order of the curves in the list controls the Z-Order for drawing. The last curve in the list will appear behind all other curves.
LineA class that describes the style, width and color of the line segments that make up a curve. Each LineItem object will contain a Line object to describe the associated line characteristics.
SymbolA class that describes the symbol type, color, size and fill attributes of the symbols that appear at each point along a curve. Each LineItem object will contain a Symbol object to describe the associated symbol characteristics.
BarA class that describes the color and fill attributes of the bars that appear on a bar chart. Each BarItem object will contain a Bar object to describe the associated bar characteristics.
ErrorBarA class that describes the color attributes of the "I-Beam" bars that appear on an error bar chart. Each ErrorBarItem object will contain an ErrorBar object to describe the associated bar characteristics.
HiLowBarA class that describes the color and fill attributes of the high-low bars that appear on a high-low bar chart. Each HiLowBarItem object will contain a HiLowBar object to describe the associated bar characteristics.
GraphItemAn abstract base class that includes position information for a variety of supplemental graphic objects on a plot. TextItem, ImageItem, ArrowItem, EllipseItem, and BoxItem are derived from GraphItem.
TextItemA class that includes the text, font, location, colors, etc. for a single caption item on the graph. Note that TextItem objects are just arbitrary text located anywhere on the graph. The axis labels, scale values, pane title, etc. are not TextItem objects.
ArrowItemA class that includes the location, orientation and size of an arrow on the plot. Note that arrows are used for captions and labeling, and they can leave off the arrowhead to make a simple line segment. However, the line segments that are part of the curves are not ArrowItem objects.
ImageItemA class that includes the location, size, and pixel data of a bitmap on the plot.
BoxItemA class that includes the location, size, color and fill properties of a rectangle on the plot.
EllipseItemA class that includes the location, size, color and fill properties of an ellipse on the plot.
PolyItemA class that includes the location, size, color and fill properties of a polygon on the plot.
GraphItemListA collection class to maintain a list of GraphItem objects. The order of the objects in the list controls the Z-Order for drawing. The last item in the list will appear behind all other items.
FontSpecA utility class that includes information about font family, color, angle, size, style, frame and background fill of text on the graph. Each class that includes text information will contain one or more FontSpec objects to specifically describe the associated fonts.
FillA utility class that includes characteristics of background color fills. Each object that has color fill capability will contain one or more Fill objects to specifically describe the associated color fill.
BorderA utility class that includes characteristics of object borders. Each object that has border capability will contain one or more Border objects to specifically describe the associated border color and line properties.
LocationA general class for handling the location of graphic objects on the plot.
PointPairA data struct that encapsulates a single pair of double values representing an (X,Y) data point. This is the internal data storage format for the value arrays in each CurveItem.
PointPairListA collection class to maintain a list of PointPair objects.
XDateThis class encapsulates a single date-time value (stored as a System.Double), plus a wide array of methods to convert between XL date, Astronomical Julian Day number, Gregorian Calendar date, fractional year, etc. See the discussion of Date-Time axes below, for details.

The graph is modified by accessing properties in each of the above classes. For example, if you include the following lines of code in your Form1_Load() method after the code samples shown previously, the plot will be modified accordingly:

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

    // Add gridlines to the plot, and make them gray
    myPane.XAxis.IsShowGrid = true;
    myPane.YAxis.IsShowGrid = true;
    myPane.XAxis.GridColor = Color.LightGray;
    myPane.YAxis.GridColor = 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.AxisFill = new Fill( Color.White,
        Color.FromArgb( 255, 255, 210), -45F );

The final "dressed-up" graph will look like this:

Many more graph attributes can be modified, but listing them all in this article would be tedious. Please refer to the help file for more details.

Interesting Tidbits

Drawing line graphs is no big deal, but there are some aspects of the drawing classes that proved to be interesting. The flexibility afforded by the transform matrix of the GDI+ drawing library is very cool. This allowed the same code to be employed for drawing all three of the axes. The coordinate system is transformed to accommodate the axis location and orientation. In each case, the coordinate system is translated and rotated so that the axis is oriented along the X direction and the origin is located at the left edge of the axis when facing from the label side. A few "if" exceptions were required to account for the fact that the "left" side of the X and Y2 axes is the minimum value, and the "left" side of the Y axis is the maximum value.

The FontSpec class also employs the transform matrix in order to position the text at any angle while also allowing the location or anchor point to be at a user specified alignment. That is, a caption can be located at a given point on the graph, positioned such that the left/center/right and top/middle/bottom can be the anchor point. This is accomplished with the following code:

// Move the coordinate system to local coordinates
// of this text object (that is, at the specified
// x,y location)
g.TranslateTransform( x, y );

// Since the text will be drawn by g.DrawString()
// assuming the location is the TopCenter
// (the Font is aligned using StringFormat to the
// center so multi-line text is center justified),
// shift the coordinate system so that we are
// actually aligned per the caller specified position
if ( alignH == FontAlignH.Left )
    dx = sizeF.Width / 2.0F;
else if ( alignH == FontAlignH.Right )
    dx = -sizeF.Width / 2.0F;
else
    dx = 0.0F;

if ( alignV == FontAlignV.Center )
    dy = -sizeF.Height / 2.0F;
else if ( alignV == FontAlignV.Bottom )
    dy = -sizeF.Height;
else
    dy = 0.0F;

// Rotate the coordinate system according to the
// specified angle of the FontSpec
if ( angle != 0.0F )
    g.RotateTransform( -angle );

// Shift the coordinates to accomodate the alignment
// parameters
g.TranslateTransform( dx, dy );

Essentially, the above code shifts the origin to the (x,y) location for the text. It then rotates the coordinate system to accommodate the text angle, and then again makes a translation to account for the fact that the DrawString() method expects to draw the text based on a TopCenter location, and the user may have specified something other than TopCenter alignment (e.g., BottomRight).

Choosing Scales

Range and Step Size

ZedGraph is set up to automatically select appropriate scale minimum, maximum and step size values based on the range of data values in the curves. Alternatively, you can manually set any or all of the values, and the scale picking logic will attempt to pick the appropriate values for the remaining parameters that are left in the automatic mode. The scale picking logic is based on the assumption that the most humanly palatable step sizes will be even divisors of 10. That is, step sizes should be 1, 2 or 5 times some power of 10. The heart of the scale picking logic is found in the CalcStepSize() method:

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;
}

The initial guess at the step size is calculated using a default number of steps named targetSteps (this value is defaulted to 7), which is the typical number of major steps you want on an axis. The actual number of steps will usually end up being this number or more. A magnitude of the initial step size is determined using Floor( Log10( stepSize ) ). This magnitude is reduced to a most significant digit, which is then promoted to either 1, 2 or 5 to make the scale an even divisor of 10. In general, I find that this logic is very good at picking scales based on the data range.

Zero Lever

Another aspect of the scale selection is whether or not the scale should be extended to include the zero value. For example, if the scale range is 1 to 10, you would typically want to go ahead and start it at zero. This is accomplished with the ZeroLever default parameter. The ZeroLever is the allowable fraction that the scale range can be extended to include the zero value. This applies below the scale range for positive scales, or above the scale range for negative scale values. As an example, if the ZeroLever is 0.25 and the data range is from 2.0 to 12.0, then the scale would be extended to a range of 0.0 to 12.0 since the zero value lies 20% outside the actual data range (which is within the 25% allowed).

Grace

Finally, ZedGraph includes "grace" properties that allow you to include extra space on the scale range before and after the minimum and maximum data values, respectively. The reason for this is to avoid having the minimum and/or maximum data points fall right on the axes. Note that the grace values apply only to the non-ordinal axis types (AxisType.Log, AxisType.Linear, and AxisType.Date). The grace values are controlled by Axis.MinGrace and Axis.MaxGrace. These values are expressed as a fraction of the total data range. For example, if MinGrace and MaxGrace are both set to 0.1, then 10% of the data range will be padded before and after the actual data range. For example, if the data values go from 50.0 to 150.0, then the data range is 100.0. 10% of this range is 10.0. Adding this before and after the range gives an effective data range of 40.0 to 160.0. The grace properties will not cause the range to extend across the zero point. That is, if both the min and max data values are zero or greater, then the axis will not be extended to negative values regardless of the grace setting. The default values for MinGrace and MaxGrace are handled by Axis.Default.MinGrace and Axis.Default.MaxGrace, respectively. Both values are set to 0.1 initially.

Axis Types

Each axis now has a property called Type, which can be set to one of five values:

  • AxisType.Linear: This is the default type, which is just a regular Cartesian axis.
  • AxisType.Log: This is a base 10 logarithmic scale.
  • AxisType.Date: This is a Date-Time axis, in which the corresponding values are XDate types. See Date-Time Axes below.
  • AxisType.Text: This is an ordinal type, in which the data values for this axis are actually not used. All points or bars will be evenly spaced on the graph. The first point will have a value of 1.0, the second will be 2.0, etc. The actual value labels will be determined by an array of strings in Axis.TextLabels. See Text Axes below.
  • AxisType.Ordinal: This is another ordinal type, in which the data values for this axis are not used. All points or bars will be evenly spaced on the graph. The first point will have a value of 1.0, the second will be 2.0, etc. The actual value labels are just determined by the numeric ordinal values (1.0, 2.0, etc.).

Date-Time Axes

ZedGraph includes the capability to handle date-time axes, e.g., the axis labels can be based on an encoded date-time value, displayed in anything from seconds to years. At the heart of the Date-Time axis is the XDate struct, which captures a time value and handles conversions to/from a wide range of date-time formats. You're probably wondering why I reinvented the wheel on this, rather than just using the built-in DateTime class (or equivalent). The main reason I made my own class is because an XDate stores its date-time information as a System.Double value. Therefore, XDate values can just be stored in an ordinary array of doubles, just like any other array of data that is passed to ZedGraph. The format of the XDate date value is actually the same as DateSerial values in Microsoft Excel. It is the number of days since December 31st, 1899 (e.g., XDate = 1.0 means the start of the day January 1st, 1900). You can get the value for any date by typing the date into an MS Excel spreadsheet, then reformatting the cell to General.

XDate handles the following date format conversions:

  • MS Excel day number (days since 00:00 on December 31st, 1899).
  • Astronomical Julian Day number (days since noon GMT on January 1st, -4712).
  • .NET DateTime class.
  • Calendar date (Gregorian calendar years, months, days, hours, minutes, seconds).
  • Year decimal (Gregorian year fraction, e.g., 1995.34567).
  • Day of year (241.1234 is the 241st day of the year).
  • Day of week (0=Sunday, 1=Monday, etc.).
  • Flexible text formatting (e.g., "Saturday, February 28th, 2004, 15:33:23") via ToString().

In addition, XDate handles adding of dates, days between dates, etc. You can explicitly cast an XDate struct to a double:

// Create an XDate for 29-Nov-1995 at 15:33:23
XDate myXDate = new XDate( 1995, 11, 29, 15, 33, 23 );
double x = (double) myXDate;

Alternatively, for other languages such as Visual Basic, you can access the double value via the XLDate property, such as:

x = myXDate.XLDate;

See the ZedGraph documentation for more information on XDate.

To use a Date-Time axis in ZedGraph, you need to change to AxisType.Date as follows:

myPane.XAxis.Type = AxisType.Date;

ZedGraph will then assume that the X array double values are actually XDate values. XAxis.Min and XAxis.Max will also be XDate values, but the units of XAxis.Step and XAxis.MinorStep will depend on the setting of XAxis.MajorUnit and XAxis.MinorUnit, respectively. XAxis.MajorUnit and XAxis.MinorUnit can be set to one of the following values: DateUnit.Year, DateUnit.Month, DateUnit.Day, DateUnit.Hour, DateUnit.Minute, DateUnit.Second. Note again that XDate values are actually units of days (actually, days since a reference date). However, if XAxis.MajorUnit = DateUnit.Year, then a value of 0.25 for XAxis.Step means that the step size is 1/4 of a year. One more extra property is used for Date-Time axes: the ScaleFormat. This is a string that defines the format of the major tic labels for the axis. For example, XAxis.ScaleFormat = "dd-MMM-yy" would give labels like "12-Dec-95". A wide range of formatting options are available -- see the ZedGraph documentation (ZedGraph.chm) file for details. Note that the formatting options are the same as those for the DateTime struct.

Assuming XAxis.MinAuto, XAxis.StepAuto, and XAxis.MaxAuto are all set to true, ZedGraph will attempt to select an axis range that fits the span of the dates. This means that the autoscaling feature will automatically set the following properties: Axis.Min, Axis.Max, Axis.Step, Axis.MinorStep, Axis.MajorUnit, Axis.MinorUnit, Axis.ScaleFormat.

Note that for date axes, Axis.MinorStepAuto is only active if Axis.StepAuto is also true. Also, the Axis.ScaleFormat is only automatically set if Axis.StepAuto is true. The ZedGraph scale ranges are set according to the following ranges:

Data RangeMajor UnitMinor UnitScale FormatExample Format
> 5 yearsYearYearyyyy1995
> 1 yearYearMonthMMM-yyMar-95
> 90 daysMonthMonthMMM-yyMar-95
> 10 daysDayDaydd-MMM17-Mar
> 3 daysDayHourdd-MMM hh:mm17-Mar 15:34
> 10 hoursHourHourhh:mm15:34
> 3 hoursHourMinutehh:mm15:34
> 10 minutesMinuteMinutehh:mm15:34
> 3 minutesMinuteSecondmm:ss34:17
< 3 minutesSecondSecondmm:ss34:17

If you want to try this out, here's the code for an example graph, which has a data point for the first day of each month for 30 months starting with 1-Jan-1995:

   // Create a new graph with topLeft at (40,40) and size 600x400
   myPane = new GraphPane( new Rectangle( 40, 40, 600, 400 ),
      "My Test Date Graph", "Date", "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
   myPane.AxisChange( CreateGraphics() );

The above code generates the following graph:

In this case, ZedGraph selected a major step size of one year and a minor step size of two months. You can easily adjust the selected format if desired. For example, if you want to use 1 month for the minor step size, just set myPane.XAxis.MinorStep = 1.0 just before the AxisChange() call. If you change the XAxis.Step value, you will also have to manually set XAxis.MajorUnit, XAxis.MinorUnit, XAxis.MinorStep, and XAxis.ScaleFormat. This is because XAxis.StepAuto == false since you have chosen to manually select the step size.

Text Axes

Another upgrade to ZedGraph is the Text axis. This is an axis in which the tic labels are arbitrary, user supplied text strings instead of value labels. Internally, a text axis is handled using ordinal values just like an ordinary axis. In this case, the first major label has a value of 1.0, the second major label has a value of 2.0, etc. It is permissible to use fractional values if you want to place points in-between the labels.

To make a text axis, you set Axis.Type = AxisType.Text. This informs ZedGraph to use the labels supplied by the user in Axis.TextLabels. The number of labels will determine the axis range. That is, 10 labels means the axis will be ranged from 1.0 to 10.0. Optionally, when you add a curve to ZedGraph, you can just skip any value array that is associated with a text axis. A default array of ordinal values will be generated. For example, if the XAxis is type Text with 10 labels, you can add a curve, leaving the X array null, and an X array will be generated internally with values from 1.0 to 10.0. The bar chart below shows the usage of the AxisType.Text.

Bar Charts

ZedGraph includes bar charting capability for vertical and horizontal bar charts, stacked bar charts, percent stacked bar charts, overlay bar charts, error bar charts, and high-low bar charts. A bar chart is created similar to a line graph, except that you use GraphPane.AddBar(), GraphPane.AddErrorBar(), or GraphPane.AddHiLowBar() to create the bar instance. It is possible to mix bars, lines, and symbols on the same graph by simply adding the different types.

Bars can be horizontal or vertical by setting the "base" axis to the X or Y axis. Under ZedGraph terminology, the "base" axis determines the bar position and the "value" axis determines the height of the bar.

Typically, bar charts would be created with XAxis.Type = AxisType.Text or XAxis.Type = AxisType.Ordinal (both types use ordinal values), such that the bars are drawn at integral values along the "base" axis, starting with 1 (e.g., the first bar cluster is at 1.0, the second is at 2.0, etc.). However, the ordinal axis type is not a requirement for bar charts. It is possible to create a bar chart that is not evenly spaced, by providing X values and using AxisType.Linear (in this case, you may need to use the GraphPane.ClusterScaleWidth property to tell ZedGraph how wide the bars should be). For bar charts, the tic marks are typically between the bar clusters, which can be accomplished with the Axis.IsTicsBetweenLabels property. However, this property is only applicable for AxisType.Text axes.

ZedGraph actually has three distinct bar types, any of which can be horizontal or vertical:

  • BarItem - is for regular bars, stacked bars, percent stacked bars, overlay bars, and sorted overlay bars.
  • ErrorBarItem - is for error bars, which are "I-Beam" bars with a symbol at each end based on upper and lower values that are user-defined.
  • HiLowBarItem - is for rectangular bars that have both upper and lower values that are user-defined.

The following is an example of an ErrorBar chart, used to show High-Low-Close data for the stock market:

The orientation (horizontal or vertical) and size of BarItem bars are determined globally by GraphPane.BarBase and other GraphPane properties. Therefore, all BarItem bars will have similar properties, and the size of the bars is scaled automatically to fill the available space. In contrast, the orientation and size of ErrorBarItem bars and HiLowBarItem bars are controlled by individual properties for each bar, such as ErrorBarItem.BarBase and ErrorBarItem.ErrorBar.Size. These bar types are actually similar to symbols, since the bar width is specified in points (1/72 inch). A single plot can have a variety of different ErrorBarItems and HiLowBarItems with different orientation and size.

Two properties are included in the GraphPane class to control the gaps between BarItem bars; GraphPane.MinBarGap (default = 0.2) is the minimum size of the gap between each bar within a bar cluster (multiple bars that share the same X value), and GraphPane.MinClusterGap (default = 1.0) is the minimum size of the gap between the bar clusters. Both of these parameters are expressed as a fraction of the individual bar size, i.e., a value of 1.0 would make the gap the same size as the bars. Note that these properties apply only to BarItem bars (not ErrorBarItem or HiLowBarItem bars). A new Bar class has been added to the BarItem class to control the properties of the bars. This Bar class has properties for Fill, FrameColor, FrameWidth, and IsFramed. The following example generates a simple bar chart:

    // Create a new graph with topLeft at (40,40) and size 600x400
    myPane = new GraphPane( new Rectangle( 40, 40, 600, 400 ),
            "My Test Bar Graph", "Label", "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.IsTicsBetweenLabels = true;

    // Set the XAxis labels
    myPane.XAxis.TextLabels = labels;
    // Set the XAxis to Text type
    myPane.XAxis.Type = AxisType.Text;

    // Fill the Axis and Pane backgrounds
    myPane.AxisFill = new Fill( Color.White,
            Color.FromArgb( 255, 255, 166), 90F );
    myPane.PaneFill = new Fill( Color.FromArgb( 250, 250, 255) );
    
    // Tell ZedGraph to refigure the
    // axes since the data have changed
    myPane.AxisChange( CreateGraphics() );

The above code generates the following graph:

Bar Types

ZedGraph can draw BarItem bar charts in a variety of types according to the GraphPane.BarType property. This can be one of the following values:

BarTypeDescription
BarType.ClusterThis is the normal format in which various bar series are grouped together in clusters at each base value (like the first example chart above).
BarType.ClusterHiLowThis format draws a hi-low (bars have a top and bottom that are user defined) in a cluster format, so multiple high-low bars can be grouped together at each base value.
BarType.OverlayIn this format, the bars are drawn on top of each other, with the first BarItem drawn at the back and the last BarItem drawn at the front.
BarType.SortedOverlayThis is similar to Overlay, but the bars are sorted on value, and the highest value is drawn at the back and the lowest value is drawn at the front.
BarType.StackThe bars are stacked on top of each other, accumulating in value.
BarType.PercentStackThe bars are stacked on top of each other and plotted as a percentile, with the total height always being 100%.

The following samples show horizontal and stacked bar types:

Pie Charts

As of version 4.0, pie charts are now available in ZedGraph. Pie charts are created in the normal fashion using the GraphPane.AddPieSlice(), which returns a PieItem. One PieItem is added for each slice of the pie. Note that, unlike the other CurveItem-derived classes, the PieItem does not use the PointPairList to store the data value. Since the pie has only a single data value, it is stored in PieItem.PieValue. The pie charts support text labels, a legend, color fills, etc. in the typical ZedGraph fashion.

Although it is technically possible to combine pie charts with line graphs on the same GraphPane, it is not recommended. If a particular GraphPane contains only PieItem objects, then the AxisChange() method will automatically make the axes invisible by setting the Axis.IsVisible property to false.

The following is an example of a ZedGraph pie chart:

The MasterPane

The MasterPane is a new class added in version 4.0 designed to help facilitate the handling of multiple GraphPane objects on a page. This is done by maintaining a list of GraphPane objects, and providing utility functions for layout, rendering, mouse point location, etc. The MasterPane class is itself derived from PaneBase, so it provides for a title, background color, a GraphItemList, etc. The following example illustrates the usage of the MasterPane class to display several charts together:

   master = new MasterPane( "ZedGraph MasterPane Example", 
                        new Rectangle( 10, 10, 600, 400 ) );

   master.PaneFill = new Fill( Color.White, Color.MediumSlateBlue, 45.0F );

   master.Legend.IsVisible = true;
   master.Legend.Position = LegendPos.TopCenter;

   TextItem text = new TextItem( "Priority", 0.88F, 0.12F );
   text.Location.CoordinateFrame = CoordType.PaneFraction;
   text.FontSpec.Angle = 15.0F;
   text.FontSpec.FontColor = Color.Red;
   text.FontSpec.IsBold = true;
   text.FontSpec.Size = 16;
   text.FontSpec.Border.IsVisible = false;
   text.FontSpec.Border.Color = Color.Red;
   text.FontSpec.Fill.IsVisible = false;
   text.Location.AlignH = AlignH.Left;
   text.Location.AlignV = AlignV.Bottom;
   master.GraphItemList.Add( text );

   text = new TextItem( "DRAFT", 0.5F, 0.5F );
   text.Location.CoordinateFrame = CoordType.PaneFraction;
   text.FontSpec.Angle = 30.0F;
   text.FontSpec.FontColor = Color.FromArgb( 70, 255, 100, 100 );
   text.FontSpec.IsBold = true;
   text.FontSpec.Size = 100;
   text.FontSpec.Border.IsVisible = false;
   text.FontSpec.Fill.IsVisible = false;
   text.Location.AlignH = AlignH.Center;
   text.Location.AlignV = AlignV.Center;
   text.ZOrder = ZOrder.A_InFront;
   master.GraphItemList.Add( text );

   ColorSymbolRotator rotator = new ColorSymbolRotator();

   for ( int j=0; j<5; j++ )
   {
      // Create a new graph - rect dimensions do not matter here, since it
      // will be resized by MasterPane.AutoPaneLayout()
      GraphPane myPane = new GraphPane( new Rectangle( 10, 10, 10, 10 ),
         "Case #" + (j+1).ToString(),
         "Time, Days",
         "Rate, m/s" );

      myPane.PaneFill = new Fill( Color.White, Color.LightYellow, 45.0F );
      myPane.BaseDimension = 6.0F;

      // Make up some data arrays based on the Sine function
      double x, y;
      PointPairList list = new PointPairList();
      for ( int i=0; i<36; i++ )
      {
         x = (double) i + 5;
         y = 3.0 * ( 1.5 + Math.Sin( (double) i * 0.2 + (double) j ) );
         list.Add( x, y );
      }

      LineItem myCurve = myPane.AddCurve( "Type " + j.ToString(),
           list, rotator.NextColor, rotator.NextSymbol );
      myCurve.Symbol.Fill = new Fill( Color.White );

      master.Add( myPane );
   }

   Graphics g = this.CreateGraphics();

   master.AutoPaneLayout( g, PaneLayout.ExplicitRow32 );
   master.AxisChange( g );

   g.Dispose();

The above code generates this graph:

Pan/Zoom Functions

The ZedGraphControl (UserControl) class now provides some interactive functionality for zooming and panning. To zoom, left click inside the AxisRect area and drag out a new rectangle to indicate the scale region into which to zoom. To pan, either click with the middle mouse button or hold down the shift key and left click to drag the graph around (the zoom/pan key combinations are user-modifiable as well). You can also add scrollbars to the control with IsShowHScrollBar and IsShowVScrollBar. The mouse-wheel can also be used for zooming. There is also a context menu that allows you to un-zoom and un-pan to prior states, to restore the scale to full auto mode, to show point value tool tips, and to copy the graph (bitmap form) to the clipboard.

The Fill Class

The Fill class is an important addition to ZedGraph, so it deserves a special mention. This class handles solid, linear gradient, and texture fills for the pane background, the axis background, the legend background, all text backgrounds, the symbol fill, and for filling the area under a line. Each of these classes will have one or more Fill classes to control the fill properties (e.g., CurveItem.Line.Fill controls the filling of the area under the curve). The Fill class has a number of constructors to make it flexible and easy to use:

Fill( Color )Makes a solid color fill with the specified color.
Fill( Color, Color )Makes a linear gradient fill from color1 to color2, with a gradient angle of 0 degrees.
Fill( Color, Color, float angle )Makes a linear gradient fill from color1 to color2, with a gradient angle as specified.
Fill( Image, WrapMode )Specifies an image to be used for filling.
Fill( Brush )Uses the specified brush directly, scaling the brush to fill the destination object bounding box.
Fill( Brush, bool isScaled )Uses the specified brush, allowing you to disable the scaling.
Fill( Brush, AlignH, AlignV )Uses the specified brush, with no scaling but the source brush alignment is specified.

Internally, the Fill class keeps a FillType enumeration (the Type property) to determine what type of fill is used, as follows:

  • FillType.None: No filling will be done -- the background will be transparent.
  • FillType.Solid: A solid color fill will be done using the SolidBrush class with the Fill.Color value.
  • FillType.Brush: A fancy fill will be done using the user-supplied brush specified in Fill.Brush. If Fill.Brush is null, then a LinearGradientBrush will be automatically created using Fill.Color and Color.White as the gradient colors, with a gradient angle of zero degrees.
  • FillType.GradientByX: This mode is intended to be used with Symbol.Fill. A linear gradient is maintained internally in the Fill class, and each symbol point is filled with a solid color that is taken from the internal gradient based on the X data value for that point. Essentially, you get a scatter plot in which each data point is colored according to its X value. The data ranges for color assignment are user assigned with the Fill.RangeMin and Fill.RangeMax properties.
  • FillType.GradientByY: This is the same as GradientByX except that the Y data values are used instead of X data values.
  • FillType.GradientByZ: This is the same as GradientByX except that the Z data values are used instead of X data values.

The following graph is an example of FillType.GradientByZ usage, in which seemingly scattered data gains visual coherency using the color attribute:

The following shows an example of using an image to fill in the bars of a bar chart:

Z-Order

The graphic items in the display are contained in two lists; GraphPane.CurveList and GraphPane.GraphItemList. CurveList contains all of the curves, including bars, lines, etc. GraphItemList contains text items, images, shapes, arrows, etc. In both lists, the Z-Order is controlled by the order of the objects in the list; the first objects in the list appear in front of the later objects in the list. You can modify the order of any object relative to other objects in the same collection (list) with the Move() method in the collection class. Further, the GraphItem class has an ZOrder property that controls the depth of each individual GraphItem relative to other, non-GraphItem objects. The ZOrder is an enumeration type with the following values:

ZOrderDescription
ZOrder.A_InFrontThe topmost depth, in front of all other objects.
ZOrder.B_BehindLegendDrawn behind the Legend object.
ZOrder.C_BehindAxisBorderDrawn behind the Axis frame border.
ZOrder.D_BehindCurvesDrawn behind all the CurveItem objects.
ZOrder.E_BehindAxisDrawn behind the Axis objects (behind the scale labels, etc.).
ZOrder.F_BehindTitleDrawn behind the GraphPane title.
ZOrder.G_BehindAllDrawn behind the Axis rectangle fill (but still in front of the pane rectangle fill).

Utility Methods

ZedGraph is being used as a class library in interactive parent applications that need information about the graph. The following are some utility methods that are worth mention (these are documented in the ZedGraph.chm doc file):

  • FindPane() is a method in the MasterPane class that, given a mouse point location, returns the GraphPane object under the mouse.
  • FindAxisRect() is a method in the MasterPane class that, given a mouse point location, returns the GraphPane object for the AxisRect in which the mouse point lies.
  • FindNearestObject() is a method in the GraphPane class that, given a mouse point location, returns the closest object and a corresponding index number for the object. This method may return Curve points, the GraphPane, GraphItems, Axes, the Legend, etc., depending on what was clicked.
  • FindNearestPoint() is a method in the GraphPane class that, given a mouse point location, returns the closest CurveItem and the index number of the closest point within that CurveItem. This routine will only consider points that are within Def.Pane.NearestTol pixels of the specified mouse point location. The default tolerance is 7 pixels.
  • ReverseTransform() is a method in the GraphPane class that, given a mouse point location, returns the X, Y, and Y2 axis values that correspond to that location.
  • GeneralTransform() is a method in the GraphPane class that, given an (X,Y) value pair, returns the corresponding (X,Y) screen pixel coordinates. The (X,Y) value pair can be expressed in a variety of coordinate systems as follows:
    • CoordType.AxisXYScale: the coordinate values are from the X and left Y axes.
    • CoordType.AxisXY2Scale: the coordinate values are from the X and right Y (Y2) axes.
    • CoordType.AxisFraction: the coordinate values are expressed as a fraction of the total AxisRect width and height (0.5 is the center of the AxisRect, 1.1 is just to the right or just above the AxisRect).
    • CoordType.PaneFraction: the coordinate values are expressed as a fraction of the total PaneRect width and height. The value should be from 0.0 to 1.0, since values outside this range are clipped.
  • CalcAxisRect() is a method in the GraphPane class that will calculate the axis rectangle (screen coordinates) given the current configuration. If for some reason you need to manually set the axis rectangle (to make it line up perfectly with other items, etc.), you can use this method to get the default rectangle, modify it as desired, then set GraphPane.AxisRect = yourRect. Changing the AxisRect will automatically set GraphPane.IsAxisRectAuto to false so that the AxisRect will remain as you set it.

Latest Update

  • 13-Aug-2005 (Version 4.2.1)
    • Disabled ZedGraphControl.PanModifierKeys property as a workaround for the Visual Studio 2003 problem.
  • 2-Aug-2005 (Version 4.2)
    • Added scrollbar options to ZedGraphControl.
    • Uses Axis.ScaleFormat for numeric labels and dates (removed Axis.NumDec and Axis.NumDecAuto).
    • Added subscription event for pan/zoom.
    • Added subscription event for custom formatting of point value tooltips.
    • Added CurveItem.IsOverrideOrdinal to allow user-specified ordinal positioning.
    • Added GraphItem.IsClippedToAxisRect.
    • Revamped Axis.Cross logic, and added Axis.IsScaleLabelsInside, Axis.IsSkipFirstLabel, and Axis.IsSkipLastLabel.
    • Added StickItem curve type.
    • Added PolyItem polygon GraphItem type.
    • Added CurveItem.FontSpec for custom font colors/types in legend.
    • Large variety of bug fixes and minor additions.

A more complete revision history is available here.

Acknowledgements

I would like to thank all those who have tested the code, reported issues, and contributed code to the ZedGraph project. I would especially like to acknowledge the contributions of the volunteer developers who made ZedGraph possible: Jerry R. Vos, Bob Kaye, and Darren Martz.

JChampion


Click here to view JChampion's online profile

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值