Click here to Skip to main content
15,885,546 members
Articles / Programming Languages / C#

High Speed, Feature Rich, and Easy-To-Use Graphs and Charts

Rate me:
Please Sign up or sign in to vote.
4.92/5 (75 votes)
29 Nov 20066 min read 223.6K   17K   263  
High speed graphs and charts that are also very easy to configure and use. As easy as inserting a simple chart in MS Excel!
#region copyright info
//
// Written by Anup. V (anupshubha@yahoo.com)
// Copyright (c) 2006.
//
// This code may be used in compiled form in any way you desire. This
// file may be redistributed by any means PROVIDING it is not sold for
// for profit without the authors written consent, and providing that 
// this notice and the authors name is included. If the source code in
// this file is used in any commercial application then acknowledgement 
// must be made to the author of this file (in whatever form you wish).
//
// This file is provided "as is" with no expressed or implied warranty.
//
// Please use and enjoy. Please let me know of any bugs/mods/improvements 
// that you have found/implemented and I will fix/incorporate them into 
// this file. 
#endregion copyright info

using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Data;
using System.Windows.Forms;
using System.Diagnostics;
using System.Globalization;

namespace GraphComponents
{
	/// <summary>
	/// Summary description for StackedBarGraph.
	/// </summary>
	public class StackedBarGraph : Graph
	{
		#region variables
		/// <summary>
		/// The left side margin width of the graph
		/// </summary>
		private int graphMarginLeft   = 50;
		/// <summary>
		/// The upper margin width of the graph
		/// </summary>
		private int graphMarginTop    = 20;
		/// <summary>
		/// The right side margin width of the graph
		/// </summary>
		private int graphMarginRight  = 20;
		/// <summary>
		/// The lower margin width of the graph
		/// </summary>
		private int graphMarginBottom = 20;

		/// <summary>
		/// Maximum value of the bar
		/// </summary>
		private float maximumValue = 100;

		/// <summary>
		/// Minimum value of the bar
		/// </summary>
		private float minimumValue;

		/// <summary>
		/// Color of the bar
		/// </summary>
		private Color barColor = Color.Lime;
		/// <summary>
		/// The color to display if the value is above normal
		/// </summary>
		private Color aboveRangeColor = Color.Salmon;
		/// <summary>
		/// The color to display if the value is below normal
		/// </summary>
		private Color belowRangeColor = Color.Thistle;

		/// <summary>
		/// Bar orientation, whether horizontal or vertical
		/// </summary>
		private Orientation barOrientation = Orientation.Vertical;

		/// <summary>
		/// Color of the graph's border that includes only the area
		/// that is filled up and not the entire rectangle
		/// </summary>
		private Color graphBorderColor = Color.Black;

		/// <summary>
		/// The alignment of the value text
		/// </summary>
		private TextAlignment valueAlignment = TextAlignment.Smart;

		/// <summary>
		/// The collection that contains all the individual bars
		/// </summary>
		private BarCollection barCollection;

		/// <summary>
		/// Used to draw the gridline
		/// </summary>
		private Gridline gridline;
		/// <summary>
		/// Used to draw the x and y axis line
		/// </summary>
		private AxisLine axisLineXandY;

		private RectangleF aboveRangeRect;
		private RectangleF belowRangeRect;

		/// <summary>
		/// Number of bars to display in the stacked bar graph
		/// </summary>
		private int barCount = 4;

		/// <summary>
		/// A ratio that defines the ratio between the width of each bar
		/// to the spacing from its adjacent bar
		/// </summary>
		private float barWidthToSpacingRatio = 1;

		/// <summary>
		/// The basic bar object that does the actual rendering.
		/// </summary>
		private BasicBar basicBar;

