
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:
- 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.
- Add a
using ZedGraph;
entry to your main form code. - Add a
GraphPane
field to your Form
class with the following statement anywhere inside the class declaration: GraphPane myPane;
- Add the following code into your
Form1_Load()
method:
myPane = new GraphPane( new Rectangle( 40, 40, 600, 400 ),
"My Test Graph/n(For CodeProject Sample)",
"My X Axis",
"My Y Axis" );
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 );
}
LineItem myCurve = myPane.AddCurve( "Porsche",
list1, Color.Red, SymbolType.Diamond );
LineItem myCurve2 = myPane.AddCurve( "Piper",
list2, Color.Blue, SymbolType.Circle );
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.
- 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.):
Class | Description |
---|
MasterPane | A 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. |
GraphPane | The 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. |
Axis | The 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 , Y2Axis | The 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. |
Legend | The class that describes the location, font, colors, etc. used to draw the legend. |
CurveItem | An 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 CurveItem s. |
LineItem | A class, derived from CurveItem , that is designed for handling a curve consisting of line segments and/or symbols. |
BarItem | A class, derived from CurveItem , that is designed for handling a horizontal or vertical bar chart. |
HiLowBarItem | A 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. |
ErrorBarItem | A 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. |
PieItem | A class, derived from CurveItem , that is designed for drawing pie charts. Each PieItem object is a single slice of the pie. |
StickItem | A 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). |
CurveList | A 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. |
Line | A 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. |
Symbol | A 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. |
Bar | A 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. |
ErrorBar | A 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. |
HiLowBar | A 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. |
GraphItem | An 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 . |
TextItem | A 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. |
ArrowItem | A 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. |
ImageItem | A class that includes the location, size, and pixel data of a bitmap on the plot. |
BoxItem | A class that includes the location, size, color and fill properties of a rectangle on the plot. |
EllipseItem | A class that includes the location, size, color and fill properties of an ellipse on the plot. |
PolyItem | A class that includes the location, size, color and fill properties of a polygon on the plot. |
GraphItemList | A 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. |
FontSpec | A 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. |
Fill | A 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. |
Border | A 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. |
Location | A general class for handling the location of graphic objects on the plot. |
PointPair | A 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 . |
PointPairList | A collection class to maintain a list of PointPair objects. |
XDate | This 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:
myPane.FontSpec.FontColor = Color.Green;
myPane.XAxis.IsShowGrid = true;
myPane.YAxis.IsShowGrid = true;
myPane.XAxis.GridColor = Color.LightGray;
myPane.YAxis.GridColor = Color.LightGray;
myPane.Legend.Position = ZedGraph.LegendPos.Bottom;
myCurve.Line.Width = 2.0F;
myCurve2.Line.Width = 2.0F;
myCurve.Line.Fill = new Fill( Color.White, Color.Red, 45F );
myCurve2.Line.Fill = new Fill( Color.White, Color.Blue, 45F );
myCurve.Symbol.Size = 8.0F;
myCurve2.Symbol.Size = 8.0F;
myCurve.Symbol.Fill = new Fill( Color.White );
myCurve2.Symbol.Fill = new Fill( Color.White );
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:
g.TranslateTransform( x, y );
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;
if ( angle != 0.0F )
g.RotateTransform( -angle );
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 )
{
double tempStep = range / targetSteps;
double mag = Math.Floor( Math.Log10( tempStep ) );
double magPow = Math.Pow( (double) 10.0, mag );
double magMsd = ( (int) (tempStep / magPow + .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 double
s, 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
:
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 Range | Major Unit | Minor Unit | Scale Format | Example Format |
---|
> 5 years | Year | Year | yyyy | 1995 |
> 1 year | Year | Month | MMM-yy | Mar-95 |
> 90 days | Month | Month | MMM-yy | Mar-95 |
> 10 days | Day | Day | dd-MMM | 17-Mar |
> 3 days | Day | Hour | dd-MMM hh:mm | 17-Mar 15:34 |
> 10 hours | Hour | Hour | hh:mm | 15:34 |
> 3 hours | Hour | Minute | hh:mm | 15:34 |
> 10 minutes | Minute | Minute | hh:mm | 15:34 |
> 3 minutes | Minute | Second | mm:ss | 34:17 |
< 3 minutes | Second | Second | mm:ss | 34: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:
myPane = new GraphPane( new Rectangle( 40, 40, 600, 400 ),
"My Test Date Graph", "Date", "My Y Axis" );
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 );
}
CurveItem myCurve = myPane.AddCurve( "My Curve",
list, Color.Red, SymbolType.Diamond );
myPane.XAxis.Type = AxisType.Date;
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 ErrorBarItem
s and HiLowBarItem
s 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:
myPane = new GraphPane( new Rectangle( 40, 40, 600, 400 ),
"My Test Bar Graph", "Label", "My Y Axis" );
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 };
BarItem myBar = myPane.AddBar( "Curve 1", null, y, Color.Red );
myBar.Bar.Fill = new Fill( Color.Red, Color.White, Color.Red );
myBar = myPane.AddBar( "Curve 2", null, y2, Color.Blue );
myBar.Bar.Fill = new Fill( Color.Blue, Color.White, Color.Blue );
myBar = myPane.AddBar( "Curve 3", null, y3, Color.Green );
myBar.Bar.Fill = new Fill( Color.Green, Color.White, Color.Green );
LineItem myCurve = myPane.AddCurve( "Curve 4",
null, y4, Color.Black, SymbolType.Circle );
myCurve.Line.Fill = new Fill( Color.White, Color.LightSkyBlue, -45F );
myCurve.Symbol.Size = 8.0F;
myCurve.Symbol.Fill = new Fill( Color.White );
myCurve.Line.Width = 2.0F;
myPane.XAxis.IsTicsBetweenLabels = true;
myPane.XAxis.TextLabels = labels;
myPane.XAxis.Type = AxisType.Text;
myPane.AxisFill = new Fill( Color.White,
Color.FromArgb( 255, 255, 166), 90F );
myPane.PaneFill = new Fill( Color.FromArgb( 250, 250, 255) );
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:
BarType | Description |
---|
BarType.Cluster | This 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.ClusterHiLow | This 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.Overlay | In 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.SortedOverlay | This 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.Stack | The bars are stacked on top of each other, accumulating in value. |
BarType.PercentStack | The 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++ )
{
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;
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:
ZOrder | Description |
---|
ZOrder.A_InFront | The topmost depth, in front of all other objects. |
ZOrder.B_BehindLegend | Drawn behind the Legend object. |
ZOrder.C_BehindAxisBorder | Drawn behind the Axis frame border. |
ZOrder.D_BehindCurves | Drawn behind all the CurveItem objects. |
ZOrder.E_BehindAxis | Drawn behind the Axis objects (behind the scale labels, etc.). |
ZOrder.F_BehindTitle | Drawn behind the GraphPane title. |
ZOrder.G_BehindAll | Drawn 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
, GraphItem
s, 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.