		/// <summary>
		/// A value beyond which the readings are above normal.
		/// The graph can be displayed in a different color if it
		/// goes above this range
		/// Eg. body temp. above 40 degree C
		/// </summary>
		private float aboveRangeValue = 70;
		/// <summary>
		/// A value beyond which the readings are below normal.
		/// The graph can be displayed in a different color if it
		/// goes below this range
		/// Eg. body temp. below 35 degree C
		private float belowRangeValue = 30;

		/// <summary>
		/// Flag indicating whether lines for above and below normal limits are to be displayed
		/// </summary>
		private bool showRangeLines = true;
		/// <summary>
		/// Flag indicating whether the values of above and below ranges are to be displayed
		/// </summary>
		private bool showRangeValues;

		#endregion variables

		#region properties

		#region ShowRangeLines
		/// <summary>
		/// Flag indicating whether lines for above and below normal limits are to be displayed
		/// </summary>
		[
		Browsable(true),
		DesignerSerializationVisibility(DesignerSerializationVisibility.Visible),
		Category("Appearance"),
		Description("Flag indicating whether lines for above and below normal limits are to be displayed")
		]
		public bool ShowRangeLines
		{
			get { return showRangeLines; }
			set 
			{ 
				showRangeLines = value; 
				RefreshDisplay ();
			}
		}
		#endregion ShowRangeLines

		#region ShowRangeValues
		/// <summary>
		/// Flag indicating whether the values of above and below ranges are to be displayed
		/// </summary>
		[
		Browsable(true),
		DesignerSerializationVisibility(DesignerSerializationVisibility.Visible),
		Category("Appearance"),
		Description("Flag indicating whether the values of above and below ranges are to be displayed")
		]
		public bool ShowRangeValues
		{
			get { return showRangeValues; }
			set 
			{ 
				showRangeValues = value; 
				RefreshDisplay ();
			}
		}
		#endregion ShowRangeValues

		#region AboveRangeValue
		/// <summary>
		/// A value beyond which the readings are above normal.
		/// The bar can be displayed in a different color if it
		/// goes above this range
		/// Eg. body temp. above 40 degree C
		/// </summary>
		[
		Browsable(true),
		DesignerSerializationVisibility(DesignerSerializationVisibility.Visible),
		Category("Appearance"),
		Description("A value beyond which the readings are above normal. The bar can be displayed in a different color if it goes above this range.")
		]
		public float AboveRangeValue
		{
			get { return aboveRangeValue; }
			set 
			{ 
				aboveRangeValue = value; 
				RefreshDisplay ();
			}
		}

		#endregion AboveRangeValue

		#region BelowRangeValue
		/// <summary>
		/// A value beyond which the readings are below normal.
		/// The bar can be displayed in a different color if it
		/// goes below this range
		/// Eg. body temp. below 35 degree C
		[
		Browsable(true),
		DesignerSerializationVisibility(DesignerSerializationVisibility.Visible),
		Category("Appearance"),
		Description("A value beyond which the readings are below normal. The bar can be displayed in a different color if it goes below this range")
		]
		public float BelowRangeValue
		{
			get { return belowRangeValue; }
			set 
			{ 
				belowRangeValue = value; 
				RefreshDisplay ();
			}
		}

		#endregion BelowRangeValue

		#region Bars
		[
		Browsable(false)
		]
		public BarCollection Bars
		{
			get { return barCollection; }
		}
		#endregion Bars

		#region GraphMargin
		/// <summary>
		/// The left side margin width of the graph
		/// </summary>
		[
		Browsable(true),
		DesignerSerializationVisibility(DesignerSerializationVisibility.Visible),
		Category("Appearance"),
		Description("The left side margin width of the graph")
		]
		public int GraphMarginLeft
		{
			get { return graphMarginLeft; }
			set 
			{ 
				if (value < 0)
					throw new ArgumentException ("Invalid property value. Margin cannot be negative.");

				graphMarginLeft = value;
				RefreshDisplay ();
			}
		}

		/// <summary>
		/// The upper margin width of the graph
		/// </summary>
		[
		Browsable(true),
		DesignerSerializationVisibility(DesignerSerializationVisibility.Visible),
		Category("Appearance"),
		Description("The upper margin width of the graph")
		]
		public int GraphMarginTop
		{
			get { return graphMarginTop; }
			set 
			{ 
				if (value < 0)
					throw new ArgumentException ("Invalid property value. Margin cannot be negative.");

				graphMarginTop = value;
				RefreshDisplay ();
			}
		}

		/// <summary>
		/// The right side margin width of the graph
		/// </summary>
		[
		Browsable(true),
		DesignerSerializationVisibility(DesignerSerializationVisibility.Visible),
		Category("Appearance"),
		Description("The right side margin width of the graph")
		]
		public int GraphMarginRight
		{
			get { return graphMarginRight; }
			set 
			{ 
				if (value < 0)
					throw new ArgumentException ("Invalid property value. Margin cannot be negative.");

				graphMarginRight = value;
				RefreshDisplay ();
			}
		}

		/// <summary>
		/// The lower margin width of the graph
		/// </summary>
		[
		Browsable(true),
		DesignerSerializationVisibility(DesignerSerializationVisibility.Visible),
		Category("Appearance"),
		Description("The lower margin width of the graph")
		]
		public int GraphMarginBottom
		{
			get { return graphMarginBottom; }
			set 
			{ 
				if (value < 0)
					throw new ArgumentException ("Invalid property value. Margin cannot be negative.");

				graphMarginBottom = value;
				RefreshDisplay ();
			}
		}

		#endregion GraphMargin

		#region AboveRangeColor
		/// <summary>
		/// The color to display if the value is above normal
		/// </summary>
		[
		Browsable(true),
		DesignerSerializationVisibility(DesignerSerializationVisibility.Visible),
		Category("Appearance"),
		Description("The color to display if the value is above normal")
		]
		public Color AboveRangeColor
		{
			get { return aboveRangeColor; }
			set 
			{ 
				aboveRangeColor = value; 
				RefreshDisplay ();
			}
		}
		#endregion AboveRangeColor

		#region BelowRangeColor
		/// <summary>
		/// The color to display if the value is below normal
		/// </summary>		
		[
		Browsable(true),
		DesignerSerializationVisibility(DesignerSerializationVisibility.Visible),
		Category("Appearance"),
		Description("The color to display if the value is below normal")
		]
		public Color BelowRangeColor
		{
			get { return belowRangeColor; }
			set 
			{ 
				belowRangeColor = value; 
				RefreshDisplay ();
			}
		}
		#endregion BelowRangeColor

		#region MaximumValue
		/// <summary>
		/// Maximum value of the bar
		/// </summary>
		[
		Browsable(true),
		DesignerSerializationVisibility(DesignerSerializationVisibility.Visible),
		Category("Appearance"),
		Description("Maximum value of the bar")
		]
		public float MaximumValue
		{
			get { return maximumValue; }
			set 
			{ 
				maximumValue = value; 
				RefreshDisplay ();
			}
		}
		#endregion MaximumValue

		#region MinimumValue
		/// <summary>
		/// Minimum value of the bar
		/// </summary>
		[
		Browsable(true),
		DesignerSerializationVisibility(DesignerSerializationVisibility.Visible),
		Category("Appearance"),
		Description("Minimum value of the bar")
		]
		public float MinimumValue
		{
			get { return minimumValue; }
			set 
			{ 
				minimumValue = value; 
				RefreshDisplay ();
			}
		}
		#endregion MinimumValue

		#region BarColor
		/// <summary>
		/// Color of the bar
		/// </summary>
		[
		Browsable(true),
		DesignerSerializationVisibility(DesignerSerializationVisibility.Visible),
		Category("Appearance"),
		Description("Color of the bar")
		]
		public Color BarColor
		{
			get { return barColor; }
			set 
			{ 
				barColor = value; 
				RefreshDisplay ();
			}
		}
		#endregion BarColor

		#region BarWidthToSpacingRatio
		/// <summary>
		/// A ratio that defines the ratio between the width 
		/// of each bar to the spacing from its adjacent bar
		/// </summary>
		[
		Browsable(true),
		DesignerSerializationVisibility(DesignerSerializationVisibility.Visible),
		NotifyParentProperty(true), 
		Category("Appearance"),
		Description("A ratio that defines the ratio between the width of each bar to the spacing from its adjacent bar")
		]
		public float BarWidthToSpacingRatio
		{
			get { return barWidthToSpacingRatio;  }
			set 
			{ 
				if (value <= 0)
					throw new ArgumentException ("Invalid property value. Ratio has to be greater than 0.");
				
				barWidthToSpacingRatio = value;
				RefreshDisplay ();
			}
		}
		#endregion BarWidthToSpacingRatio

		#region BarCount
		/// <summary>
		/// Number of bars to display on the stacked bar graph
		/// </summary>
		[
		Browsable(true),
		DesignerSerializationVisibility(DesignerSerializationVisibility.Visible),
		Category("Appearance"),
		Description("Number of bars to display on the stacked bar graph")
		]
		public int BarCount
		{
			get { return barCount; }
			set 
			{ 
				if (value < 0)
					throw new ArgumentException ("Invalid property value. Bar count cannot be negative.");
				
				if (barCount < value)
				{
					// we need to add a few bars here...
					int numOfNewBars = value - barCount;
					for (int i = 0; i < numOfNewBars; i ++)
						Bars.Add (new Bar (i.ToString (CultureInfo.CurrentUICulture), 50));
				}
				else if (barCount > value)
				{
					// we need to remove a few bars from the back side..
					int numOfBarsToRemove = value - barCount;

					for (int i = 0; i < numOfBarsToRemove; i ++)
						Bars.RemoveAt (barCount - 1 - i);
				}

				barCount = value;
//				Bars.Clear ();
//				for (int i = 0; i < barCount; i ++)
//					Bars.Add (new Bar (i.ToString (CultureInfo.CurrentUICulture), 50));

				RefreshDisplay ();
			}
		}
		#endregion BarCount

		#region BarOrientation
		[
		Browsable(true),
		DesignerSerializationVisibility(DesignerSerializationVisibility.Visible),
		Category("Appearance"),
		Description("Bar orientation, whether horizontal or vertical")
		]
		public Orientation BarOrientation
		{
			get { return barOrientation; }
			set 
			{ 
				barOrientation = value; 
				RefreshDisplay ();
			}
		}

		#endregion BarOrientation

		#region TextAlignment
		/// <summary>
		/// The alignment of the value's text
		/// </summary>
		[
		Browsable(true),
		DesignerSerializationVisibility(DesignerSerializationVisibility.Visible),
		Category("Appearance"),
		Description("The alignment of the value's text")
		]
		public TextAlignment ValueAlignment
		{
			get { return valueAlignment; }
			set 
			{ 
				valueAlignment = value; 
				RefreshDisplay ();
			}
		}
		#endregion TextAlignment
		#endregion properties

		#region methods
		/// <summary>
		/// Creates a stacked bar graph user control with default properties
		/// </summary>
		public StackedBarGraph()
		{
			// This call is required by the Windows.Forms Form Designer.
			InitializeComponent();

			// TODO: Add any initialization after the InitComponent call
			basicBar = new BasicBar ();
			basicBar.ShowRangeLines  = false;
			basicBar.ShowRangeValues = false;
			basicBar.BarGraduation   = Graduation.None;

			gridline      = new Gridline (this);
			axisLineXandY = new AxisLine (this);

			barCollection = new BarCollection ();
			
			for (int i = 0; i < barCount; i ++)
			{
				Bars.Add (new Bar (i.ToString (CultureInfo.CurrentUICulture), 50));
			}

			this.SetStyle (ControlStyles.DoubleBuffer | 
				           ControlStyles.UserPaint    | 
				           ControlStyles.AllPaintingInWmPaint,
				           true);
			this.UpdateStyles ();

			GraphArea = new Rectangle (ClientRectangle.Left   + graphMarginLeft,
				                       ClientRectangle.Top    + graphMarginTop,
				                       ClientRectangle.Width  - graphMarginRight  - graphMarginLeft,
				                       ClientRectangle.Height - graphMarginBottom - graphMarginTop);			

			Debug.Assert (GraphArea.Height == (GraphArea.Bottom - GraphArea.Top), "Problem Ctor");

		}

		/// <summary> 
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
			}
			base.Dispose( disposing );
		}

		#region Component Designer generated code
		/// <summary> 
		/// Required method for Designer support - do not modify 
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			// 
			// StackedBarGraph
			// 
			this.Name = "StackedBarGraph";
			this.Size = new System.Drawing.Size(344, 256);

		}
		#endregion

		protected override void OnPaint(PaintEventArgs e)
		{		
			if (! Visible)
				return;

			Graphics graphics = e.Graphics;

			Draw (graphics);

			base.OnPaint (e);
				
		}

		#region IGraphElement Members

		public override void Draw(Graphics graphics)
		{
			// TODO:  Add StackedBarGraph.Draw implementation
			graphics.SmoothingMode = SmoothingMode.AntiAlias;

			CalculateGraphArea ();

			if (GraphArea.Width == 0 || GraphArea.Height == 0)
				return;

			if (MinimumValue >= MaximumValue)
				return;

			graphics.SetClip (GraphArea);

			gridline.Draw (graphics);

			DrawRangeLinesAndValues (graphics);

			#region comments describing bar spacing
			// For a vertical bar orientation...
			// We need to leave some gap at the left edge of the Y axis, otherwise,
			// the bar graph will look ugly
			// suppose we have a barWidthToSpacingRatio of 1.5 and barCount is 5,
			// then if the graphArea.Width is 100, we need the bars to be as follows
			// space Bar1 space1 Bar2 space2 Bar3 space3 Bar4 space4 Bar5 space5
			// So, we have 5 bars and 6 space regions
			// and barWidth = 1.5 times barSpacing
			// so, barSpacing is calculated as follows
			// 6*barSpacing + 1.5*5*barSpacing = 100
			// 13.5*barSpacing = 100
			#endregion comments describing bar spacing
			float x = ( (float) BarCount * barWidthToSpacingRatio ) + (BarCount + 1);

			float barSpacing = 0F;
			if (barOrientation == Orientation.Vertical)
			{
				barSpacing = (float) GraphArea.Width / x;
			}
			else
			{
				barSpacing = (float) GraphArea.Height / x;
			}

			float barWidth = barSpacing * barWidthToSpacingRatio;
			float barArea  = barWidth + barSpacing;

			float currentBarOffset = 0;
			
			if (barOrientation == Orientation.Vertical)
			{
				currentBarOffset = GraphArea.Left + barSpacing;
			}
			else
			{
				currentBarOffset = GraphArea.Bottom - barSpacing - barWidth;
			}

			basicBar.MaximumValue = minimumValue;
			basicBar.MaximumValue = maximumValue;

			for (int i = 0; i < barCount; i ++)
			{
				float barValue = barCollection[i].BarValue;

				#region bar color
				Color graphColor = barColor;

				if (barValue > aboveRangeValue)
					graphColor = aboveRangeColor;
				else if (barValue < belowRangeValue)
					graphColor = belowRangeColor;
				
				graphColor = Color.FromArgb (Transparency, graphColor);
				#endregion bar color

				#region draw bar
	
				if (barOrientation == Orientation.Vertical)
				{
					basicBar.ClientRectangle = new Rectangle ((int) currentBarOffset, GraphArea.Top, (int) barWidth, GraphArea.Height);
				}
				else
				{
					basicBar.ClientRectangle = new Rectangle (GraphArea.Left, (int) currentBarOffset, GraphArea.Width, (int) barWidth);
				}

				basicBar.BarColor			  = graphColor;
				basicBar.BarValue			  = barValue;
				basicBar.BarOrientation		  = barOrientation;
				basicBar.ForeColor			  = this.ForeColor;
				basicBar.ValueFormat		  = ValueFormat;
				basicBar.ValueAlignment		  = valueAlignment;
				basicBar.TextFont			  = this.Font;			
				basicBar.BorderColor          = graphBorderColor;
				basicBar.OutOfRangeArrowColor = BarColor;

				basicBar.Draw (graphics);

				if (barOrientation == Orientation.Vertical)
				{
					currentBarOffset += barArea;
				}
				else
				{
					currentBarOffset -= barArea;
				}

				#endregion draw bar
			}
			
			axisLineXandY.Draw (graphics);
			DrawBarNames (graphics, barArea, barSpacing);
			DrawBarAxisValues (graphics);
		}
		
		private void DrawBarAxisValues (Graphics graphics)
		{
			graphics.SetClip (ClientRectangle);

			if (barOrientation == Orientation.Vertical)
			{
				StringFormat sf  = new StringFormat ();
				sf.Trimming      = StringTrimming.Character;
				sf.FormatFlags   = StringFormatFlags.NoWrap;
				sf.Alignment     = StringAlignment.Far;
				sf.LineAlignment = StringAlignment.Center;

				Brush textBrush = new SolidBrush (YAxisColor);
			
				float offset = GraphArea.Top - Font.Height / 2;
				float graduationPixelDiff = GraphArea.Height / GraduationsY;
				float valueOffset = 0;

				float graduationDiff = (maximumValue - minimumValue) / GraduationsY;

				for (int i = GraduationsY; i >= 0; i --)
				{
					RectangleF axisValuesRect = new RectangleF (ClientRectangle.Left, offset, GraphMarginLeft - Font.Height / 2, Font.Height);
					float graduationValue = maximumValue - graduationDiff * valueOffset;
					valueOffset ++;

					if (axisValuesRect.IntersectsWith (aboveRangeRect) ||
						axisValuesRect.IntersectsWith (belowRangeRect) )
					{
						offset += graduationPixelDiff;
						continue;
					}

					string val = "";
					if (ValueFormat.Length != 0)
						val = string.Format (CultureInfo.CurrentUICulture, ValueFormat, graduationValue);
					else
						val = graduationValue.ToString (CultureInfo.CurrentUICulture);

					graphics.DrawString (val, Font, textBrush, axisValuesRect, sf);
					offset += graduationPixelDiff;
				}
			}
			else
			{
				StringFormat sf  = new StringFormat ();
				sf.Trimming      = StringTrimming.Character;
				sf.FormatFlags   = StringFormatFlags.NoWrap;
				sf.Alignment     = StringAlignment.Center;
				sf.LineAlignment = StringAlignment.Center;

				Brush textBrush = new SolidBrush (XAxisColor);
			
				float offset = GraphArea.Left;
				float graduationPixelDiff = GraphArea.Width / GraduationsX;

				float graduationDiff = (maximumValue - minimumValue) / GraduationsX;

				for (int i = 0; i <= GraduationsX; i ++)
				{
					float graduationValue = minimumValue + graduationDiff * i;

					string val = "";
					if (ValueFormat.Length != 0)
						val = string.Format (CultureInfo.CurrentUICulture, ValueFormat, graduationValue);
					else
						val = graduationValue.ToString (CultureInfo.CurrentUICulture);

					SizeF numberSize = graphics.MeasureString (val, Font, (int) graduationPixelDiff);
					
					RectangleF axisValuesRect = new RectangleF (offset - numberSize.Width / 2, GraphArea.Bottom + 2, numberSize.Width, Font.Height);

					if (axisValuesRect.IntersectsWith (aboveRangeRect) ||
						axisValuesRect.IntersectsWith (belowRangeRect) )
					{
						offset += graduationPixelDiff;
						continue;
					}

					graphics.DrawString (val, Font, textBrush, axisValuesRect, sf);

					offset += graduationPixelDiff;
				}

			}
		}

		private void DrawBarNames (Graphics graphics, float barArea, float barSpacing)
		{
			graphics.SetClip (ClientRectangle);
			
			StringFormat sf  = new StringFormat ();
			sf.Trimming      = StringTrimming.Character;
			sf.FormatFlags   = StringFormatFlags.NoWrap;
			sf.LineAlignment = StringAlignment.Center;
			
			float offset = 0;

			if (barOrientation == Orientation.Vertical)
			{
				Brush textBrush = new SolidBrush (XAxisColor);
				sf.Alignment     = StringAlignment.Center;
				offset = GraphArea.Left + barSpacing / 2;

				for (int i = 0; i < barCount; i ++)
				{
					string name = barCollection[i].Name;
					RectangleF nameRect = new RectangleF (offset, GraphArea.Bottom, barArea, Font.Height * 2F);					
					graphics.DrawString (name, Font, textBrush, nameRect, sf);
					offset += barArea;
				}
				textBrush.Dispose ();
			}
			else
			{
				Brush textBrush = new SolidBrush (YAxisColor);
				sf.Alignment    = StringAlignment.Far;
				float barWidth  = barArea - barSpacing;
				offset = GraphArea.Bottom - barSpacing - (barWidth / 2) - (FontHeight / 2);

				for (int i = 0; i < barCount; i ++)
				{
					string name = barCollection[i].Name;
					RectangleF nameRect = new RectangleF (ClientRectangle.Left, offset, GraphMarginLeft - FontHeight / 2, Font.Height);
					graphics.DrawString (name, Font, textBrush, nameRect, sf);
					offset -= barArea;
				}
				textBrush.Dispose ();
			}


		}

		private void CalculateGraphArea ()
		{
			GraphArea = new Rectangle (
				ClientRectangle.Left   + graphMarginLeft,
				ClientRectangle.Top    + graphMarginTop,
				ClientRectangle.Width  - graphMarginRight - graphMarginLeft,
				ClientRectangle.Height - graphMarginBottom - graphMarginTop);
		}

		private void DrawRangeLinesAndValues (Graphics graphics)
		{
			graphics.SetClip (ClientRectangle);

			#region draw range lines
			if (ShowRangeLines)
			{
				if (barOrientation == Orientation.Vertical)
				{
					float aboveRangeHeight = (float) GraphArea.Height * ( (AboveRangeValue - minimumValue) / (maximumValue - minimumValue) );
					float belowRangeHeight = (float) GraphArea.Height * ( (BelowRangeValue - minimumValue) / (maximumValue - minimumValue) );

					Pen pen = new Pen (Color.Black);
					pen.DashStyle = DashStyle.Dash;
					graphics.DrawLine (pen, GraphArea.Left, GraphArea.Bottom - aboveRangeHeight, GraphArea.Right, GraphArea.Bottom - aboveRangeHeight);
					graphics.DrawLine (pen, GraphArea.Left, GraphArea.Bottom - belowRangeHeight, GraphArea.Right, GraphArea.Bottom - belowRangeHeight);
				}
				else
				{
					float aboveRangeWidth = (float) GraphArea.Width * ( (AboveRangeValue - minimumValue) / (maximumValue - minimumValue) );
					float belowRangeWidth = (float) GraphArea.Width * ( (BelowRangeValue - minimumValue) / (maximumValue - minimumValue) );

					Pen pen = new Pen (Color.Black);
					pen.DashStyle = DashStyle.Dash;
					graphics.DrawLine (pen, GraphArea.Left + aboveRangeWidth, GraphArea.Top, GraphArea.Left + aboveRangeWidth, GraphArea.Bottom);
					graphics.DrawLine (pen, GraphArea.Left + belowRangeWidth, GraphArea.Top, GraphArea.Left + belowRangeWidth, GraphArea.Bottom);
				}
			}
			#endregion draw range lines

			#region draw range values
			if (ShowRangeValues)
			{
				StringFormat sf  = new StringFormat ();
				Brush textBrush = new SolidBrush (this.ForeColor);

				if (barOrientation == Orientation.Vertical)
				{
					float aboveRangeHeight = (float) GraphArea.Height * ( (AboveRangeValue - minimumValue) / (maximumValue - minimumValue));
					float belowRangeHeight = (float) GraphArea.Height * ( (BelowRangeValue - minimumValue) / (maximumValue - minimumValue));

					aboveRangeRect = new RectangleF (ClientRectangle.Left, GraphArea.Bottom - aboveRangeHeight - Font.Height / 2, GraphMarginLeft - Font.Height / 2, Font.Height);
					belowRangeRect = new RectangleF (ClientRectangle.Left, GraphArea.Bottom - belowRangeHeight - Font.Height / 2, GraphMarginLeft - Font.Height / 2, Font.Height);
					
					sf.Trimming      = StringTrimming.Character;
					sf.FormatFlags   = StringFormatFlags.NoWrap;
					sf.Alignment     = StringAlignment.Far;
					sf.LineAlignment = StringAlignment.Center;
					
					string above = "";
					string below = "";
					if (ValueFormat.Length != 0)
					{
						above = string.Format (CultureInfo.CurrentUICulture, ValueFormat, aboveRangeValue);
						below = string.Format (CultureInfo.CurrentUICulture, ValueFormat, belowRangeValue);
					}
					else
					{
						above = aboveRangeValue.ToString (CultureInfo.CurrentUICulture);
						below = belowRangeValue.ToString (CultureInfo.CurrentUICulture);
					}
					graphics.DrawString (above, Font, textBrush, aboveRangeRect, sf);
					graphics.DrawString (below, Font, textBrush, belowRangeRect, sf);

				}
				else
				{
					float aboveRangeWidth = (float) GraphArea.Width * ( (AboveRangeValue - minimumValue) / (maximumValue - minimumValue));
					float belowRangeWidth = (float) GraphArea.Width * ( (BelowRangeValue - minimumValue) / (maximumValue - minimumValue));

					string above = "";
					string below = "";
					if (ValueFormat.Length != 0)
					{
						above = string.Format (CultureInfo.CurrentUICulture, ValueFormat, aboveRangeValue);
						below = string.Format (CultureInfo.CurrentUICulture, ValueFormat, belowRangeValue);
					}
					else
					{
						above = aboveRangeValue.ToString (CultureInfo.CurrentUICulture);
						below = belowRangeValue.ToString (CultureInfo.CurrentUICulture);
					}

					SizeF aboveSize = graphics.MeasureString (above, Font);
					SizeF belowSize = graphics.MeasureString (below, Font);

					aboveRangeRect = new RectangleF (GraphArea.Left + aboveRangeWidth - aboveSize.Width / 2, GraphArea.Bottom + 2, aboveSize.Width, Font.Height);
					belowRangeRect = new RectangleF (GraphArea.Left + belowRangeWidth - belowSize.Width / 2, GraphArea.Bottom + 2, belowSize.Width, Font.Height);

					sf.Alignment = StringAlignment.Far;
					sf.LineAlignment = StringAlignment.Center;
					
					graphics.DrawString (above, Font, textBrush, aboveRangeRect, sf);
					graphics.DrawString (below, Font, textBrush, belowRangeRect, sf);

				}

			}
			#endregion draw range values

		}

		/// <summary>
		/// Updates the display of the graph. Call this method once you
		/// set all the values so that the changes are reflected on the 
		/// graph
		/// </summary>
		public void UpdateDisplay ()
		{
			RefreshDisplay ();
		}

		#endregion
		#endregion methods
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Software Developer (Senior)
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions