Click here to Skip to main content
15,886,026 members
Articles / Multimedia / GDI+

A flexible charting library for .NET

Rate me:
Please Sign up or sign in to vote.
4.70/5 (1,112 votes)
6 Jun 200730 min read 8.9M   180.2K   2.1K  
Looking for a way to draw 2D line graphs with C#? Here's yet another charting class library with a high degree of configurability, that is also easy to use.
//============================================================================
//ZedGraph Class Library - A Flexible Line Graph/Bar Graph Library in C#
//Copyright � 2004  John Champion
//
//This library is free software; you can redistribute it and/or
//modify it under the terms of the GNU Lesser General Public
//License as published by the Free Software Foundation; either
//version 2.1 of the License, or (at your option) any later version.
//
//This library is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//Lesser General Public License for more details.
//
//You should have received a copy of the GNU Lesser General Public
//License along with this library; if not, write to the Free Software
//Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//=============================================================================

using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Drawing.Imaging;
using System.Drawing.Printing;
using System.Data;
using System.Globalization;
using System.IO;
using System.Resources;
using System.Reflection;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace ZedGraph
{
/*
	/// <summary>
	/// 
	/// </summary>
	public struct DrawingThreadData
	{
		/// <summary>
		/// 
		/// </summary>
		public Graphics _g;
		/// <summary>
		/// 
		/// </summary>
		public MasterPane _masterPane;

//		public DrawingThread( Graphics g, MasterPane masterPane )
//		{
//			_g = g;
//			_masterPane = masterPane;
//		}
	}
*/

	/// <summary>
	/// The ZedGraphControl class provides a UserControl interface to the
	/// <see cref="ZedGraph"/> class library.  This allows ZedGraph to be installed
	/// as a control in the Visual Studio toolbox.  You can use the control by simply
	/// dragging it onto a form in the Visual Studio form editor.  All graph
	/// attributes are accessible via the <see cref="ZedGraphControl.GraphPane"/>
	/// property.
	/// </summary>
	/// <author> John Champion revised by Jerry Vos </author>
	/// <version> $Revision: 3.80 $ $Date: 2007/02/18 05:51:54 $ </version>
	public class ZedGraphControl : System.Windows.Forms.UserControl
	{
		private System.ComponentModel.IContainer components;

	#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()
		{
			this.components = new System.ComponentModel.Container();
			this.hScrollBar1 = new System.Windows.Forms.HScrollBar();
			this.vScrollBar1 = new System.Windows.Forms.VScrollBar();
			this.contextMenu = new System.Windows.Forms.ContextMenu();
			this.pointToolTip = new System.Windows.Forms.ToolTip(this.components);
			this.SuspendLayout();
			// 
			// hScrollBar1
			// 
			this.hScrollBar1.Location = new System.Drawing.Point(0, 128);
			this.hScrollBar1.Name = "hScrollBar1";
			this.hScrollBar1.Size = new System.Drawing.Size(128, 17);
			this.hScrollBar1.TabIndex = 0;
			this.hScrollBar1.Scroll += new System.Windows.Forms.ScrollEventHandler(this.hScrollBar1_Scroll);
			// 
			// vScrollBar1
			// 
			this.vScrollBar1.Location = new System.Drawing.Point(128, 0);
			this.vScrollBar1.Name = "vScrollBar1";
			this.vScrollBar1.Size = new System.Drawing.Size(17, 128);
			this.vScrollBar1.TabIndex = 1;
			this.vScrollBar1.Scroll += new System.Windows.Forms.ScrollEventHandler(this.vScrollBar1_Scroll);
			// 
			// contextMenu
			// 
			this.contextMenu.Popup += new System.EventHandler(this.ContextMenu_Popup);
			// 
			// pointToolTip
			// 
			this.pointToolTip.AutoPopDelay = 5000;
			this.pointToolTip.InitialDelay = 100;
			this.pointToolTip.ReshowDelay = 0;
			// 
			// ZedGraphControl
			// 
			this.ContextMenu = this.contextMenu;
			this.Controls.Add(this.vScrollBar1);
			this.Controls.Add(this.hScrollBar1);
			this.Name = "ZedGraphControl";
			this.MouseWheel += new System.Windows.Forms.MouseEventHandler( this.ZedGraphControl_MouseWheel );
			this.Resize += new System.EventHandler(this.ZedGraphControl_ReSize);
			this.KeyUp += new System.Windows.Forms.KeyEventHandler(this.ZedGraphControl_KeyUp);
			this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.ZedGraphControl_KeyDown);
			this.ResumeLayout(false);

		}
	#endregion

	#region Private Fields

		/// <summary>
		/// This private field contains the instance for the MasterPane object of this control.
		/// You can access the MasterPane object through the public property
		/// <see cref="ZedGraphControl.MasterPane"/>. This is nulled when this Control is
		/// disposed.
		/// </summary>
		private MasterPane _masterPane;

		/// <summary>
		/// private field that determines if anti-aliased drawing will be forced on.  Use the
		/// public property <see cref="ZedGraphControl.IsAntiAlias"/> to access this value.
		/// </summary>
		private bool _isAntiAlias = false;

		/// <summary>
		/// private field that determines whether or not tooltips will be displayed
		/// when the mouse hovers over data values.  Use the public property
		/// <see cref="IsShowPointValues"/> to access this value.
		/// </summary>
		private bool _isShowPointValues = false;
		/// <summary>
		/// private field that determines whether or not tooltips will be displayed
		/// showing the scale values while the mouse is located within the ChartRect.
		/// Use the public property <see cref="IsShowCursorValues"/> to access this value.
		/// </summary>
		private bool _isShowCursorValues = false;
		/// <summary>
		/// private field that determines the format for displaying tooltip values.
		/// This format is passed to <see cref="PointPairBase.ToString(string)"/>.
		/// Use the public property <see cref="PointValueFormat"/> to access this
		/// value.
		/// </summary>
		private string _pointValueFormat = PointPair.DefaultFormat;

		/// <summary>
		/// private field that determines whether or not the context menu will be available.  Use the
		/// public property <see cref="IsShowContextMenu"/> to access this value.
		/// </summary>
		private bool _isShowContextMenu = true;

		/// <summary>
		/// private field that determines whether or not a message box will be shown in response to
		/// a context menu "Copy" command.  Use the
		/// public property <see cref="IsShowCopyMessage"/> to access this value.
		/// </summary>
		/// <remarks>
		/// Note that, if this value is set to false, the user will receive no indicative feedback
		/// in response to a Copy action.
		/// </remarks>
		private bool _isShowCopyMessage = true;

		private SaveFileDialog _saveFileDialog = new SaveFileDialog();

		/// <summary>
		/// private field that determines whether the settings of
		/// <see cref="ZedGraph.PaneBase.IsFontsScaled" /> and <see cref="PaneBase.IsPenWidthScaled" />
		/// will be overridden to true during printing operations.
		/// </summary>
		/// <remarks>
		/// Printing involves pixel maps that are typically of a dramatically different dimension
		/// than on-screen pixel maps.  Therefore, it becomes more important to scale the fonts and
		/// lines to give a printed image that looks like what is shown on-screen.  The default
		/// setting for <see cref="ZedGraph.PaneBase.IsFontsScaled" /> is true, but the default
		/// setting for <see cref="PaneBase.IsPenWidthScaled" /> is false.
		/// </remarks>
		/// <value>
		/// A value of true will cause both <see cref="ZedGraph.PaneBase.IsFontsScaled" /> and
		/// <see cref="PaneBase.IsPenWidthScaled" /> to be temporarily set to true during
		/// printing operations.
		/// </value>
		private bool _isPrintScaleAll = true;
		/// <summary>
		/// private field that determines whether or not the visible aspect ratio of the
		/// <see cref="MasterPane" /> <see cref="PaneBase.Rect" /> will be preserved
		/// when printing this <see cref="ZedGraphControl" />.
		/// </summary>
		private bool _isPrintKeepAspectRatio = true;
		/// <summary>
		/// private field that determines whether or not the <see cref="MasterPane" />
		/// <see cref="PaneBase.Rect" /> dimensions will be expanded to fill the
		/// available space when printing this <see cref="ZedGraphControl" />.
		/// </summary>
		/// <remarks>
		/// If <see cref="IsPrintKeepAspectRatio" /> is also true, then the <see cref="MasterPane" />
		/// <see cref="PaneBase.Rect" /> dimensions will be expanded to fit as large
		/// a space as possible while still honoring the visible aspect ratio.
		/// </remarks>
		private bool _isPrintFillPage = true;

		/// <summary>
		/// private field that determines the format for displaying tooltip date values.
		/// This format is passed to <see cref="XDate.ToString(string)"/>.
		/// Use the public property <see cref="PointDateFormat"/> to access this
		/// value.
		/// </summary>
		private string _pointDateFormat = XDate.DefaultFormatStr;

		/// <summary>
		/// private value that determines whether or not zooming is enabled for the control in the
		/// vertical direction.  Use the public property <see cref="IsEnableVZoom"/> to access this
		/// value.
		/// </summary>
		private bool _isEnableVZoom = true;
		/// <summary>
		/// private value that determines whether or not zooming is enabled for the control in the
		/// horizontal direction.  Use the public property <see cref="IsEnableHZoom"/> to access this
		/// value.
		/// </summary>
		private bool _isEnableHZoom = true;

		/// <summary>
		/// private value that determines whether or not point editing is enabled in the
		/// vertical direction.  Use the public property <see cref="IsEnableVEdit"/> to access this
		/// value.
		/// </summary>
		private bool _isEnableVEdit = false;
		/// <summary>
		/// private value that determines whether or not point editing is enabled in the
		/// horizontal direction.  Use the public property <see cref="IsEnableHEdit"/> to access this
		/// value.
		/// </summary>
		private bool _isEnableHEdit = false;

		/// <summary>
		/// private value that determines whether or not panning is allowed for the control in the
		/// horizontal direction.  Use the
		/// public property <see cref="IsEnableHPan"/> to access this value.
		/// </summary>
		private bool _isEnableHPan = true;
		/// <summary>
		/// private value that determines whether or not panning is allowed for the control in the
		/// vertical direction.  Use the
		/// public property <see cref="IsEnableVPan"/> to access this value.
		/// </summary>
		private bool _isEnableVPan = true;

		// Revision: JCarpenter 10/06
		/// <summary>
		/// Internal variable that indicates if the control can manage selections. 
		/// </summary>
		private bool _isEnableSelection = false;

		private double _zoomStepFraction = 0.1;

		private ScrollRange _xScrollRange;

		private ScrollRangeList _yScrollRangeList;
		private ScrollRangeList _y2ScrollRangeList;

		private bool _isShowHScrollBar = false;
		private bool _isShowVScrollBar = false;
		//private bool		isScrollY2 = false;
		private bool _isAutoScrollRange = false;

		private double _scrollGrace = 0.00; //0.05;

		private bool _isSynchronizeXAxes = false;
		private bool _isSynchronizeYAxes = false;

		//private System.Windows.Forms.HScrollBar hScrollBar1;
		//private System.Windows.Forms.VScrollBar vScrollBar1;

		// The range of values to use the scroll control bars
		private const int _ScrollControlSpan = int.MaxValue;
		// The ratio of the largeChange to the smallChange for the scroll bars
		private const int _ScrollSmallRatio = 10;

		private bool _isZoomOnMouseCenter = false;

		private ResourceManager _resourceManager;

		/// <summary>
		/// private field that stores a <see cref="PrintDocument" /> instance, which maintains
		/// a persistent selection of printer options.
		/// </summary>
		/// <remarks>
		/// This is needed so that a "Print" action utilizes the settings from a prior
		/// "Page Setup" action.</remarks>
		private PrintDocument _pdSave = null;
		//private PrinterSettings printSave = null;
		//private PageSettings pageSave = null;

		/// <summary>
		/// This private field contains a list of selected CurveItems.
		/// </summary>
		//private List<CurveItem> _selection = new List<CurveItem>();
		private Selection _selection = new Selection();

	#endregion

	#region Fields: Buttons & Keys Properties

		/// <summary>
		/// Gets or sets a value that determines which Mouse button will be used to click on
		/// linkable objects
		/// </summary>
		/// <seealso cref="LinkModifierKeys" />
		private MouseButtons _linkButtons = MouseButtons.Left;
		/// <summary>
		/// Gets or sets a value that determines which modifier keys will be used to click
		/// on linkable objects
		/// </summary>
		/// <seealso cref="LinkButtons" />
		private Keys _linkModifierKeys = Keys.Alt;

		/// <summary>
		/// Gets or sets a value that determines which Mouse button will be used to edit point
		/// data values
		/// </summary>
		/// <remarks>
		/// This setting only applies if <see cref="IsEnableHEdit" /> and/or
		/// <see cref="IsEnableVEdit" /> are true.
		/// </remarks>
		/// <seealso cref="EditModifierKeys" />
		private MouseButtons _editButtons = MouseButtons.Right;
		/// <summary>
		/// Gets or sets a value that determines which modifier keys will be used to edit point
		/// data values
		/// </summary>
		/// <remarks>
		/// This setting only applies if <see cref="IsEnableHEdit" /> and/or
		/// <see cref="IsEnableVEdit" /> are true.
		/// </remarks>
		/// <seealso cref="EditButtons" />
		private Keys _editModifierKeys = Keys.Alt;

		/// <summary>
		/// Gets or sets a value that determines which mouse button will be used to select
		/// <see cref="CurveItem" />'s.
		/// </summary>
		/// <remarks>
		/// This setting only applies if <see cref="IsEnableSelection" /> is true.
		/// </remarks>
		/// <seealso cref="SelectModifierKeys" />
		private MouseButtons _selectButtons = MouseButtons.Left;
		/// <summary>
		/// Gets or sets a value that determines which modifier keys will be used to select
		/// <see cref="CurveItem" />'s.
		/// </summary>
		/// <remarks>
		/// This setting only applies if <see cref="IsEnableSelection" /> is true.
		/// </remarks>
		/// <seealso cref="SelectButtons" />
		private Keys _selectModifierKeys = Keys.Shift;

		private Keys _selectAppendModifierKeys = Keys.Shift | Keys.Control;

		/// <summary>
		/// Gets or sets a value that determines which Mouse button will be used to perform
		/// zoom operations
		/// </summary>
		/// <remarks>
		/// This setting only applies if <see cref="IsEnableHZoom" /> and/or
		/// <see cref="IsEnableVZoom" /> are true.
		/// </remarks>
		/// <seealso cref="ZoomModifierKeys" />
		/// <seealso cref="ZoomButtons2" />
		/// <seealso cref="ZoomModifierKeys2" />
		private MouseButtons _zoomButtons = MouseButtons.Left;
		/// <summary>
		/// Gets or sets a value that determines which modifier keys will be used to perform
		/// zoom operations
		/// </summary>
		/// <remarks>
		/// This setting only applies if <see cref="IsEnableHZoom" /> and/or
		/// <see cref="IsEnableVZoom" /> are true.
		/// </remarks>
		/// <seealso cref="ZoomButtons" />
		/// <seealso cref="ZoomButtons2" />
		/// <seealso cref="ZoomModifierKeys2" />
		private Keys _zoomModifierKeys = Keys.None;

		/// <summary>
		/// Gets or sets a value that determines which Mouse button will be used as a
		/// secondary option to perform zoom operations
		/// </summary>
		/// <remarks>
		/// This setting only applies if <see cref="IsEnableHZoom" /> and/or
		/// <see cref="IsEnableVZoom" /> are true.
		/// </remarks>
		/// <seealso cref="ZoomModifierKeys2" />
		/// <seealso cref="ZoomButtons" />
		/// <seealso cref="ZoomModifierKeys" />
		private MouseButtons _zoomButtons2 = MouseButtons.None;
		/// <summary>
		/// Gets or sets a value that determines which modifier keys will be used as a
		/// secondary option to perform zoom operations
		/// </summary>
		/// <remarks>
		/// This setting only applies if <see cref="IsEnableHZoom" /> and/or
		/// <see cref="IsEnableVZoom" /> are true.
		/// </remarks>
		/// <seealso cref="ZoomButtons" />
		/// <seealso cref="ZoomButtons2" />
		/// <seealso cref="ZoomModifierKeys2" />
		private Keys _zoomModifierKeys2 = Keys.None;

		/// <summary>
		/// Gets or sets a value that determines which Mouse button will be used to perform
		/// panning operations
		/// </summary>
		/// <remarks>
		/// This setting only applies if <see cref="IsEnableHPan" /> and/or
		/// <see cref="IsEnableVPan" /> are true.  A Pan operation (dragging the graph with
		/// the mouse) should not be confused with a scroll operation (using a scroll bar to
		/// move the graph).
		/// </remarks>
		/// <seealso cref="PanModifierKeys" />
		/// <seealso cref="PanButtons2" />
		/// <seealso cref="PanModifierKeys2" />
		private MouseButtons _panButtons = MouseButtons.Left;

		// Setting this field to Keys.Shift here
		// causes an apparent bug to crop up in VS 2003, by which it will have the value:
		// "System.Windows.Forms.Keys.Shift+None", which won't compile
		/// <summary>
		/// Gets or sets a value that determines which modifier keys will be used to perform
		/// panning operations
		/// </summary>
		/// <remarks>
		/// This setting only applies if <see cref="IsEnableHPan" /> and/or
		/// <see cref="IsEnableVPan" /> are true.  A Pan operation (dragging the graph with
		/// the mouse) should not be confused with a scroll operation (using a scroll bar to
		/// move the graph).
		/// </remarks>
		/// <seealso cref="PanButtons" />
		/// <seealso cref="PanButtons2" />
		/// <seealso cref="PanModifierKeys2" />
		private Keys _panModifierKeys = Keys.Control;

		/// <summary>
		/// Gets or sets a value that determines which Mouse button will be used as a
		/// secondary option to perform panning operations
		/// </summary>
		/// <remarks>
		/// This setting only applies if <see cref="IsEnableHPan" /> and/or
		/// <see cref="IsEnableVPan" /> are true.  A Pan operation (dragging the graph with
		/// the mouse) should not be confused with a scroll operation (using a scroll bar to
		/// move the graph).
		/// </remarks>
		/// <seealso cref="PanModifierKeys2" />
		/// <seealso cref="PanButtons" />
		/// <seealso cref="PanModifierKeys" />
		private MouseButtons _panButtons2 = MouseButtons.Middle;

		// Setting this field to Keys.Shift here
		// causes an apparent bug to crop up in VS 2003, by which it will have the value:
		// "System.Windows.Forms.Keys.Shift+None", which won't compile
		/// <summary>
		/// Gets or sets a value that determines which modifier keys will be used as a
		/// secondary option to perform panning operations
		/// </summary>
		/// <remarks>
		/// This setting only applies if <see cref="IsEnableHPan" /> and/or
		/// <see cref="IsEnableVPan" /> are true.  A Pan operation (dragging the graph with
		/// the mouse) should not be confused with a scroll operation (using a scroll bar to
		/// move the graph).
		/// </remarks>
		/// <seealso cref="PanButtons2" />
		/// <seealso cref="PanButtons" />
		/// <seealso cref="PanModifierKeys" />
		private Keys _panModifierKeys2 = Keys.None;

	#endregion

	#region Button and Key Properties

		/// <summary>
		/// Gets or sets a value that determines which mouse button will be used as a primary option
		/// to trigger a zoom event.
		/// </summary>
		/// <remarks>
		/// This value is combined with <see cref="ZoomModifierKeys"/> to determine the actual zoom combination.
		/// A secondary zoom button/key combination option is available via <see cref="ZoomButtons2"/> and
		/// <see cref="ZoomModifierKeys2"/>.  To not use this button/key combination, set the value
		/// of <see cref="ZoomButtons"/> to <see cref="MouseButtons.None"/>.
		/// </remarks>
		[	Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
			DefaultValue(MouseButtons.Left),
			Description( "Determines which mouse button is used as the primary for zooming" )]
		public MouseButtons ZoomButtons
		{
			get { return _zoomButtons; }
			set { _zoomButtons = value; }
		}
		/// <summary>
		/// Gets or sets a value that determines which mouse button will be used as the secondary option
		/// to trigger a zoom event.
		/// </summary>
		/// <remarks>
		/// This value is combined with <see cref="ZoomModifierKeys2"/> to determine the actual zoom combination.
		/// The primary zoom button/key combination option is available via <see cref="ZoomButtons"/> and
		/// <see cref="ZoomModifierKeys"/>.  To not use this button/key combination, set the value
		/// of <see cref="ZoomButtons2"/> to <see cref="MouseButtons.None"/>.
		/// </remarks>
		[	Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
			DefaultValue(MouseButtons.None),
			Description( "Determines which mouse button is used as the secondary for zooming" )]
		public MouseButtons ZoomButtons2
		{
			get { return _zoomButtons2; }
			set { _zoomButtons2 = value; }
		}
		/// <summary>
		/// Gets or sets a value that determines which modifier keys will be used as a primary option
		/// to trigger a zoom event.
		/// </summary>
		/// <remarks>
		/// This value is combined with <see cref="ZoomButtons"/> to determine the actual zoom combination.
		/// A secondary zoom button/key combination option is available via <see cref="ZoomButtons2"/> and
		/// <see cref="ZoomModifierKeys2"/>.  To not use this button/key combination, set the value
		/// of <see cref="ZoomButtons"/> to <see cref="MouseButtons.None"/>.
		/// </remarks>
		[	Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
			DefaultValue( Keys.None ),
			Description( "Determines which modifier key used as the primary for zooming" )]
		public Keys ZoomModifierKeys
		{
			get { return _zoomModifierKeys; }
			set { _zoomModifierKeys = value; }
		}
		/// <summary>
		/// Gets or sets a value that determines which modifier keys will be used as a secondary option
		/// to trigger a zoom event.
		/// </summary>
		/// <remarks>
		/// This value is combined with <see cref="ZoomButtons2"/> to determine the actual zoom combination.
		/// A primary zoom button/key combination option is available via <see cref="ZoomButtons"/> and
		/// <see cref="ZoomModifierKeys"/>.  To not use this button/key combination, set the value
		/// of <see cref="ZoomButtons2"/> to <see cref="MouseButtons.None"/>.
		/// </remarks>
		[	Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
			DefaultValue( Keys.None ),
			Description( "Determines which modifier key used as the secondary for zooming" )]
		public Keys ZoomModifierKeys2
		{
			get { return _zoomModifierKeys2; }
			set { _zoomModifierKeys2 = value; }
		}

		/// <summary>
		/// Gets or sets a value that determines which mouse button will be used as a primary option
		/// to trigger a pan event.
		/// </summary>
		/// <remarks>
		/// This value is combined with <see cref="PanModifierKeys"/> to determine the actual pan combination.
		/// A secondary pan button/key combination option is available via <see cref="PanButtons2"/> and
		/// <see cref="PanModifierKeys2"/>.  To not use this button/key combination, set the value
		/// of <see cref="PanButtons"/> to <see cref="MouseButtons.None"/>.
		/// </remarks>
		[	Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
			DefaultValue( MouseButtons.Left ),
			Description( "Determines which mouse button is used as the primary for panning" )]
		public MouseButtons PanButtons
		{
			get { return _panButtons; }
			set { _panButtons = value; }
		}
		/// <summary>
		/// Gets or sets a value that determines which mouse button will be used as the secondary option
		/// to trigger a pan event.
		/// </summary>
		/// <remarks>
		/// This value is combined with <see cref="PanModifierKeys2"/> to determine the actual pan combination.
		/// The primary pan button/key combination option is available via <see cref="PanButtons"/> and
		/// <see cref="PanModifierKeys"/>.  To not use this button/key combination, set the value
		/// of <see cref="PanButtons2"/> to <see cref="MouseButtons.None"/>.
		/// </remarks>
		[	Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
			DefaultValue( MouseButtons.Middle ),
			Description( "Determines which mouse button is used as the secondary for panning" )]
		public MouseButtons PanButtons2
		{
			get { return _panButtons2; }
			set { _panButtons2 = value; }
		}

		// NOTE: The default value of PanModifierKeys is Keys.Shift. Because of an apparent bug in
		// VS 2003, the initial value set in InitializeComponent by the code wizard is "Keys.Shift+None"
		// which will not compile.  As a temporary workaround, I've hidden the value so that it won't
		// have compile errors.  This problem does not exist in VS 2005.

		/// <summary>
		/// Gets or sets a value that determines which modifier keys will be used as a primary option
		/// to trigger a pan event.
		/// </summary>
		/// <remarks>
		/// This value is combined with <see cref="PanButtons"/> to determine the actual pan combination.
		/// A secondary pan button/key combination option is available via <see cref="PanButtons2"/> and
		/// <see cref="PanModifierKeys2"/>.  To not use this button/key combination, set the value
		/// of <see cref="PanButtons"/> to <see cref="MouseButtons.None"/>.
		/// </remarks>
		[	Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
			DefaultValue( Keys.Control ),
			Description( "Determines which modifier key is used as the primary for panning" )]
		public Keys PanModifierKeys
		{
			get { return _panModifierKeys; }
			set { _panModifierKeys = value; }
		}

		/// <summary>
		/// Gets or sets a value that determines which modifier keys will be used as a secondary option
		/// to trigger a pan event.
		/// </summary>
		/// <remarks>
		/// This value is combined with <see cref="PanButtons2"/> to determine the actual pan combination.
		/// A primary pan button/key combination option is available via <see cref="PanButtons"/> and
		/// <see cref="PanModifierKeys"/>.  To not use this button/key combination, set the value
		/// of <see cref="PanButtons2"/> to <see cref="MouseButtons.None"/>.
		/// </remarks>
		[	Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
			DefaultValue( Keys.None ),
			Description( "Determines which modifier key is used as the secondary for panning" )]
		public Keys PanModifierKeys2
		{
			get { return _panModifierKeys2; }
			set { _panModifierKeys2 = value; }
		}

		/// <summary>
		/// Gets or sets a value that determines which Mouse button will be used to edit point
		/// data values
		/// </summary>
		/// <remarks>
		/// This setting only applies if <see cref="IsEnableHEdit" /> and/or
		/// <see cref="IsEnableVEdit" /> are true.
		/// </remarks>
		/// <seealso cref="EditModifierKeys" />
		[Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
		 DefaultValue( MouseButtons.Right ),
		 Description( "Specify mouse button for point editing" )]
		public MouseButtons EditButtons
		{
			get { return _editButtons; }
			set { _editButtons = value; }
		}
		/// <summary>
		/// Gets or sets a value that determines which modifier keys will be used to edit point
		/// data values
		/// </summary>
		/// <remarks>
		/// This setting only applies if <see cref="IsEnableHEdit" /> and/or
		/// <see cref="IsEnableVEdit" /> are true.
		/// </remarks>
		/// <seealso cref="EditButtons" />
		[Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
		 DefaultValue( Keys.Alt ),
		 Description( "Specify modifier key for point editing" )]
		public Keys EditModifierKeys
		{
			get { return _editModifierKeys; }
			set { _editModifierKeys = value; }
		}

		/// <summary>
		/// Gets or sets a value that determines which Mouse button will be used to 
		/// select <see cref="CurveItem" />'s.
		/// </summary>
		/// <remarks>
		/// This setting only applies if <see cref="IsEnableSelection" /> is true.
		/// </remarks>
		/// <seealso cref="SelectModifierKeys" />
		[Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
		 DefaultValue( MouseButtons.Left ),
		 Description( "Specify mouse button for curve selection" )]
		public MouseButtons SelectButtons
		{
			get { return _selectButtons; }
			set { _selectButtons = value; }
		}
		/// <summary>
		/// Gets or sets a value that determines which Modifier keys will be used to 
		/// select <see cref="CurveItem" />'s.
		/// </summary>
		/// <remarks>
		/// This setting only applies if <see cref="IsEnableSelection" /> is true.
		/// </remarks>
		/// <seealso cref="SelectButtons" />
		[Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
		 DefaultValue( Keys.Shift ),
		 Description( "Specify modifier key for curve selection" )]
		public Keys SelectModifierKeys
		{
			get { return _selectModifierKeys; }
			set { _selectModifierKeys = value; }
		}

		/// <summary>
		/// Gets or sets a value that determines which Modifier keys will be used to 
		/// append a <see cref="CurveItem" /> to the selection list.
		/// </summary>
		[Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
		 DefaultValue( Keys.Shift | Keys.Alt ),
		 Description( "Specify modifier key for append curve selection" )]
		public Keys SelectAppendModifierKeys
		{
			get { return _selectAppendModifierKeys; }
		}

		/// <summary>
		/// Gets or sets a value that determines which Mouse button will be used to click
		/// on linkable objects
		/// </summary>
		/// <seealso cref="LinkModifierKeys" />
		/// <seealso cref="LinkEvent"/>
		// /// <seealso cref="ZedGraph.Web.IsImageMap"/>
		[	Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
			DefaultValue( MouseButtons.Left ),
			Description( "Specify mouse button for clicking on linkable objects" )]
		public MouseButtons LinkButtons
		{
			get { return _linkButtons; }
			set { _linkButtons = value; }
		}
		/// <summary>
		/// Gets or sets a value that determines which modifier keys will be used to click
		/// on linkable objects
		/// </summary>
		/// <seealso cref="LinkButtons" />
		/// <seealso cref="LinkEvent"/>
		// /// <seealso cref="ZedGraph.Web.IsImageMap"/>
		[	Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
			DefaultValue( Keys.Alt ),
			Description( "Specify modifier key for clicking on linkable objects" )]
		public Keys LinkModifierKeys
		{
			get { return _linkModifierKeys; }
			set { _linkModifierKeys = value; }
		}

	#endregion

	#region Fields: Temporary state variables

		/// <summary>
		/// Internal variable that indicates the control is currently being zoomed. 
		/// </summary>
		private bool _isZooming = false;
		/// <summary>
		/// Internal variable that indicates the control is currently being panned.
		/// </summary>
		private bool _isPanning = false;
		/// <summary>
		/// Internal variable that indicates a point value is currently being edited.
		/// </summary>
		private bool _isEditing = false;

		// Revision: JCarpenter 10/06
		/// <summary>
		/// Internal variable that indicates the control is currently using selection. 
		/// </summary>
		private bool _isSelecting = false;

		/// <summary>
		/// Internal variable that stores the <see cref="GraphPane"/> reference for the Pane that is
		/// currently being zoomed or panned.
		/// </summary>
		private GraphPane _dragPane = null;
		/// <summary>
		/// Internal variable that stores a rectangle which is either the zoom rectangle, or the incremental
		/// pan amount since the last mousemove event.
		/// </summary>
		private Point _dragStartPt;
		private Point _dragEndPt;

		private int _dragIndex;
		private CurveItem _dragCurve;
		private PointPair _dragStartPair;
		/// <summary>
		/// private field that stores the state of the scale ranges prior to starting a panning action.
		/// </summary>
		private ZoomState _zoomState;
		private ZoomStateStack _zoomStateStack;
		private System.Windows.Forms.HScrollBar hScrollBar1;
		private System.Windows.Forms.VScrollBar vScrollBar1;
		private System.Windows.Forms.ToolTip pointToolTip;
		private System.Windows.Forms.ContextMenu contextMenu;

		//temporarily save the location of a context menu click so we can use it for reference
		// Note that Control.MousePosition ends up returning the position after the mouse has
		// moved to the menu item within the context menu.  Therefore, this point is saved so
		// that we have the point at which the context menu was first right-clicked
		internal Point _menuClickPt;

	#endregion

	#region Events

		/// <summary>
		/// A delegate that allows subscribing methods to append or modify the context menu.
		/// </summary>
		/// <param name="sender">The source <see cref="ZedGraphControl"/> object</param>
		/// <param name="menu">A reference to the <see cref="ContextMenu"/> object
		/// that contains the context menu.
		/// </param>
		/// <param name="mousePt">The point at which the mouse was clicked</param>
		/// <param name="objState">The current context menu state</param>
		/// <seealso cref="ContextMenuBuilder" />
		public delegate void ContextMenuBuilderEventHandler( ZedGraphControl sender,
			ContextMenu menu, Point mousePt, ContextMenuObjectState objState  );

		/// <summary>
		/// Subscribe to this event to be able to modify the ZedGraph context menu.
		/// </summary>
		/// <remarks>
		/// The context menu is built on the fly after a right mouse click.  You can add menu items
		/// to this menu by simply modifying the <see paramref="menu"/> parameter.
		/// </remarks>
		[	Bindable( true ), Category( "Events" ),
			Description( "Subscribe to this event to be able to modify the ZedGraph context menu" )]
		public event ContextMenuBuilderEventHandler ContextMenuBuilder;

		/// <summary>
		/// A delegate that allows notification of zoom and pan events.
		/// </summary>
		/// <param name="sender">The source <see cref="ZedGraphControl"/> object</param>
		/// <param name="oldState">A <see cref="ZoomState"/> object that corresponds to the state of the
		/// <see cref="GraphPane"/> before the zoom or pan event.</param>
		/// <param name="newState">A <see cref="ZoomState"/> object that corresponds to the state of the
		/// <see cref="GraphPane"/> after the zoom or pan event</param>
		/// <seealso cref="ZoomEvent" />
		public delegate void ZoomEventHandler( ZedGraphControl sender, ZoomState oldState,
			ZoomState newState );

		/// <summary>
		/// Subscribe to this event to be notified when the <see cref="GraphPane"/> is zoomed or panned by the user,
		/// either via a mouse drag operation or by the context menu commands.
		/// </summary>
		[	Bindable( true ), Category( "Events" ),
			Description( "Subscribe to this event to be notified when the graph is zoomed or panned" )]
		public event ZoomEventHandler ZoomEvent;

		/// <summary>
		/// A delegate that allows notification of scroll events.
		/// </summary>
		/// <param name="sender">The source <see cref="ZedGraphControl"/> object</param>
		/// <param name="scrollBar">The source <see cref="ScrollBar"/> object</param>
		/// <param name="oldState">A <see cref="ZoomState"/> object that corresponds to the state of the
		/// <see cref="GraphPane"/> before the scroll event.</param>
		/// <param name="newState">A <see cref="ZoomState"/> object that corresponds to the state of the
		/// <see cref="GraphPane"/> after the scroll event</param>
		/// <seealso cref="ZoomEvent" />
		public delegate void ScrollDoneHandler( ZedGraphControl sender, ScrollBar scrollBar,
			ZoomState oldState, ZoomState newState );

		// This event is not supported for .Net 1.1 since the Scroll Capture event cannot be captured.
//		/// <summary>
//		/// Subscribe to this event to be notified when the <see cref="GraphPane"/> is scrolled by the user
//		/// using the scrollbars.
//		/// </summary>
//		[	Bindable( true ), Category( "Events" ),
//			Description( "Subscribe this event to be notified when a scroll operation using the scrollbars is completed" )]
//		public event ScrollDoneHandler ScrollDoneEvent;

		/// <summary>
		/// A delegate that allows notification of scroll events.
		/// </summary>
		/// <param name="sender">The source <see cref="ZedGraphControl"/> object</param>
		/// <param name="scrollBar">The source <see cref="ScrollBar"/> object</param>
		/// <param name="oldState">A <see cref="ZoomState"/> object that corresponds to the state of the
		/// <see cref="GraphPane"/> before the scroll event.</param>
		/// <param name="newState">A <see cref="ZoomState"/> object that corresponds to the state of the
		/// <see cref="GraphPane"/> after the scroll event</param>
		/// <seealso cref="ZoomEvent" />
		public delegate void ScrollProgressHandler( ZedGraphControl sender, ScrollBar scrollBar,
			ZoomState oldState, ZoomState newState );

		/// <summary>
		/// Subscribe to this event to be notified when the <see cref="GraphPane"/> is scrolled by the user
		/// using the scrollbars.
		/// </summary>
		[	Bindable( true ), Category( "Events" ),
			Description( "Subscribe this event to be notified continuously as a scroll operation is taking place" )]
		public event ScrollProgressHandler ScrollProgressEvent;

		/// <summary>
		/// A delegate that receives notification after a point-edit operation is completed.
		/// </summary>
		/// <param name="sender">The source <see cref="ZedGraphControl"/> object</param>
		/// <param name="pane">The <see cref="GraphPane"/> object that contains the
		/// point that has been edited</param>
		/// <param name="curve">The <see cref="CurveItem"/> object that contains the point
		/// that has been edited</param>
		/// <param name="iPt">The integer index of the edited <see cref="PointPair"/> within the
		/// <see cref="IPointList"/> of the selected <see cref="CurveItem"/>
		/// </param>
		/// <seealso cref="PointValueEvent" />
		public delegate string PointEditHandler( ZedGraphControl sender, GraphPane pane,
			CurveItem curve, int iPt );

		/// <summary>
		/// Subscribe to this event to receive notifcation and/or respond after a data
		/// point has been edited via <see cref="IsEnableHEdit" /> and <see cref="IsEnableVEdit" />.
		/// </summary>
		/// <example>
		/// <para>To subscribe to this event, use the following in your Form_Load method:</para>
		/// <code>zedGraphControl1.PointEditEvent +=
		/// new ZedGraphControl.PointEditHandler( MyPointEditHandler );</code>
		/// <para>Add this method to your Form1.cs:</para>
		/// <code>
		///    private string MyPointEditHandler( object sender, GraphPane pane, CurveItem curve, int iPt )
		///    {
		///        PointPair pt = curve[iPt];
		///        return "This value is " + pt.Y.ToString("f2") + " gallons";
		///    }</code>
		/// </example>
		[	Bindable( true ), Category( "Events" ),
			Description( "Subscribe to this event to respond to data point edit actions" )]
		public event PointEditHandler PointEditEvent;

		/// <summary>
		/// A delegate that allows custom formatting of the point value tooltips
		/// </summary>
		/// <param name="sender">The source <see cref="ZedGraphControl"/> object</param>
		/// <param name="pane">The <see cref="GraphPane"/> object that contains the point value of interest</param>
		/// <param name="curve">The <see cref="CurveItem"/> object that contains the point value of interest</param>
		/// <param name="iPt">The integer index of the selected <see cref="PointPair"/> within the
		/// <see cref="IPointList"/> of the selected <see cref="CurveItem"/></param>
		/// <seealso cref="PointValueEvent" />
		public delegate string PointValueHandler( ZedGraphControl sender, GraphPane pane,
			CurveItem curve, int iPt );

		/// <summary>
		/// Subscribe to this event to provide custom formatting for the tooltips
		/// </summary>
		/// <example>
		/// <para>To subscribe to this event, use the following in your FormLoad method:</para>
		/// <code>zedGraphControl1.PointValueEvent +=
		/// new ZedGraphControl.PointValueHandler( MyPointValueHandler );</code>
		/// <para>Add this method to your Form1.cs:</para>
		/// <code>
		///    private string MyPointValueHandler( object sender, GraphPane pane, CurveItem curve, int iPt )
		///    {
		///    #region
		///        PointPair pt = curve[iPt];
		///        return "This value is " + pt.Y.ToString("f2") + " gallons";
		///    #endregion
		///    }</code>
		/// </example>
		[	Bindable( true ), Category( "Events" ),
			Description( "Subscribe to this event to provide custom-formatting for data point tooltips" )]
		public event PointValueHandler PointValueEvent;

		/// <summary>
		/// A delegate that allows notification of mouse events on Graph objects.
		/// </summary>
		/// <param name="sender">The source <see cref="ZedGraphControl"/> object</param>
		/// <param name="e">A <see cref="MouseEventArgs" /> corresponding to this event</param>
		/// <seealso cref="MouseDownEvent" />
		/// <returns>
		/// Return true if you have handled the mouse event entirely, and you do not
		/// want the <see cref="ZedGraphControl"/> to do any further action (e.g., starting
		/// a zoom operation).  Return false if ZedGraph should go ahead and process the
		/// mouse event.
		/// </returns>
		public delegate bool ZedMouseEventHandler( ZedGraphControl sender, MouseEventArgs e );

		/// <summary>
		/// Subscribe to this event to provide notification of MouseDown clicks on graph
		/// objects
		/// </summary>
		/// <remarks>
		/// This event provides for a notification when the mouse is clicked on an object
		/// within any <see cref="GraphPane"/> of the <see cref="MasterPane"/> associated
		/// with this <see cref="ZedGraphControl" />.  This event will use the
		/// <see cref="ZedGraph.MasterPane.FindNearestPaneObject"/> method to determine which object
		/// was clicked.  The boolean value that you return from this handler determines whether
		/// or not the <see cref="ZedGraphControl"/> will do any further handling of the
		/// MouseDown event (see <see cref="ZedMouseEventHandler" />).  Return true if you have
		/// handled the MouseDown event entirely, and you do not
		/// want the <see cref="ZedGraphControl"/> to do any further action (e.g., starting
		/// a zoom operation).  Return false if ZedGraph should go ahead and process the
		/// MouseDown event.
		/// </remarks>
		[	Bindable( true ), Category( "Events" ),
			Description( "Subscribe to be notified when the left mouse button is clicked down" )]
		public event ZedMouseEventHandler MouseDownEvent;

		/// <summary>
		/// Hide the standard control MouseDown event so that the ZedGraphControl.MouseDownEvent
		/// can be used.  This is so that the user must return true/false in order to indicate
		/// whether or not we should respond to the event.
		/// </summary>
		[Bindable( false ), Browsable( false )]
		public new event MouseEventHandler MouseDown;
		/// <summary>
		/// Hide the standard control MouseUp event so that the ZedGraphControl.MouseUpEvent
		/// can be used.  This is so that the user must return true/false in order to indicate
		/// whether or not we should respond to the event.
		/// </summary>
		[Bindable( false ), Browsable( false )]
		public new event MouseEventHandler MouseUp;
		/// <summary>
		/// Hide the standard control MouseMove event so that the ZedGraphControl.MouseMoveEvent
		/// can be used.  This is so that the user must return true/false in order to indicate
		/// whether or not we should respond to the event.
		/// </summary>
		[Bindable( false ), Browsable( false )]
		private new event MouseEventHandler MouseMove;
		/// <summary>
		/// Subscribe to this event to provide notification of MouseUp clicks on graph
		/// objects
		/// </summary>
		/// <remarks>
		/// This event provides for a notification when the mouse is clicked on an object
		/// within any <see cref="GraphPane"/> of the <see cref="MasterPane"/> associated
		/// with this <see cref="ZedGraphControl" />.  This event will use the
		/// <see cref="ZedGraph.MasterPane.FindNearestPaneObject"/> method to determine which object
		/// was clicked.  The boolean value that you return from this handler determines whether
		/// or not the <see cref="ZedGraphControl"/> will do any further handling of the
		/// MouseUp event (see <see cref="ZedMouseEventHandler" />).  Return true if you have
		/// handled the MouseUp event entirely, and you do not
		/// want the <see cref="ZedGraphControl"/> to do any further action (e.g., starting
		/// a zoom operation).  Return false if ZedGraph should go ahead and process the
		/// MouseUp event.
		/// </remarks>
		[	Bindable( true ), Category( "Events" ),
			Description( "Subscribe to be notified when the left mouse button is released" )]
		public event ZedMouseEventHandler MouseUpEvent;
		/// <summary>
		/// Subscribe to this event to provide notification of MouseMove events over graph
		/// objects
		/// </summary>
		/// <remarks>
		/// This event provides for a notification when the mouse is moving over on the control.
		/// The boolean value that you return from this handler determines whether
		/// or not the <see cref="ZedGraphControl"/> will do any further handling of the
		/// MouseMove event (see <see cref="ZedMouseEventHandler" />).  Return true if you
		/// have handled the MouseMove event entirely, and you do not
		/// want the <see cref="ZedGraphControl"/> to do any further action.
		/// Return false if ZedGraph should go ahead and process the MouseMove event.
		/// </remarks>
		[	Bindable( true ), Category( "Events" ),
			Description( "Subscribe to be notified when the mouse is moved inside the control" )]
		public event ZedMouseEventHandler MouseMoveEvent;

		/// <summary>
		/// Subscribe to this event to provide notification of Double Clicks on graph
		/// objects
		/// </summary>
		/// <remarks>
		/// This event provides for a notification when the mouse is double-clicked on an object
		/// within any <see cref="GraphPane"/> of the <see cref="MasterPane"/> associated
		/// with this <see cref="ZedGraphControl" />.  This event will use the
		/// <see cref="ZedGraph.MasterPane.FindNearestPaneObject"/> method to determine which object
		/// was clicked.  The boolean value that you return from this handler determines whether
		/// or not the <see cref="ZedGraphControl"/> will do any further handling of the
		/// DoubleClick event (see <see cref="ZedMouseEventHandler" />).  Return true if you have
		/// handled the DoubleClick event entirely, and you do not
		/// want the <see cref="ZedGraphControl"/> to do any further action. 
		/// Return false if ZedGraph should go ahead and process the
		/// DoubleClick event.
		/// </remarks>
		[	Bindable( true ), Category( "Events" ),
			Description( "Subscribe to be notified when the left mouse button is double-clicked" )]
		public event ZedMouseEventHandler DoubleClickEvent;

		/// <summary>
		/// A delegate that allows notification of clicks on ZedGraph objects that have
		/// active links enabled
		/// </summary>
		/// <param name="sender">The source <see cref="ZedGraphControl"/> object</param>
		/// <param name="pane">The source <see cref="GraphPane" /> in which the click
		/// occurred.
		/// </param>
		/// <param name="source">The source object which was clicked.  This is typically
		/// a type of <see cref="CurveItem" /> if a curve point was clicked, or
		/// a type of <see cref="GraphObj" /> if a graph object was clicked.
		/// </param>
		/// <param name="link">The <see cref="Link" /> object, belonging to
		/// <paramref name="source" />, that contains the link information
		/// </param>
		/// <param name="index">An index value, typically used if a <see cref="CurveItem" />
		/// was clicked, indicating the ordinal value of the actual point that was clicked.
		/// </param>
		/// <returns>
		/// Return true if you have handled the LinkEvent entirely, and you do not
		/// want the <see cref="ZedGraphControl"/> to do any further action.
		/// Return false if ZedGraph should go ahead and process the LinkEvent.
		/// </returns>
		public delegate bool LinkEventHandler( ZedGraphControl sender, GraphPane pane,
			object source, Link link, int index );

		/// <summary>
		/// Subscribe to this event to be able to respond to mouse clicks within linked
		/// objects.
		/// </summary>
		/// <remarks>
		/// Linked objects are typically either <see cref="GraphObj" /> type objects or
		/// <see cref="CurveItem" /> type objects.  These object types can include
		/// hyperlink information allowing for "drill-down" type operation.  
		/// </remarks>
		/// <seealso cref="LinkEventHandler"/>
		/// <seealso cref="Link" />
		/// <seealso cref="CurveItem.Link">CurveItem.Link</seealso>
		/// <seealso cref="GraphObj.Link">GraphObj.Link</seealso>
		// /// <seealso cref="ZedGraph.Web.IsImageMap" />
		[	Bindable( true ), Category( "Events" ),
			Description( "Subscribe to be notified when a link-enabled item is clicked" )]
		public event LinkEventHandler LinkEvent;

	#endregion

	#region Constructors

		/// <summary>
		/// Default Constructor
		/// </summary>
		public ZedGraphControl()
		{
			InitializeComponent();

			// These commands do nothing, but they get rid of the compiler warnings for
			// unused events
			bool b = MouseDown == null || MouseUp == null || MouseMove == null;

			// Link in these events from the base class, since we disable them from this class.
			base.MouseDown += new System.Windows.Forms.MouseEventHandler( this.ZedGraphControl_MouseDown );
			base.MouseUp += new System.Windows.Forms.MouseEventHandler( this.ZedGraphControl_MouseUp );
			base.MouseMove += new System.Windows.Forms.MouseEventHandler( this.ZedGraphControl_MouseMove );

			//this.MouseWheel += new System.Windows.Forms.MouseEventHandler( this.ZedGraphControl_MouseWheel );

			// Use double-buffering for flicker-free updating:
			SetStyle( ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint
				| ControlStyles.DoubleBuffer | ControlStyles.ResizeRedraw, true );
			//isTransparentBackground = false;
			//SetStyle( ControlStyles.Opaque, false );
			SetStyle( ControlStyles.SupportsTransparentBackColor, true );
			//this.BackColor = Color.Transparent;

			_resourceManager = new ResourceManager( "ZedGraph.ZedGraph_v5.ZedGraphLocale",
				Assembly.GetExecutingAssembly() );

			Rectangle rect = new Rectangle( 0, 0, this.Size.Width, this.Size.Height );
			_masterPane = new MasterPane( "", rect );
			_masterPane.Margin.All = 0;
			_masterPane.Title.IsVisible = false;

			string titleStr = _resourceManager.GetString( "title_def" );
			string xStr = _resourceManager.GetString( "x_title_def" );
			string yStr = _resourceManager.GetString( "y_title_def" );

			//GraphPane graphPane = new GraphPane( rect, "Title", "X Axis", "Y Axis" );
			GraphPane graphPane = new GraphPane( rect, titleStr, xStr, yStr );
			using ( Graphics g = this.CreateGraphics() )
			{
				graphPane.AxisChange( g );
				//g.Dispose();
			}
			_masterPane.Add( graphPane );

			this.hScrollBar1.Minimum = 0;
			this.hScrollBar1.Maximum = 100;
			this.hScrollBar1.Value = 0;

			this.vScrollBar1.Minimum = 0;
			this.vScrollBar1.Maximum = 100;
			this.vScrollBar1.Value = 0;

			_xScrollRange = new ScrollRange( true );
			_yScrollRangeList = new ScrollRangeList();
			_y2ScrollRangeList = new ScrollRangeList();

			_yScrollRangeList.Add( new ScrollRange( true ) );
			_y2ScrollRangeList.Add( new ScrollRange( false ) );

			_zoomState = null;
			_zoomStateStack = new ZoomStateStack();
		}

		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		/// <param name="disposing">true if the components should be
		/// disposed, false otherwise</param>
		protected override void Dispose( bool disposing )
		{
			lock ( this )
			{
				if ( disposing )
				{
					if ( components != null )
						components.Dispose();
				}
				base.Dispose( disposing );

				_masterPane = null;
			}
		}
	
	#endregion

	#region Properties

		/// <summary>
		/// Gets or sets the <see cref="ZedGraph.MasterPane"/> property for the control
		/// </summary>
		[	Bindable( false ), Browsable( false ),
			DesignerSerializationVisibility( DesignerSerializationVisibility.Hidden )]
		public MasterPane MasterPane
		{
			get { lock ( this ) return _masterPane; }
			set { lock ( this ) _masterPane = value; }
		}

		// Testing for Designer attribute
		/*
		Class1 _class1 = null;
		[ Bindable( true ), Browsable( true ), Category( "Data" ), NotifyParentProperty( true ),
			DesignerSerializationVisibility( DesignerSerializationVisibility.Content ),
			Description( "My Class1 Test" )]
		public Class1 Class1
		{
			get { if ( _class1 == null ) _class1 = new Class1(); return _class1; }
			set { _class1 = value; }
		}
		*/
	
		/// <summary>
		/// Gets or sets the <see cref="ZedGraph.GraphPane"/> property for the control
		/// </summary>
		/// <remarks>
		/// <see cref="ZedGraphControl"/> actually uses a <see cref="MasterPane"/> object
		/// to hold a list of <see cref="GraphPane"/> objects.  This property really only
		/// accesses the first <see cref="GraphPane"/> in the list.  If there is more
		/// than one <see cref="GraphPane"/>, use the <see cref="MasterPane"/>
		/// indexer property to access any of the <see cref="GraphPane"/> objects.</remarks>
		[
			Bindable( false ), Browsable( false ),
			DesignerSerializationVisibility( DesignerSerializationVisibility.Hidden )
		]
		//[
		//	Bindable( true ), Browsable( true ), Category( "Data" ), NotifyParentProperty( true ),
		//	AttributeProvider( typeof( GraphPane ) ),
		//	Description("Access to the primary GraphPane object associated with this control")
		//]
		public GraphPane GraphPane
		{
			get
			{
				// Just return the first GraphPane in the list
				lock ( this )
				{
					if ( _masterPane != null && _masterPane.PaneList.Count > 0 )
						return _masterPane[0];
					else
						return null;
				}
			}

			set
			{
				lock ( this )
				{
					//Clear the list, and replace it with the specified Graphpane
					if ( _masterPane != null )
					{
						_masterPane.PaneList.Clear();
						_masterPane.Add( value );
					}
				}
			}
		}

		/// <summary>
		/// Gets or sets a value that determines if all drawing operations for this control
		/// will be forced to operate in Anti-alias mode.  Note that if this value is set to
		/// "true", it overrides the setting for sub-objects.  Otherwise, the sub-object settings
		/// (such as <see cref="FontSpec.IsAntiAlias"/>)
		/// will be honored.
		/// </summary>
		[Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
		 DefaultValue( false ),
		 Description( "true to force all objects to be draw in anti-alias mode" )]
		public bool IsAntiAlias
		{
			get { return _isAntiAlias; }
			set { _isAntiAlias = value; }
		}

		/// <summary>
		/// Gets or sets a value that determines whether or not tooltips will be displayed
		/// when the mouse hovers over data values.
		/// </summary>
		/// <remarks>The displayed values are taken from <see cref="PointPair.Tag"/>
		/// if it is a <see cref="System.String"/> type, or <see cref="PointPairBase.ToString()"/>
		/// otherwise (using the <see cref="PointValueFormat" /> as a format string).
		/// Additionally, the user can custom format the values using the
		/// <see cref="PointValueEvent" /> event.  Note that <see cref="IsShowPointValues" />
		/// may be overridden by <see cref="IsShowCursorValues" />.
		/// </remarks>
		[	Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
			DefaultValue( false ),
			Description( "true to display tooltips when the mouse hovers over data points" )]
		public bool IsShowPointValues
		{
			get { return _isShowPointValues; }
			set { _isShowPointValues = value; }
		}

		/// <summary>
		/// Gets or sets a value that determines whether or not tooltips will be displayed
		/// showing the current scale values when the mouse is within the
		/// <see cref="Chart.Rect" />.
		/// </summary>
		/// <remarks>The displayed values are taken from the current mouse position, and formatted
		/// according to <see cref="PointValueFormat" /> and/or <see cref="PointDateFormat" />.  If this
		/// value is set to true, it overrides the <see cref="IsShowPointValues" /> setting.
		/// </remarks>
		[	Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
			DefaultValue( false ),
			Description( "true to display tooltips showing the current mouse position within the Chart area" )]
		public bool IsShowCursorValues
		{
			get { return _isShowCursorValues; }
			set { _isShowCursorValues = value; }
		}

		/// <summary>
		/// Gets or sets a value that determines whether or not editing of point data is allowed in
		/// the horizontal direction.
		/// </summary>
		/// <remarks>
		/// Editing is done by holding down the Alt key, and left-clicking on an individual point of
		/// a given <see cref="CurveItem" /> to drag it to a new location.  The Mouse and Key
		/// combination for this mode are modifiable using <see cref="EditButtons" /> and
		/// <see cref="EditModifierKeys" />.
		/// </remarks>
		/// <seealso cref="EditButtons" />
		/// <seealso cref="EditModifierKeys" />
		/// <seealso cref="IsEnableVEdit" />
		[	Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
			DefaultValue( false ),
			Description( "true to allow horizontal editing by alt-left-click-drag" )]
		public bool IsEnableHEdit
		{
			get { return _isEnableHEdit; }
			set { _isEnableHEdit = value; }
		}

		/// <summary>
		/// Gets or sets a value that determines whether or not editing of point data is allowed in
		/// the vertical direction.
		/// </summary>
		/// <remarks>
		/// Editing is done by holding down the Alt key, and left-clicking on an individual point of
		/// a given <see cref="CurveItem" /> to drag it to a new location.  The Mouse and Key
		/// combination for this mode are modifiable using <see cref="EditButtons" /> and
		/// <see cref="EditModifierKeys" />.
		/// </remarks>
		[	Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
			DefaultValue( false ),
			Description( "true to allow vertical editing by alt-left-click-drag" )]
		public bool IsEnableVEdit
		{
			get { return _isEnableVEdit; }
			set { _isEnableVEdit = value; }
		}

		/// <summary>
		/// Gets or sets a value that determines whether or not zooming is allowed for the control.
		/// </summary>
		/// <remarks>
		/// Zooming is done by left-clicking inside the <see cref="Chart.Rect"/> to drag
		/// out a rectangle, indicating the new scale ranges that will be part of the graph.
		/// </remarks>
		[	Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
			DefaultValue( true ),
			Description( "true to allow horizontal and vertical zooming by left-click-drag" )]
		public bool IsEnableZoom
		{
			set { _isEnableHZoom = value; _isEnableVZoom = value; }
		}
		/// <summary>
		/// Gets or sets a value that determines whether or not zooming is allowed for the control in
		/// the horizontal direction.
		/// </summary>
		/// <remarks>
		/// Zooming is done by left-clicking inside the <see cref="Chart.Rect"/> to drag
		/// out a rectangle, indicating the new scale ranges that will be part of the graph.
		/// </remarks>
		[	Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
			DefaultValue( true ),
			Description( "true to allow horizontal zooming by left-click-drag" )]
		public bool IsEnableHZoom
		{
			get { return _isEnableHZoom; }
			set { _isEnableHZoom = value; }
		}
		/// <summary>
		/// Gets or sets a value that determines whether or not zooming is allowed for the control in
		/// the vertical direction.
		/// </summary>
		/// <remarks>
		/// Zooming is done by left-clicking inside the <see cref="Chart.Rect"/> to drag
		/// out a rectangle, indicating the new scale ranges that will be part of the graph.
		/// </remarks>
		[	Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
			DefaultValue( true ),
			Description( "true to allow vertical zooming by left-click-drag" )]
		public bool IsEnableVZoom
		{
			get { return _isEnableVZoom; }
			set { _isEnableVZoom = value; }
		}
		/// <summary>
		/// Gets or sets a value that determines whether or not panning is allowed for the control in
		/// the horizontal direction.
		/// </summary>
		/// <remarks>
		/// Panning is done by clicking the middle mouse button (or holding down the shift key
		/// while clicking the left mouse button) inside the <see cref="Chart.Rect"/> and
		/// dragging the mouse around to shift the scale ranges as desired.
		/// </remarks>
		/// <seealso cref="IsEnableVPan"/>
		[	Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
			DefaultValue( true ),
			Description( "true to allow horizontal panning by middle-mouse-drag or shift-left-drag" )]
		public bool IsEnableHPan
		{
			get { return _isEnableHPan; }
			set { _isEnableHPan = value; }
		}

		/// <summary>
		/// Gets or sets a value that determines whether or not panning is allowed for the control in
		/// the vertical direction.
		/// </summary>
		/// <remarks>
		/// Panning is done by clicking the middle mouse button (or holding down the shift key
		/// while clicking the left mouse button) inside the <see cref="Chart.Rect"/> and
		/// dragging the mouse around to shift the scale ranges as desired.
		/// </remarks>
		/// <seealso cref="IsEnableHPan"/>
		[	Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
			DefaultValue( true ),
			Description( "true to allow vertical panning by middle-mouse-drag or shift-left-drag" )]
		public bool IsEnableVPan
		{
			get { return _isEnableVPan; }
			set { _isEnableVPan = value; }
		}

		/// <summary>
		/// Gets or sets a value that determines whether or not the context menu will be available.
		/// </summary>
		/// <remarks>The context menu is a menu that appears when you right-click on the
		/// <see cref="ZedGraphControl"/>.  It provides options for Zoom, Pan, AutoScale, Clipboard
		/// Copy, and toggle <see cref="IsShowPointValues"/>.
		/// </remarks>
		/// <value>true to allow the context menu, false to disable it</value>
		[	Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
			DefaultValue( true ),
			Description( "true to enable the right mouse button context menu" )]
		public bool IsShowContextMenu
		{
			get { return _isShowContextMenu; }
			set { _isShowContextMenu = value; }
		}

		/// <summary>
		/// Gets or sets a value that determines whether or not a message box will be shown
		/// in response to a context menu "Copy" command.
		/// </summary>
		/// <remarks>
		/// Note that, if this property is set to false, the user will receive no
		/// indicative feedback in response to a Copy action.
		/// </remarks>
		[Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
		 DefaultValue( true ),
		 Description( "true to show a message box after a 'Copy' context menu action completes" )]
		public bool IsShowCopyMessage
		{
			get { return _isShowCopyMessage; }
			set { _isShowCopyMessage = value; }
		}

		/// <summary>
		/// Gets or sets the <see cref="SaveFileDialog" /> instance that will be used
		/// by the "Save As..." context menu item.
		/// </summary>
		/// <remarks>
		/// This provides the opportunity to modify the dialog, such as setting the
		/// <see cref="FileDialog.InitialDirectory" /> property.
		/// </remarks>
		[Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
		 DefaultValue( true ),
		 Description( "Provides access to the SaveFileDialog for the 'Save As' menu item" )]
		public SaveFileDialog SaveFileDialog
		{
			get { return _saveFileDialog; }
			set { _saveFileDialog = value; }
		}

		/// <summary>
		/// Gets or sets a value that determines whether or not the visible aspect ratio of the
		/// <see cref="MasterPane" /> <see cref="PaneBase.Rect" /> will be preserved
		/// when printing this <see cref="ZedGraphControl" />.
		/// </summary>
		[	Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
			DefaultValue( true ),
			Description( "true to preserve the displayed aspect ratio when printing" )]
		public bool IsPrintKeepAspectRatio
		{
			get { return _isPrintKeepAspectRatio; }
			set { _isPrintKeepAspectRatio = value; }
		}

		/// <summary>
		/// Gets or sets a value that determines whether or not the <see cref="MasterPane" />
		/// <see cref="PaneBase.Rect" /> dimensions will be expanded to fill the
		/// available space when printing this <see cref="ZedGraphControl" />.
		/// </summary>
		/// <remarks>
		/// If <see cref="IsPrintKeepAspectRatio" /> is also true, then the <see cref="MasterPane" />
		/// <see cref="PaneBase.Rect" /> dimensions will be expanded to fit as large
		/// a space as possible while still honoring the visible aspect ratio.
		/// </remarks>
		[	Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
			DefaultValue( true ),
			Description( "true to resize to fill the page when printing" )]
		public bool IsPrintFillPage
		{
			get { return _isPrintFillPage; }
			set { _isPrintFillPage = value; }
		}
		/// <summary>
		/// Gets or sets a value that determines whether the settings of
		/// <see cref="ZedGraph.PaneBase.IsFontsScaled" /> and <see cref="PaneBase.IsPenWidthScaled" />
		/// will be overridden to true during printing operations.
		/// </summary>
		/// <remarks>
		/// Printing involves pixel maps that are typically of a dramatically different dimension
		/// than on-screen pixel maps.  Therefore, it becomes more important to scale the fonts and
		/// lines to give a printed image that looks like what is shown on-screen.  The default
		/// setting for <see cref="ZedGraph.PaneBase.IsFontsScaled" /> is true, but the default
		/// setting for <see cref="PaneBase.IsPenWidthScaled" /> is false.
		/// </remarks>
		/// <value>
		/// A value of true will cause both <see cref="ZedGraph.PaneBase.IsFontsScaled" /> and
		/// <see cref="PaneBase.IsPenWidthScaled" /> to be temporarily set to true during
		/// printing operations.
		/// </value>
		[	Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
			DefaultValue( true ),
			Description( "true to force font and pen width scaling when printing" )]
		public bool IsPrintScaleAll
		{
			get { return _isPrintScaleAll; }
			set { _isPrintScaleAll = value; }
		}

		/// <summary>
		/// Gets or sets a value that controls whether or not the axis value range for the scroll
		/// bars will be set automatically.
		/// </summary>
		/// <remarks>
		/// If this value is set to true, then the range of the scroll bars will be set automatically
		/// to the actual range of the data as returned by <see cref="CurveList.GetRange" /> at the
		/// time that <see cref="AxisChange" /> was last called.  Note that a value of true
		/// can override any setting of <see cref="ScrollMinX" />, <see cref="ScrollMaxX" />,
		/// <see cref="ScrollMinY" />, <see cref="ScrollMaxY" />, 
		/// <see cref="ScrollMinY2" />, and <see cref="ScrollMaxY2" />.  Note also that you must
		/// call <see cref="AxisChange" /> from the <see cref="ZedGraphControl" /> for this to
		/// work properly (e.g., don't call it directly from the <see cref="GraphPane" />.
		/// Alternatively, you can call <see cref="SetScrollRangeFromData" /> at anytime to set
		/// the scroll bar range.<br />
		/// <b>In most cases, you will probably want to disable
		/// <see cref="ZedGraph.GraphPane.IsBoundedRanges" /> before activating this option.</b>
		/// </remarks>
		[	Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
			DefaultValue( false ),
			Description( "true to automatically set the scroll bar range to the actual data range" )]
		public bool IsAutoScrollRange
		{
			get { return _isAutoScrollRange; }
			set { _isAutoScrollRange = value; }
		}

		/// <summary>
		/// Set a "grace" value that leaves a buffer area around the data when
		/// <see cref="IsAutoScrollRange" /> is true.
		/// </summary>
		/// <remarks>
		/// This value represents a fraction of the total range around each axis.  For example, if the
		/// axis ranges from 0 to 100, then a 0.05 value for ScrollGrace would set the scroll range
		/// to -5 to 105.
		/// </remarks>
		public double ScrollGrace
		{
			get { return _scrollGrace; }
			set { _scrollGrace = value; }
		}

		/// <summary>
		/// Gets or sets a value that determines if the horizontal scroll bar will be visible.
		/// </summary>
		/// <remarks>This scroll bar allows the display to be scrolled in the horizontal direction.
		/// Another option is display panning, in which the user can move the display around by
		/// clicking directly on it and dragging (see <see cref="IsEnableHPan"/> and <see cref="IsEnableVPan"/>).
		/// You can control the available range of scrolling with the <see cref="ScrollMinX"/> and
		/// <see cref="ScrollMaxX"/> properties.  Note that the scroll range can be set automatically by
		/// <see cref="IsAutoScrollRange" />.<br />
		/// <b>In most cases, you will probably want to disable
		/// <see cref="ZedGraph.GraphPane.IsBoundedRanges" /> before activating this option.</b>
		/// </remarks>
		/// <value>A boolean value.  true to display a horizontal scrollbar, false otherwise.</value>
		[	Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
			DefaultValue( false ),
			Description( "true to display the horizontal scroll bar" )]
		public bool IsShowHScrollBar
		{
			get { return _isShowHScrollBar; }
			set { _isShowHScrollBar = value; ZedGraphControl_ReSize( this, new EventArgs() ); }
		}
		/// <summary>
		/// Gets or sets a value that determines if the vertical scroll bar will be visible.
		/// </summary>
		/// <remarks>This scroll bar allows the display to be scrolled in the vertical direction.
		/// Another option is display panning, in which the user can move the display around by
		/// clicking directly on it and dragging (see <see cref="IsEnableHPan"/> and <see cref="IsEnableVPan"/>).
		/// You can control the available range of scrolling with the <see cref="ScrollMinY"/> and
		/// <see cref="ScrollMaxY"/> properties.
		/// Note that the vertical scroll bar only affects the <see cref="YAxis"/>; it has no impact on
		/// the <see cref="Y2Axis"/>.  The panning options affect both the <see cref="YAxis"/> and
		/// <see cref="Y2Axis"/>.  Note also that the scroll range can be set automatically by
		/// <see cref="IsAutoScrollRange" />.<br />
		/// <b>In most cases, you will probably want to disable
		/// <see cref="ZedGraph.GraphPane.IsBoundedRanges" /> before activating this option.</b>
		/// </remarks>
		/// <value>A boolean value.  true to display a vertical scrollbar, false otherwise.</value>
		[	Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
			DefaultValue( false ),
			Description( "true to display the vertical scroll bar" )]
		public bool IsShowVScrollBar
		{
			get { return _isShowVScrollBar; }
			set { _isShowVScrollBar = value; ZedGraphControl_ReSize( this, new EventArgs() ); }
		}

		/// <summary>
		/// Gets or sets a value that determines if the <see cref="XAxis" /> <see cref="Scale" />
		/// ranges for all <see cref="GraphPane" /> objects in the <see cref="MasterPane" /> will
		/// be forced to match.
		/// </summary>
		/// <remarks>
		/// If set to true (default is false), then all of the <see cref="GraphPane" /> objects
		/// in the <see cref="MasterPane" /> associated with this <see cref="ZedGraphControl" />
		/// will be forced to have matching scale ranges for the x axis.  That is, zoom, pan,
		/// and scroll operations will result in zoom/pan/scroll for all graphpanes simultaneously.
		/// </remarks>
		[	Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
			DefaultValue( false ),
			Description( "true to force the X axis ranges for all GraphPanes to match" )]
		public bool IsSynchronizeXAxes
		{
			get { return _isSynchronizeXAxes; }
			set
			{
				if ( _isSynchronizeXAxes != value )
					ZoomStatePurge();
				_isSynchronizeXAxes = value;
			}
		}

		/// <summary>
		/// Gets or sets a value that determines if the <see cref="YAxis" /> <see cref="Scale" />
		/// ranges for all <see cref="GraphPane" /> objects in the <see cref="MasterPane" /> will
		/// be forced to match.
		/// </summary>
		/// <remarks>
		/// If set to true (default is false), then all of the <see cref="GraphPane" /> objects
		/// in the <see cref="MasterPane" /> associated with this <see cref="ZedGraphControl" />
		/// will be forced to have matching scale ranges for the y axis.  That is, zoom, pan,
		/// and scroll operations will result in zoom/pan/scroll for all graphpanes simultaneously.
		/// </remarks>
		[	Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
			DefaultValue( false ),
			Description( "true to force the Y axis ranges for all GraphPanes to match" )]
		public bool IsSynchronizeYAxes
		{
			get { return _isSynchronizeYAxes; }
			set
			{
				if ( _isSynchronizeYAxes != value )
					ZoomStatePurge();
				_isSynchronizeYAxes = value;
			}
		}

		/// <summary>
		/// Gets or sets a value that determines if the vertical scroll bar will affect the Y2 axis.
		/// </summary>
		/// <remarks>
		/// The vertical scroll bar is automatically associated with the Y axis.  With this value, you
		/// can choose to include or exclude the Y2 axis with the scrolling.  Note that the Y2 axis
		/// scrolling is handled as a secondary.  The vertical scroll bar position always reflects
		/// the status of the Y axis.  This can cause the Y2 axis to "jump" when first scrolled if
		/// the <see cref="ScrollMinY2" /> and <see cref="ScrollMaxY2" /> values are not set to the
		/// same proportions as <see cref="ScrollMinY" /> and <see cref="ScrollMaxY" /> with respect
		/// to the actual <see cref="Scale.Min"/> and <see cref="Scale.Max" />.  Also note that
		/// this property is actually just an alias to the <see cref="ScrollRange.IsScrollable" />
		/// property of the first element of <see cref="YScrollRangeList" />.
		/// </remarks>
		/// <seealso cref="IsShowVScrollBar"/>
		/// <seealso cref="ScrollMinY2"/>
		/// <seealso cref="ScrollMaxY2"/>
		/// <seealso cref="YScrollRangeList" />
		/// <seealso cref="Y2ScrollRangeList" />
		[	Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
			DefaultValue( false ),
			Description( "true to scroll the Y2 axis along with the Y axis" )]
		public bool IsScrollY2
		{
			get
			{
				if ( _y2ScrollRangeList != null && _y2ScrollRangeList.Count > 0 )
					return _y2ScrollRangeList[0].IsScrollable;
				else
					return false;
			}
			set
			{
				if ( _y2ScrollRangeList != null && _y2ScrollRangeList.Count > 0 )
				{
					ScrollRange tmp = _y2ScrollRangeList[0];
					tmp.IsScrollable = value;
					_y2ScrollRangeList[0] = tmp;
				}
			}
		}

		/// <summary>
		/// Access the <see cref="ScrollRangeList" /> for the Y axes.
		/// </summary>
		/// <remarks>
		/// This list maintains the user scale ranges for the scroll bars for each axis
		/// in the <see cref="ZedGraph.GraphPane.YAxisList" />.  Each ordinal location in
		/// <see cref="YScrollRangeList" /> corresponds to an equivalent ordinal location
		/// in <see cref="ZedGraph.GraphPane.YAxisList" />.
		/// </remarks>
		/// <seealso cref="ScrollMinY" />
		/// <seealso cref="ScrollMaxY" />
		[Bindable( true ), Category( "Display" ), NotifyParentProperty( true )]
		[Description( "Sets the manual scroll bar ranges for the collection of Y axes" )]
		public ScrollRangeList YScrollRangeList
		{
			get { return _yScrollRangeList; }
		}

		/// <summary>
		/// Access the <see cref="ScrollRangeList" /> for the Y2 axes.
		/// </summary>
		/// <remarks>
		/// This list maintains the user scale ranges for the scroll bars for each axis
		/// in the <see cref="ZedGraph.GraphPane.Y2AxisList" />.  Each ordinal location in
		/// <see cref="Y2ScrollRangeList" /> corresponds to an equivalent ordinal location
		/// in <see cref="ZedGraph.GraphPane.Y2AxisList" />.
		/// </remarks>
		/// <seealso cref="ScrollMinY2" />
		/// <seealso cref="ScrollMaxY2" />
		[Bindable( true ), Category( "Display" ), NotifyParentProperty( true )]
		[Description( "Sets the manual scroll bar ranges for the collection of Y2 axes" )]
		public ScrollRangeList Y2ScrollRangeList
		{
			get { return _y2ScrollRangeList; }
		}

		/// <summary>
		/// The minimum value for the X axis scroll range.
		/// </summary>
		/// <remarks>
		/// Effectively, the minimum endpoint of the scroll range will cause the
		/// <see cref="Scale.Min"/> value to be set to <see cref="ScrollMinX"/>.  Note that this
		/// value applies only to the scroll bar settings.  Axis panning (see <see cref="IsEnableHPan"/>)
		/// is not affected by this value.  Note that this value can be overridden by
		/// <see cref="IsAutoScrollRange" /> and <see cref="SetScrollRangeFromData" />.
		/// </remarks>
		/// <value>A double value indicating the minimum axis value</value>
		[	Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
			DefaultValue( 0 ),
			Description( "Sets the manual scroll minimum value for the X axis" )]
		public double ScrollMinX
		{
			get { return _xScrollRange.Min; }
			set { _xScrollRange.Min = value; }
		}
		/// <summary>
		/// The maximum value for the X axis scroll range.
		/// </summary>
		/// <remarks>
		/// Effectively, the maximum endpoint of the scroll range will cause the
		/// <see cref="Scale.Max"/> value to be set to <see cref="ScrollMaxX"/>.  Note that this
		/// value applies only to the scroll bar settings.  Axis panning (see <see cref="IsEnableHPan"/>)
		/// is not affected by this value.  Note that this value can be overridden by
		/// <see cref="IsAutoScrollRange" /> and <see cref="SetScrollRangeFromData" />.
		/// </remarks>
		/// <value>A double value indicating the maximum axis value</value>
		[	Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
			DefaultValue( 0 ),
			Description( "Sets the manual scroll maximum value for the X axis" )]
		public double ScrollMaxX
		{
			get { return _xScrollRange.Max; }
			set { _xScrollRange.Max = value; }
		}
		/// <summary>
		/// The minimum value for the Y axis scroll range.
		/// </summary>
		/// <remarks>
		/// Effectively, the minimum endpoint of the scroll range will cause the
		/// <see cref="Scale.Min"/> value to be set to <see cref="ScrollMinY"/>.  Note that this
		/// value applies only to the scroll bar settings.  Axis panning (see <see cref="IsEnableVPan"/>)
		/// is not affected by this value.  Note that this value can be overridden by
		/// <see cref="IsAutoScrollRange" /> and <see cref="SetScrollRangeFromData" />.  Also note that
		/// this property is actually just an alias to the <see cref="ScrollRange.Min" />
		/// property of the first element of <see cref="YScrollRangeList" />.
		/// </remarks>
		/// <value>A double value indicating the minimum axis value</value>
		/// <seealso cref="YScrollRangeList" />
		[	Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
			DefaultValue( 0 ),
			Description( "Sets the manual scroll minimum value for the Y axis" )]
		public double ScrollMinY
		{
			get
			{
				if ( _yScrollRangeList != null && _yScrollRangeList.Count > 0 )
					return _yScrollRangeList[0].Min;
				else
					return double.NaN;
			}
			set
			{
				if ( _yScrollRangeList != null && _yScrollRangeList.Count > 0 )
				{
					ScrollRange tmp = _yScrollRangeList[0];
					tmp.Min = value;
					_yScrollRangeList[0] = tmp;
				}
			}
		}
		/// <summary>
		/// The maximum value for the Y axis scroll range.
		/// </summary>
		/// <remarks>
		/// Effectively, the maximum endpoint of the scroll range will cause the
		/// <see cref="Scale.Max"/> value to be set to <see cref="ScrollMaxY"/>.  Note that this
		/// value applies only to the scroll bar settings.  Axis panning (see <see cref="IsEnableVPan"/>)
		/// is not affected by this value.  Note that this value can be overridden by
		/// <see cref="IsAutoScrollRange" /> and <see cref="SetScrollRangeFromData" />.  Also note that
		/// this property is actually just an alias to the <see cref="ScrollRange.Max" />
		/// property of the first element of <see cref="YScrollRangeList" />.
		/// </remarks>
		/// <value>A double value indicating the maximum axis value</value>
		/// <seealso cref="YScrollRangeList" />
		[	Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
			DefaultValue( 0 ),
			Description( "Sets the manual scroll maximum value for the Y axis" )]
		public double ScrollMaxY
		{
			get
			{
				if ( _yScrollRangeList != null && _yScrollRangeList.Count > 0 )
					return _yScrollRangeList[0].Max;
				else
					return double.NaN;
			}
			set
			{
				if ( _yScrollRangeList != null && _yScrollRangeList.Count > 0 )
				{
					ScrollRange tmp = _yScrollRangeList[0];
					tmp.Max = value;
					_yScrollRangeList[0] = tmp;
				}
			}
		}
		/// <summary>
		/// The minimum value for the Y2 axis scroll range.
		/// </summary>
		/// <remarks>
		/// Effectively, the minimum endpoint of the scroll range will cause the
		/// <see cref="Scale.Min"/> value to be set to <see cref="ScrollMinY2"/>.  Note that this
		/// value applies only to the scroll bar settings.  Axis panning (see <see cref="IsEnableVPan"/>)
		/// is not affected by this value.  Note that this value can be overridden by
		/// <see cref="IsAutoScrollRange" /> and <see cref="SetScrollRangeFromData" />.  Also note that
		/// this property is actually just an alias to the <see cref="ScrollRange.Min" />
		/// property of the first element of <see cref="Y2ScrollRangeList" />.
		/// </remarks>
		/// <value>A double value indicating the minimum axis value</value>
		/// <seealso cref="Y2ScrollRangeList" />
		[	Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
			DefaultValue( 0 ),
			Description( "Sets the manual scroll minimum value for the Y2 axis" )]
		public double ScrollMinY2
		{
			get
			{
				if ( _y2ScrollRangeList != null && _y2ScrollRangeList.Count > 0 )
					return _y2ScrollRangeList[0].Min;
				else
					return double.NaN;
			}
			set
			{
				if ( _y2ScrollRangeList != null && _y2ScrollRangeList.Count > 0 )
				{
					ScrollRange tmp = _y2ScrollRangeList[0];
					tmp.Min = value;
					_y2ScrollRangeList[0] = tmp;
				}
			}
		}
		/// <summary>
		/// The maximum value for the Y2 axis scroll range.
		/// </summary>
		/// <remarks>
		/// Effectively, the maximum endpoint of the scroll range will cause the
		/// <see cref="Scale.Max"/> value to be set to <see cref="ScrollMaxY2"/>.  Note that this
		/// value applies only to the scroll bar settings.  Axis panning (see <see cref="IsEnableVPan"/>)
		/// is not affected by this value.  Note that this value can be overridden by
		/// <see cref="IsAutoScrollRange" /> and <see cref="SetScrollRangeFromData" />.  Also note that
		/// this property is actually just an alias to the <see cref="ScrollRange.Max" />
		/// property of the first element of <see cref="Y2ScrollRangeList" />.
		/// </remarks>
		/// <value>A double value indicating the maximum axis value</value>
		/// <seealso cref="Y2ScrollRangeList" />
		[	Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
			DefaultValue( 0 ),
			Description( "Sets the manual scroll maximum value for the Y2 axis" )]
		public double ScrollMaxY2
		{
			get
			{
				if ( _y2ScrollRangeList != null && _y2ScrollRangeList.Count > 0 )
					return _y2ScrollRangeList[0].Max;
				else
					return double.NaN;
			}
			set
			{
				if ( _y2ScrollRangeList != null && _y2ScrollRangeList.Count > 0 )
				{
					ScrollRange tmp = _y2ScrollRangeList[0];
					tmp.Max = value;
					_y2ScrollRangeList[0] = tmp;
				}
			}
		}

		/// <summary>
		/// Returns true if the user is currently scrolling via the scrollbar, or
		/// false if no scrolling is taking place.
		/// </summary>
		/// <remarks>
		/// This method just tests ScrollBar.Capture to see if the
		/// mouse has been captured by the scroll bar.  If so, scrolling is active.
		/// </remarks>
		public Boolean IsScrolling
		{
			get
			{
				if ( hScrollBar1 != null && vScrollBar1 != null )
					return hScrollBar1.Capture || vScrollBar1.Capture;
				else
					return false;
			}
		}


		/// <summary>
		/// Gets or sets the format for displaying tooltip values.
		/// This format is passed to <see cref="PointPairBase.ToString(string)"/>.
		/// </summary>
		/// <remarks>
		/// Use the <see cref="System.Globalization.NumberFormatInfo" /> type
		/// to determine the format strings.
		/// </remarks>
		[	Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
			DefaultValue( PointPair.DefaultFormat ),
			Description( "Sets the numeric display format string for the point value tooltips" )]
		public string PointValueFormat
		{
			get { return _pointValueFormat; }
			set { _pointValueFormat = value; }
		}

		/// <summary>
		/// Gets or sets the format for displaying tooltip values.
		/// This format is passed to <see cref="XDate.ToString(string)"/>.
		/// </summary>
		/// <remarks>
		/// Use the <see cref="System.Globalization.DateTimeFormatInfo" /> type
		/// to determine the format strings.
		/// </remarks>
		[	Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
			DefaultValue( XDate.DefaultFormatStr ),
			Description( "Sets the date display format for the point value tooltips" )]
		public string PointDateFormat
		{
			get { return _pointDateFormat; }
			set { _pointDateFormat = value; }
		}

		/// <summary>
		/// Gets or sets the step size fraction for zooming with the mouse wheel.
		/// A value of 0.1 will result in a 10% zoom step for each mouse wheel movement.
		/// </summary>
		[	Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
			DefaultValue( 0.1 ),
			Description( "Sets the step size fraction for zooming with the mouse wheel" )]
		public double ZoomStepFraction
		{
			get { return _zoomStepFraction; }
			set { _zoomStepFraction = value; }
		}

		/// <summary>
		/// Gets or sets a boolean value that determines if zooming with the wheel mouse
		/// is centered on the mouse location, or centered on the existing graph.
		/// </summary>
		[	Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
			DefaultValue( false ),
			Description( "true to center the mouse wheel zoom at the current mouse location" )]
		public bool IsZoomOnMouseCenter
		{
			get { return _isZoomOnMouseCenter; }
			set { _isZoomOnMouseCenter = value; }
		}

		/// <summary>
		/// Gets the graph pane's current image.
		/// <seealso cref="Bitmap"/>
		/// </summary>
		/// <exception cref="ZedGraphException">
		/// When the control has been disposed before this call.
		/// </exception>
		[	Bindable( false ), Browsable( false ),
			DesignerSerializationVisibility( DesignerSerializationVisibility.Hidden )]
		public Image GetImage()
		{
			lock ( this )
			{
				if ( BeenDisposed || _masterPane == null || _masterPane[0] == null )
					throw new ZedGraphException( "The control has been disposed" );

				return _masterPane.GetImage();
			}
		}

		/// <summary>
		/// This checks if the control has been disposed.  This is synonymous with
		/// the graph pane having been nulled or disposed.  Therefore this is the
		/// same as <c>ZedGraphControl.GraphPane == null</c>.
		/// </summary>
		[	Bindable( false ), Browsable( false ),
			DesignerSerializationVisibility( DesignerSerializationVisibility.Hidden )]
		public bool BeenDisposed
		{
			get
			{
				lock ( this ) return _masterPane == null;
			}
		}

		// Revision: JCarpenter 10/06
		/// <summary>
		/// Readonly property that gets the list of selected CurveItems
		/// </summary>
		public Selection Selection
		{
			get { return _selection; }
		}

		/// <summary>
		/// Gets or sets a value that determines whether or not selection is allowed for the control.
		/// </summary>
		[Bindable( true ), Category( "Display" ), NotifyParentProperty( true ),
			DefaultValue( false ),
			Description( "true to allow selecting Curves" )]
		public bool IsEnableSelection
		{
			get { return _isEnableSelection; }
			set
			{
				_isEnableSelection = value;

				/*
				if ( value )
				{
					this.Cursor = Cursors.Default;
					this.IsEnableZoom = false;
				}
				else
				{
					this.Cursor = Cursors.Cross;
					this.IsEnableZoom = true;
				}
				*/
			}
		}

	#endregion

	#region Methods

		/// <summary>
		/// Called by the system to update the control on-screen
		/// </summary>
		/// <param name="e">
		/// A PaintEventArgs object containing the Graphics specifications
		/// for this Paint event.
		/// </param>
		protected override void OnPaint( PaintEventArgs e )
		{
			lock ( this )
			{
				if ( BeenDisposed || _masterPane == null || this.GraphPane == null )
					return;

				if ( hScrollBar1 != null && this.GraphPane != null &&
					vScrollBar1 != null && _yScrollRangeList != null )
				{
					SetScroll( hScrollBar1, this.GraphPane.XAxis, _xScrollRange.Min, _xScrollRange.Max );
					SetScroll( vScrollBar1, this.GraphPane.YAxis, _yScrollRangeList[0].Min,
						_yScrollRangeList[0].Max );
				}

				SmoothingMode sModeSave = e.Graphics.SmoothingMode;
				TextRenderingHint sHintSave = e.Graphics.TextRenderingHint;
				if ( _isAntiAlias )
				{
					e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
					e.Graphics.TextRenderingHint = TextRenderingHint.AntiAlias;
				}
				base.OnPaint( e );

				// Add a try/catch pair since the users of the control can't catch this one
				try { _masterPane.Draw( e.Graphics ); }
				catch { }

				e.Graphics.SmoothingMode = sModeSave;
				e.Graphics.TextRenderingHint = sHintSave;
			}

/*
			// first, see if an old thread is still running
			if ( t != null && t.IsAlive )
			{
				t.Abort();
			}

			//dt = new DrawingThread( e.Graphics, _masterPane );
			//g = e.Graphics;

			// Fire off the new thread
			t = new Thread( new ParameterizedThreadStart( DoDrawingThread ) );
			//ct.ApartmentState = ApartmentState.STA;
			//ct.SetApartmentState( ApartmentState.STA );
			DrawingThreadData dtd;
			dtd._g = e.Graphics;
			dtd._masterPane = _masterPane;

			t.Start( dtd );
			//ct.Join();
*/
		}

//		Thread t = null;
		//DrawingThread dt = null;

/*
		/// <summary>
		/// 
		/// </summary>
		/// <param name="dtdobj"></param>
		public void DoDrawingThread( object dtdobj )
		{
			try
			{
				DrawingThreadData dtd = (DrawingThreadData) dtdobj;

				if ( dtd._g != null && dtd._masterPane != null )
					dtd._masterPane.Draw( dtd._g );

				//				else
				//				{
				//					using ( Graphics g2 = CreateGraphics() )
				//						_masterPane.Draw( g2 );
				//				}
			}
			catch
			{

			}
		}
*/

		/// <summary>
		/// Called when the control has been resized.
		/// </summary>
		/// <param name="sender">
		/// A reference to the control that has been resized.
		/// </param>
		/// <param name="e">
		/// An EventArgs object.
		/// </param>
		protected void ZedGraphControl_ReSize( object sender, System.EventArgs e )
		{
			lock ( this )
			{
				if ( BeenDisposed || _masterPane == null )
					return;

				Size newSize = this.Size;

				if ( _isShowHScrollBar )
				{
					hScrollBar1.Visible = true;
					newSize.Height -= this.hScrollBar1.Size.Height;
					hScrollBar1.Location = new Point( 0, newSize.Height );
					hScrollBar1.Size = new Size( newSize.Width, hScrollBar1.Height );
				}
				else
					hScrollBar1.Visible = false;

				if ( _isShowVScrollBar )
				{
					vScrollBar1.Visible = true;
					newSize.Width -= this.vScrollBar1.Size.Width;
					vScrollBar1.Location = new Point( newSize.Width, 0 );
					vScrollBar1.Size = new Size( vScrollBar1.Width, newSize.Height );
				}
				else
					vScrollBar1.Visible = false;

				using ( Graphics g = this.CreateGraphics() )
				{
					_masterPane.ReSize( g, new RectangleF( 0, 0, newSize.Width, newSize.Height ) );
					//g.Dispose();
				}
				this.Invalidate();
			}
		}
		/// <summary>This performs an axis change command on the graphPane.
		/// </summary>
		/// <remarks>
		/// This is the same as
		/// <c>ZedGraphControl.GraphPane.AxisChange( ZedGraphControl.CreateGraphics() )</c>, however,
		/// this method also calls <see cref="SetScrollRangeFromData" /> if <see cref="IsAutoScrollRange" />
		/// is true.
		/// </remarks>
		public virtual void AxisChange()
		{
			lock ( this )
			{
				if ( BeenDisposed || _masterPane == null )
					return;

				using ( Graphics g = this.CreateGraphics() )
				{
					_masterPane.AxisChange( g );
					//g.Dispose();
				}

				if ( _isAutoScrollRange )
					SetScrollRangeFromData();
			}
		}
	#endregion

	#region Mouse Events

		/// <summary>
		/// Handle a MouseDown event in the <see cref="ZedGraphControl" />
		/// </summary>
		/// <param name="sender">A reference to the <see cref="ZedGraphControl" /></param>
		/// <param name="e">A <see cref="MouseEventArgs" /> instance</param>
		protected void ZedGraphControl_MouseDown( object sender, MouseEventArgs e )
		{
			_isPanning = false;
			_isZooming = false;
			_isEditing = false;
			_isSelecting = false;
			_dragPane = null;

			Point mousePt = new Point( e.X, e.Y );

			// Callback for doubleclick events
			if ( _masterPane != null && e.Clicks > 1 && this.DoubleClickEvent != null )
			{
				if ( this.DoubleClickEvent( this, e ) )
					return;
			}

			// Provide Callback for MouseDown events
			if ( _masterPane != null && this.MouseDownEvent != null )
			{
				if ( this.MouseDownEvent( this, e ) )
					return;
			}

			if ( e.Clicks > 1 || _masterPane == null )
				return;

			// First, see if the click is within a Linkable object within any GraphPane
			GraphPane pane = this.MasterPane.FindPane( mousePt );
			if (	pane != null &&
					e.Button == _linkButtons && Control.ModifierKeys == _linkModifierKeys )
			{
				object source;
				Link link;
				int index;
				using ( Graphics g = this.CreateGraphics() )
				{
					float scaleFactor = pane.CalcScaleFactor();
					if ( pane.FindLinkableObject( mousePt, g, scaleFactor, out source, out link, out index ) )
					{
						if ( LinkEvent != null && LinkEvent( this, pane, source, link, index ) )
							return;

						string url;
						CurveItem curve = source as CurveItem;

						if ( curve != null )
							url = link.MakeCurveItemUrl( pane, curve, index );
						else
							url = link._url;

						if ( url != string.Empty )
						{
							System.Diagnostics.Process.Start( url );
							// linkable objects override any other actions with mouse
							return;
						}
					}
					//g.Dispose();
				}
			}

			// Second, Check to see if it's within a Chart Rect
			pane = this.MasterPane.FindChartRect( mousePt );
			//Rectangle rect = new Rectangle( mousePt, new Size( 1, 1 ) );

			if ( pane != null &&
				( _isEnableHPan || _isEnableVPan ) &&
				( ( e.Button == _panButtons && Control.ModifierKeys == _panModifierKeys ) ||
				( e.Button == _panButtons2 && Control.ModifierKeys == _panModifierKeys2 ) ) )
			{
				_isPanning = true;
				_dragStartPt = mousePt;
				_dragPane = pane;
				//_zoomState = new ZoomState( _dragPane, ZoomState.StateType.Pan );
				ZoomStateSave( _dragPane, ZoomState.StateType.Pan );
			}
			else if ( pane != null && ( _isEnableHZoom || _isEnableVZoom ) &&
				( ( e.Button == _zoomButtons && Control.ModifierKeys == _zoomModifierKeys ) ||
				( e.Button == _zoomButtons2 && Control.ModifierKeys == _zoomModifierKeys2 ) ) )
			{
				_isZooming = true;
				_dragStartPt = mousePt;
				_dragEndPt = mousePt;
				_dragEndPt.Offset( 1, 1 );
				_dragPane = pane;
				ZoomStateSave( _dragPane, ZoomState.StateType.Zoom );
			}
			//Revision: JCarpenter 10/06
			else if ( pane != null && _isEnableSelection && e.Button == _selectButtons &&
				( Control.ModifierKeys == _selectModifierKeys ||
					Control.ModifierKeys == _selectAppendModifierKeys ))
			{
				_isSelecting = true;
				_dragStartPt = mousePt;
				_dragEndPt = mousePt;
				_dragEndPt.Offset( 1, 1 );
				_dragPane = pane;
			}
			else if ( pane != null && ( _isEnableHEdit || _isEnableVEdit ) &&
				 ( e.Button == EditButtons && Control.ModifierKeys == EditModifierKeys ) )
			{

				// find the point that was clicked, and make sure the point list is editable
				// and that it's a primary Y axis (the first Y or Y2 axis)
				if ( pane.FindNearestPoint( mousePt, out _dragCurve, out _dragIndex ) &&
							_dragCurve.Points is IPointListEdit )
				{
					_isEditing = true;
					_dragPane = pane;
					_dragStartPt = mousePt;
					_dragStartPair = _dragCurve[_dragIndex];
				}
			}
		}

		/// <summary>
		/// Set the cursor according to the current mouse location.
		/// </summary>
		protected void SetCursor()
		{
			SetCursor( this.PointToClient( Control.MousePosition ) );
		}

		/// <summary>
		/// Set the cursor according to the current mouse location.
		/// </summary>
		protected void SetCursor( Point mousePt )
		{
			if ( _masterPane != null )
			{
				GraphPane pane = _masterPane.FindChartRect( mousePt );
				if ( ( _isEnableHPan || _isEnableVPan ) && ( Control.ModifierKeys == Keys.Shift || _isPanning ) &&
					( pane != null || _isPanning ) )
					this.Cursor = Cursors.Hand;
				else if ( ( _isEnableVZoom || _isEnableHZoom ) && ( pane != null || _isZooming ) )
					this.Cursor = Cursors.Cross;
				else if ( _isEnableSelection && ( pane != null || _isSelecting ) )
					this.Cursor = Cursors.Cross;
				else
					this.Cursor = Cursors.Default;

				//			else if ( isZoomMode || isPanMode )
				//				this.Cursor = Cursors.No;
			}
		}

		/// <summary>
		/// Handle a KeyUp event
		/// </summary>
		/// <param name="sender">The <see cref="ZedGraphControl" /> in which the KeyUp occurred.</param>
		/// <param name="e">A <see cref="KeyEventArgs" /> instance.</param>
		protected void ZedGraphControl_KeyUp( object sender, KeyEventArgs e )
		{
			SetCursor();
		}

		/// <summary>
		/// Handle the Key Events so ZedGraph can Escape out of a panning or zooming operation.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		protected void ZedGraphControl_KeyDown( object sender, System.Windows.Forms.KeyEventArgs e )
		{
			SetCursor();

			if ( e.KeyCode == Keys.Escape )
			{
				if ( _isPanning )
					HandlePanCancel();
				if ( _isZooming )
					HandleZoomCancel();
				if ( _isEditing )
					HandleEditCancel();
				//if ( _isSelecting )
				// Esc always cancels the selection
				HandleSelectionCancel();

				_isZooming = false;
				_isPanning = false;
				_isEditing = false;
				_isSelecting = false;

				Refresh();
			}
		}

		/// <summary>
		/// Handle a MouseUp event in the <see cref="ZedGraphControl" />
		/// </summary>
		/// <param name="sender">A reference to the <see cref="ZedGraphControl" /></param>
		/// <param name="e">A <see cref="MouseEventArgs" /> instance</param>
		protected void ZedGraphControl_MouseUp( object sender, MouseEventArgs e )
		{
			// Provide Callback for MouseUp events
			if ( _masterPane != null && this.MouseUpEvent != null )
			{
				if ( this.MouseUpEvent( this, e ) )
					return;
			}

			if ( _masterPane != null && _dragPane != null )
			{
				// If the MouseUp event occurs, the user is done dragging.
				if ( _isZooming )
					HandleZoomFinish( sender, e );
				else if ( _isPanning )
					HandlePanFinish();
				else if ( _isEditing )
					HandleEditFinish();
				//Revision: JCarpenter 10/06
				else if ( _isSelecting )
					HandleSelectionFinish( sender, e );
			}

			// Reset the rectangle.
			//dragStartPt = new Rectangle( 0, 0, 0, 0 );
			_dragPane = null;
			_isZooming = false;
			_isPanning = false;
			_isEditing = false;
			_isSelecting = false;

			Cursor.Current = Cursors.Default;
		}

		/// <summary>
		/// Make a string label that corresponds to a user scale value.
		/// </summary>
		/// <param name="axis">The axis from which to obtain the scale value.  This determines
		/// if it's a date value, linear, log, etc.</param>
		/// <param name="val">The value to be made into a label</param>
		/// <param name="iPt">The ordinal position of the value</param>
		/// <param name="isOverrideOrdinal">true to override the ordinal settings of the axis,
		/// and prefer the actual value instead.</param>
		/// <returns>The string label.</returns>
		protected string MakeValueLabel( Axis axis, double val, int iPt, bool isOverrideOrdinal )
		{
			if ( axis != null )
			{
				if ( axis.Scale.IsDate || axis.Scale.Type == AxisType.DateAsOrdinal )
				{
					return XDate.ToString( val, _pointDateFormat );
				}
				else if ( axis._scale.IsText && axis._scale._textLabels != null )
				{
					int i = iPt;
					if ( isOverrideOrdinal )
						i = (int)( val - 0.5 );

					if ( i >= 0 && i < axis._scale._textLabels.Length )
						return axis._scale._textLabels[i];
					else
						return ( i + 1 ).ToString();
				}
				else if ( axis.Scale.IsAnyOrdinal && axis.Scale.Type != AxisType.LinearAsOrdinal
								&& !isOverrideOrdinal )
				{
					return iPt.ToString( _pointValueFormat );
				}
				else
					return val.ToString( _pointValueFormat );
			}
			else
				return "";
		}

		/// <summary>
		/// protected method for handling MouseMove events to display tooltips over
		/// individual datapoints.
		/// </summary>
		/// <param name="sender">
		/// A reference to the control that has the MouseMove event.
		/// </param>
		/// <param name="e">
		/// A MouseEventArgs object.
		/// </param>
		protected void ZedGraphControl_MouseMove( object sender, MouseEventArgs e )
		{
			if ( _masterPane != null )
			{
				// Provide Callback for MouseMove events
				if ( this.MouseMoveEvent != null && this.MouseMoveEvent( this, e ) )
					return;

				Point mousePt = new Point( e.X, e.Y );

				//Point tempPt = this.PointToClient( Control.MousePosition );

				SetCursor( mousePt );

				// If the mouse is being dragged,
				// undraw and redraw the rectangle as the mouse moves.
				if ( _isZooming )
					HandleZoomDrag( mousePt );
				else if ( _isPanning )
					HandlePanDrag( mousePt );
				else if ( _isEditing )
					HandleEditDrag( mousePt );
				else if ( _isShowCursorValues )
					HandleCursorValues( mousePt );
				else if ( _isShowPointValues )
					HandlePointValues( mousePt );
				//Revision: JCarpenter 10/06
				else if ( _isSelecting )
					HandleZoomDrag( mousePt );
			}
		}

		private Point HandlePointValues( Point mousePt )
		{
			int iPt;
			GraphPane pane;
			object nearestObj;

			using ( Graphics g = this.CreateGraphics() )
			{

				if ( _masterPane.FindNearestPaneObject( mousePt,
					g, out pane, out nearestObj, out iPt ) )
				{
					if ( nearestObj is CurveItem && iPt >= 0 )
					{
						CurveItem curve = (CurveItem)nearestObj;
						// Provide Callback for User to customize the tooltips
						if ( this.PointValueEvent != null )
						{
							string label = this.PointValueEvent( this, pane, curve, iPt );
							if ( label != null && label.Length > 0 )
							{
								this.pointToolTip.SetToolTip( this, label );
								this.pointToolTip.Active = true;
							}
							else
								this.pointToolTip.Active = false;
						}
						else
						{

							if ( curve is PieItem )
							{
								this.pointToolTip.SetToolTip( this,
									( (PieItem)curve ).Value.ToString( _pointValueFormat ) );
							}
//							else if ( curve is OHLCBarItem || curve is JapaneseCandleStickItem )
//							{
//								StockPt spt = (StockPt)curve.Points[iPt];
//								this.pointToolTip.SetToolTip( this, ( (XDate) spt.Date ).ToString( "MM/dd/yyyy" ) + "\nOpen: $" +
//								spt.Open.ToString( "N2" ) +
//								"\nHigh: $" +
//								spt.High.ToString( "N2" ) + "\nLow: $" +
//								spt.Low.ToString( "N2" ) + "\nClose: $" +
//								spt.Close.ToString
//								( "N2" ) );
//							}
							else
							{
								PointPair pt = curve.Points[iPt];

								if ( pt.Tag is string )
									this.pointToolTip.SetToolTip( this, (string)pt.Tag );
								else
								{
									double xVal, yVal, lowVal;
									ValueHandler valueHandler = new ValueHandler( pane, false );
									if ( ( curve is BarItem || curve is ErrorBarItem || curve is HiLowBarItem )
											&& pane.BarSettings.Base != BarBase.X )
										valueHandler.GetValues( curve, iPt, out yVal, out lowVal, out xVal );
									else
										valueHandler.GetValues( curve, iPt, out xVal, out lowVal, out yVal );

									string xStr = MakeValueLabel( pane.XAxis, xVal, iPt,
										curve.IsOverrideOrdinal );
									string yStr = MakeValueLabel( curve.GetYAxis( pane ), yVal, iPt,
										curve.IsOverrideOrdinal );

									this.pointToolTip.SetToolTip( this, "( " + xStr + ", " + yStr + " )" );

									//this.pointToolTip.SetToolTip( this,
									//	curve.Points[iPt].ToString( this.pointValueFormat ) );
								}
							}

							this.pointToolTip.Active = true;
						}
					}
					else
						this.pointToolTip.Active = false;
				}
				else
					this.pointToolTip.Active = false;

				//g.Dispose();
			}
			return mousePt;
		}

		private Point HandleCursorValues( Point mousePt )
		{
			GraphPane pane = _masterPane.FindPane( mousePt );
			if ( pane != null && pane.Chart._rect.Contains( mousePt ) )
			{
				double x, y, y2;
				pane.ReverseTransform( mousePt, out x, out y, out y2 );
				string xStr = MakeValueLabel( pane.XAxis, x, -1, true );
				string yStr = MakeValueLabel( pane.YAxis, y, -1, true );
				string y2Str = MakeValueLabel( pane.Y2Axis, y2, -1, true );

				this.pointToolTip.SetToolTip( this, "( " + xStr + ", " + yStr + ", " + y2Str + " )" );
				this.pointToolTip.Active = true;
			}
			else
				this.pointToolTip.Active = false;
			return mousePt;
		}


	#endregion

	#region Mouse Wheel Zoom Events

		/// <summary>
		/// Handle a MouseWheel event in the <see cref="ZedGraphControl" />
		/// </summary>
		/// <param name="sender">A reference to the <see cref="ZedGraphControl" /></param>
		/// <param name="e">A <see cref="MouseEventArgs" /> instance</param>
		protected void ZedGraphControl_MouseWheel( object sender, MouseEventArgs e )
		{
			if ( ( _isEnableVZoom || _isEnableHZoom ) && _masterPane != null )
			{
				GraphPane pane = this.MasterPane.FindChartRect( new PointF( e.X, e.Y ) );
				if ( pane != null && e.Delta != 0 )
				{
					ZoomState oldState = ZoomStateSave( pane, ZoomState.StateType.WheelZoom );
					//ZoomState oldState = pane.ZoomStack.Push( pane, ZoomState.StateType.Zoom );

					PointF centerPoint = new PointF( e.X, e.Y );
					double zoomFraction = ( 1 + ( e.Delta < 0 ? 1.0 : -1.0 ) * ZoomStepFraction );

					ZoomPane( pane, zoomFraction, centerPoint, _isZoomOnMouseCenter, false );

					ApplyToAllPanes( pane );

					using ( Graphics g = this.CreateGraphics() )
					{
						// always AxisChange() the dragPane
						pane.AxisChange( g );

						foreach ( GraphPane tempPane in _masterPane._paneList )
						{
							if ( tempPane != pane && ( _isSynchronizeXAxes || _isSynchronizeYAxes ) )
								tempPane.AxisChange( g );
						}
					}

					ZoomStatePush( pane );

					// Provide Callback to notify the user of zoom events
					if ( this.ZoomEvent != null )
						this.ZoomEvent( this, oldState, new ZoomState( pane, ZoomState.StateType.WheelZoom ) );

					this.Refresh();

				}
			}
		}

		/// <summary>
		/// Zoom a specified pane in or out according to the specified zoom fraction.
		/// </summary>
		/// <remarks>
		/// The zoom will occur on the <see cref="XAxis" />, <see cref="YAxis" />, and
		/// <see cref="Y2Axis" /> only if the corresponding flag, <see cref="IsEnableHZoom" /> or
		/// <see cref="IsEnableVZoom" />, is true.  Note that if there are multiple Y or Y2 axes, all of
		/// them will be zoomed.
		/// </remarks>
		/// <param name="pane">The <see cref="GraphPane" /> instance to be zoomed.</param>
		/// <param name="zoomFraction">The fraction by which to zoom, less than 1 to zoom in, greater than
		/// 1 to zoom out.  For example, 0.9 will zoom in such that the scale is 90% of what it was
		/// originally.</param>
		/// <param name="centerPt">The screen position about which the zoom will be centered.  This
		/// value is only used if <see paramref="isZoomOnCenter" /> is true.
		/// </param>
		/// <param name="isZoomOnCenter">true to cause the zoom to be centered on the point
		/// <see paramref="centerPt" />, false to center on the <see cref="Chart.Rect" />.
		/// </param>
		/// <param name="isRefresh">true to force a refresh of the control, false to leave it unrefreshed</param>
		protected void ZoomPane( GraphPane pane, double zoomFraction, PointF centerPt,
					bool isZoomOnCenter, bool isRefresh )
		{
			double x;
			double[] y;
			double[] y2;

			pane.ReverseTransform( centerPt, out x, out y, out y2 );

			if ( _isEnableHZoom )
				ZoomScale( pane.XAxis, zoomFraction, x, isZoomOnCenter );
			if ( _isEnableVZoom )
			{
				for ( int i = 0; i < pane.YAxisList.Count; i++ )
					ZoomScale( pane.YAxisList[i], zoomFraction, y[i], isZoomOnCenter );
				for ( int i = 0; i < pane.Y2AxisList.Count; i++ )
					ZoomScale( pane.Y2AxisList[i], zoomFraction, y2[i], isZoomOnCenter );
			}

			using ( Graphics g = this.CreateGraphics() )
			{
				pane.AxisChange( g );
				//g.Dispose();
			}

			this.SetScroll( this.hScrollBar1, pane.XAxis, _xScrollRange.Min, _xScrollRange.Max );
			this.SetScroll( this.vScrollBar1, pane.YAxis, _yScrollRangeList[0].Min,
				_yScrollRangeList[0].Max );

			if ( isRefresh )
				Refresh();
		}

		/// <summary>
		/// Zoom a specified pane in or out according to the specified zoom fraction.
		/// </summary>
		/// <remarks>
		/// The zoom will occur on the <see cref="XAxis" />, <see cref="YAxis" />, and
		/// <see cref="Y2Axis" /> only if the corresponding flag, <see cref="IsEnableHZoom" /> or
		/// <see cref="IsEnableVZoom" />, is true.  Note that if there are multiple Y or Y2 axes, all of
		/// them will be zoomed.
		/// </remarks>
		/// <param name="pane">The <see cref="GraphPane" /> instance to be zoomed.</param>
		/// <param name="zoomFraction">The fraction by which to zoom, less than 1 to zoom in, greater than
		/// 1 to zoom out.  For example, 0.9 will zoom in such that the scale is 90% of what it was
		/// originally.</param>
		/// <param name="centerPt">The screen position about which the zoom will be centered.  This
		/// value is only used if <see paramref="isZoomOnCenter" /> is true.
		/// </param>
		/// <param name="isZoomOnCenter">true to cause the zoom to be centered on the point
		/// <see paramref="centerPt" />, false to center on the <see cref="Chart.Rect" />.
		/// </param>
		public void ZoomPane( GraphPane pane, double zoomFraction, PointF centerPt, bool isZoomOnCenter )
		{
			ZoomPane( pane, zoomFraction, centerPt, isZoomOnCenter, true );
		}


		/// <summary>
		/// Zoom the specified axis by the specified amount, with the center of the zoom at the
		/// (optionally) specified point.
		/// </summary>
		/// <remarks>
		/// This method is used for MouseWheel zoom operations</remarks>
		/// <param name="axis">The <see cref="Axis" /> to be zoomed.</param>
		/// <param name="zoomFraction">The zoom fraction, less than 1.0 to zoom in, greater than 1.0 to
		/// zoom out.  That is, a value of 0.9 will zoom in such that the scale length is 90% of what
		/// it previously was.</param>
		/// <param name="centerVal">The location for the center of the zoom.  This is only used if
		/// <see paramref="IsZoomOnMouseCenter" /> is true.</param>
		/// <param name="isZoomOnCenter">true if the zoom is to be centered at the
		/// <see paramref="centerVal" /> screen position, false for the zoom to be centered within
		/// the <see cref="Chart.Rect" />.
		/// </param>
		protected void ZoomScale( Axis axis, double zoomFraction, double centerVal, bool isZoomOnCenter )
		{
			if ( axis != null && zoomFraction > 0.0001 && zoomFraction < 1000.0 )
			{
				Scale scale = axis._scale;
/*
				if ( axis.Scale.IsLog )
				{
					double ratio = Math.Sqrt( axis._scale._max / axis._scale._min * zoomFraction );

					if ( !isZoomOnCenter )
						centerVal = Math.Sqrt( axis._scale._max * axis._scale._min );

					axis._scale._min = centerVal / ratio;
					axis._scale._max = centerVal * ratio;
				}
				else
				{
*/
					double minLin = axis._scale._minLinearized;
					double maxLin = axis._scale._maxLinearized;
					double range = ( maxLin - minLin ) * zoomFraction / 2.0;

					if ( !isZoomOnCenter )
						centerVal = ( maxLin + minLin ) / 2.0;

					axis._scale._minLinearized = centerVal - range;
					axis._scale._maxLinearized = centerVal + range;
//				}

				axis._scale._minAuto = false;
				axis._scale._maxAuto = false;
			}
		}

	#endregion

	#region Pan Events

		private Point HandlePanDrag( Point mousePt )
		{
			double x1, x2;
			double[] y1, y2, yy1, yy2;
			//PointF endPoint = mousePt;
			//PointF startPoint = ( (Control)sender ).PointToClient( this.dragRect.Location );

			_dragPane.ReverseTransform( _dragStartPt, out x1, out y1, out yy1 );
			_dragPane.ReverseTransform( mousePt, out x2, out y2, out yy2 );

			if ( _isEnableHPan )
			{
				PanScale( _dragPane.XAxis, x1, x2 );
				this.SetScroll( this.hScrollBar1, _dragPane.XAxis, _xScrollRange.Min, _xScrollRange.Max );
			}
			if ( _isEnableVPan )
			{
				for ( int i = 0; i < y1.Length; i++ )
					PanScale( _dragPane.YAxisList[i], y1[i], y2[i] );
				for ( int i = 0; i < yy1.Length; i++ )
					PanScale( _dragPane.Y2AxisList[i], yy1[i], yy2[i] );
				this.SetScroll( this.vScrollBar1, _dragPane.YAxis, _yScrollRangeList[0].Min,
					_yScrollRangeList[0].Max );
			}

			ApplyToAllPanes( _dragPane );

			Refresh();

			_dragStartPt = mousePt;

			return mousePt;
		}

		private void HandlePanFinish()
		{
			// push the prior saved zoomstate, since the scale ranges have already been changed on
			// the fly during the panning operation
			if ( _zoomState != null && _zoomState.IsChanged( _dragPane ) )
			{
				//_dragPane.ZoomStack.Push( _zoomState );
				ZoomStatePush( _dragPane );

				// Provide Callback to notify the user of pan events
				if ( this.ZoomEvent != null )
					this.ZoomEvent( this, _zoomState,
						new ZoomState( _dragPane, ZoomState.StateType.Pan ) );

				_zoomState = null;
			}
		}

		private void HandlePanCancel()
		{
			if ( _isPanning )
			{
				if ( _zoomState != null && _zoomState.IsChanged( _dragPane ) )
				{
					ZoomStateRestore( _dragPane );
					//_zoomState.ApplyState( _dragPane );
					//_zoomState = null;
				}
				_isPanning = false;
				Refresh();

				ZoomStateClear();
			}
		}

		/// <summary>
		/// Handle a panning operation for the specified <see cref="Axis" />.
		/// </summary>
		/// <param name="axis">The <see cref="Axis" /> to be panned</param>
		/// <param name="startVal">The value where the pan started.  The scale range
		/// will be shifted by the difference between <see paramref="startVal" /> and
		/// <see paramref="endVal" />.
		/// </param>
		/// <param name="endVal">The value where the pan ended.  The scale range
		/// will be shifted by the difference between <see paramref="startVal" /> and
		/// <see paramref="endVal" />.
		/// </param>
		protected void PanScale( Axis axis, double startVal, double endVal )
		{
			if ( axis != null )
			{
				Scale scale = axis._scale;
				double delta = scale.Linearize( startVal ) - scale.Linearize( endVal );

				scale._minLinearized += delta;
				scale._maxLinearized += delta;

				scale._minAuto = false;
				scale._maxAuto = false;

/*
				if ( axis.Type == AxisType.Log )
				{
					axis._scale._min *= startVal / endVal;
					axis._scale._max *= startVal / endVal;
				}
				else
				{
					axis._scale._min += startVal - endVal;
					axis._scale._max += startVal - endVal;
				}
*/
			}
		}

	#endregion

	#region Edit Point Events

		private void HandleEditDrag( Point mousePt )
		{
			// get the scale values that correspond to the current point
			double curX, curY;
			_dragPane.ReverseTransform( mousePt, _dragCurve.IsY2Axis, _dragCurve.YAxisIndex,
					out curX, out curY );
			double startX, startY;
			_dragPane.ReverseTransform( _dragStartPt, _dragCurve.IsY2Axis, _dragCurve.YAxisIndex,
					out startX, out startY );

			// calculate the new scale values for the point
			PointPair newPt = new PointPair( _dragStartPair );

			Scale xScale = _dragPane.XAxis._scale;
			if ( _isEnableHEdit )
				newPt.X = xScale.DeLinearize( xScale.Linearize( newPt.X ) +
							xScale.Linearize( curX )- xScale.Linearize( startX ) );

			Scale yScale = _dragCurve.GetYAxis( _dragPane )._scale;
			if ( _isEnableVEdit )
				newPt.Y = yScale.DeLinearize( yScale.Linearize( newPt.Y ) +
							yScale.Linearize( curY ) - yScale.Linearize( startY ) );

			// save the data back to the point list
			IPointListEdit list = _dragCurve.Points as IPointListEdit;
			if ( list != null )
				list[_dragIndex] = newPt;

			// force a redraw
			Refresh();
		}

		private void HandleEditFinish()
		{
			if ( this.PointEditEvent != null )
				this.PointEditEvent( this, _dragPane, _dragCurve, _dragIndex );
		}

		private void HandleEditCancel()
		{
			if ( _isEditing )
			{
				IPointListEdit list = _dragCurve.Points as IPointListEdit;
				if ( list != null )
					list[_dragIndex] = _dragStartPair;
				_isEditing = false;
				Refresh();
			}
		}

	#endregion

	#region Zoom Events

		private void HandleZoomDrag( Point mousePt )
		{
			// Hide the previous rectangle by calling the
			// DrawReversibleFrame method with the same parameters.
			Rectangle rect = CalcScreenRect( _dragStartPt, _dragEndPt );
			ControlPaint.DrawReversibleFrame( rect, this.BackColor, FrameStyle.Dashed );

			// Bound the zoom to the ChartRect
			_dragEndPt = Point.Round( BoundPointToRect( mousePt, _dragPane.Chart._rect ) );
			rect = CalcScreenRect( _dragStartPt, _dragEndPt );
			// Draw the new rectangle by calling DrawReversibleFrame again.
			ControlPaint.DrawReversibleFrame( rect, this.BackColor, FrameStyle.Dashed );
		}

		private void HandleZoomFinish( object sender, MouseEventArgs e )
		{
			PointF mousePtF = BoundPointToRect( new Point( e.X, e.Y ), _dragPane.Chart._rect );

			// Only accept a drag if it covers at least 5 pixels in each direction
			//Point curPt = ( (Control)sender ).PointToScreen( Point.Round( mousePt ) );
			if ( ( Math.Abs( mousePtF.X - _dragStartPt.X ) > 4 || !_isEnableHZoom ) &&
					( Math.Abs( mousePtF.Y - _dragStartPt.Y ) > 4 || !_isEnableVZoom ) )
			{
				// Draw the rectangle to be evaluated. Set a dashed frame style
				// using the FrameStyle enumeration.
				//ControlPaint.DrawReversibleFrame( this.dragRect,
				//	this.BackColor, FrameStyle.Dashed );

				double x1, x2;
				double[] y1, y2, yy1, yy2;
				//PointF startPoint = ( (Control)sender ).PointToClient( this.dragRect.Location );

				_dragPane.ReverseTransform( _dragStartPt, out x1, out y1, out yy1 );
				_dragPane.ReverseTransform( mousePtF, out x2, out y2, out yy2 );

				ZoomStatePush( _dragPane );
				//ZoomState oldState = _dragPane.ZoomStack.Push( _dragPane,
				//			ZoomState.StateType.Zoom );

				if ( _isEnableHZoom )
				{
					_dragPane.XAxis._scale._min = Math.Min( x1, x2 );
					_dragPane.XAxis._scale._minAuto = false;

					_dragPane.XAxis._scale._max = Math.Max( x1, x2 );
					_dragPane.XAxis._scale._maxAuto = false;
				}
				if ( _isEnableVZoom )
				{
					for ( int i = 0; i < y1.Length; i++ )
					{
						_dragPane.YAxisList[i]._scale._min = Math.Min( y1[i], y2[i] );
						_dragPane.YAxisList[i]._scale._max = Math.Max( y1[i], y2[i] );
						_dragPane.YAxisList[i]._scale._minAuto = false;
						_dragPane.YAxisList[i]._scale._maxAuto = false;
					}
					for ( int i = 0; i < yy1.Length; i++ )
					{
						_dragPane.Y2AxisList[i]._scale._min = Math.Min( yy1[i], yy2[i] );
						_dragPane.Y2AxisList[i]._scale._max = Math.Max( yy1[i], yy2[i] );
						_dragPane.Y2AxisList[i]._scale._minAuto = false;
						_dragPane.Y2AxisList[i]._scale._maxAuto = false;
					}
				}

				this.SetScroll( this.hScrollBar1, _dragPane.XAxis, _xScrollRange.Min, _xScrollRange.Max );
				this.SetScroll( this.vScrollBar1, _dragPane.YAxis, _yScrollRangeList[0].Min,
					_yScrollRangeList[0].Max );

				ApplyToAllPanes( _dragPane );

				// Provide Callback to notify the user of zoom events
				if ( this.ZoomEvent != null )
					this.ZoomEvent( this, _zoomState, //oldState,
						new ZoomState( _dragPane, ZoomState.StateType.Zoom ) );

				using ( Graphics g = this.CreateGraphics() )
				{
					// always AxisChange() the dragPane
					_dragPane.AxisChange( g );

					foreach ( GraphPane pane in _masterPane._paneList )
					{
						if ( pane != _dragPane && ( _isSynchronizeXAxes || _isSynchronizeYAxes ) )
								pane.AxisChange( g );
					}
				}
			}

			Refresh();
		}

		private void HandleZoomCancel()
		{
			if ( _isZooming )
			{
				_isZooming = false;
				Refresh();

				ZoomStateClear();
			}
		}

		private PointF BoundPointToRect( Point mousePt, RectangleF rect )
		{
			PointF newPt = new PointF( mousePt.X, mousePt.Y );

			if ( mousePt.X < rect.X ) newPt.X = rect.X;
			if ( mousePt.X > rect.Right ) newPt.X = rect.Right;
			if ( mousePt.Y < rect.Y ) newPt.Y = rect.Y;
			if ( mousePt.Y > rect.Bottom ) newPt.Y = rect.Bottom;

			return newPt;
		}

		private Rectangle CalcScreenRect( Point mousePt1, Point mousePt2 )
		{
			Point screenPt = PointToScreen( mousePt1 );
			Size size = new Size( mousePt2.X - mousePt1.X, mousePt2.Y - mousePt1.Y );
			Rectangle rect = new Rectangle( screenPt, size );

			if ( _isZooming )
			{
				Rectangle chartRect = Rectangle.Round( _dragPane.Chart._rect );

				Point chartPt = PointToScreen( chartRect.Location );

				if ( !_isEnableVZoom )
				{
					rect.Y = chartPt.Y;
					rect.Height = chartRect.Height + 1;
				}
				else if ( !_isEnableHZoom )
				{
					rect.X = chartPt.X;
					rect.Width = chartRect.Width + 1;
				}
			}

			return rect;
		}

	#endregion

	#region Selection Events

		// Revision: JCarpenter 10/06
		/// <summary>
		/// Perform selection on curves within the drag pane, or under the mouse click.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void HandleSelectionFinish( object sender, MouseEventArgs e )
		{
			if ( e.Button != _selectButtons )
			{
				Refresh();
				return;
			}

			PointF mousePtF = BoundPointToRect( new Point( e.X, e.Y ), _dragPane.Chart._rect );

			PointF mousePt = BoundPointToRect( new Point( e.X, e.Y ), _dragPane.Rect );

			Point curPt = ( (Control)sender ).PointToScreen( Point.Round( mousePt ) );

			// Only accept a drag if it covers at least 5 pixels in each direction
			//Point curPt = ( (Control)sender ).PointToScreen( Point.Round( mousePt ) );
			if ( ( Math.Abs( mousePtF.X - _dragStartPt.X ) > 4 ) &&
					  ( Math.Abs( mousePtF.Y - _dragStartPt.Y ) > 4 ) )
			{

				#region New Code to Select on Rubber Band

				double x1, x2;
				double[] y1, y2, yy1, yy2;
				PointF startPoint = ( (Control)sender ).PointToClient( new Point( Convert.ToInt32( this._dragPane.Rect.X ), Convert.ToInt32( this._dragPane.Rect.Y ) ) );

				_dragPane.ReverseTransform( _dragStartPt, out x1, out y1, out yy1 );
				_dragPane.ReverseTransform( mousePtF, out x2, out y2, out yy2 );

				CurveList objects = new CurveList();

				double left = Math.Min( x1, x2 );
				double right = Math.Max( x1, x2 );

				double top = 0;
				double bottom = 0;

				for ( int i = 0; i < y1.Length; i++ )
				{
					bottom = Math.Min( y1[i], y2[i] );
					top = Math.Max( y1[i], y2[i] );
				}

				for ( int i = 0; i < yy1.Length; i++ )
				{
					bottom = Math.Min( bottom, yy2[i] );
					bottom = Math.Min( yy1[i], bottom );
					top = Math.Max( top, yy2[i] );
					top = Math.Max( yy1[i], top );
				}

				double w = right - left;
				double h = bottom - top;

				RectangleF rF = new RectangleF( (float)left, (float)top, (float)w, (float)h );

				_dragPane.FindContainedObjects( rF, this.CreateGraphics(), out objects );

				if ( Control.ModifierKeys == _selectAppendModifierKeys )
					_selection.AddToSelection( _masterPane, objects );
				else
					_selection.Select( _masterPane, objects );
				//				this.Select( objects );

				//Graphics g = this.CreateGraphics();
				//this._dragPane.AxisChange( g );
				//g.Dispose();

				#endregion
			}
			else   // It's a single-select
			{
				#region New Code to Single Select

				//Point mousePt = new Point( e.X, e.Y );

				int iPt;
				GraphPane pane;
				object nearestObj;

				using ( Graphics g = this.CreateGraphics() )
				{
					if ( this.MasterPane.FindNearestPaneObject( mousePt, g, out pane,
								out nearestObj, out iPt ) )
					{
						if ( nearestObj is CurveItem && iPt >= 0 )
						{
							if ( Control.ModifierKeys == _selectAppendModifierKeys )
								_selection.AddToSelection( _masterPane, nearestObj as CurveItem );
							else
								_selection.Select( _masterPane, nearestObj as CurveItem );
						}
						else
							_selection.ClearSelection( _masterPane );

						Refresh();
					}
					else
					{
						_selection.ClearSelection( _masterPane );
					}
				}
				#endregion New Code to Single Select
			}

			using ( Graphics g = this.CreateGraphics() )
			{
				// always AxisChange() the dragPane
				_dragPane.AxisChange( g );

				foreach ( GraphPane pane in _masterPane._paneList )
				{
					if ( pane != _dragPane && ( _isSynchronizeXAxes || _isSynchronizeYAxes ) )
						pane.AxisChange( g );
				}
			}

			Refresh();
		}

		private void HandleSelectionCancel()
		{
			_isSelecting = false;

			_selection.ClearSelection( _masterPane );

			Refresh();
		}

	#endregion

	#region ScrollBars

		private void vScrollBar1_Scroll( object sender, ScrollEventArgs e )
		{
			if ( this.GraphPane != null )
			{
/*
				ScrollBar scrollBar = sender as ScrollBar;
				bool clearZoomState = false;
				//ZoomState curState = new ZoomState( this.GraphPane, ZoomState.StateType.Scroll );
				if ( _zoomState == null ) //&& !scrollBar.Capture )
				{
					clearZoomState = true;
					ZoomStateSave( this.GraphPane, ZoomState.StateType.Scroll );
				}
*/
				for ( int i = 0; i < this.GraphPane.YAxisList.Count; i++ )
				{
					ScrollRange scroll = _yScrollRangeList[i];
					if ( scroll.IsScrollable )
					{
						Axis axis = this.GraphPane.YAxisList[i];
						HandleScroll( axis, e.NewValue, scroll.Min, scroll.Max, vScrollBar1.LargeChange,
										!axis.Scale.IsReverse );
					}
				}

				for ( int i = 0; i < this.GraphPane.Y2AxisList.Count; i++ )
				{
					ScrollRange scroll = _y2ScrollRangeList[i];
					if ( scroll.IsScrollable )
					{
						Axis axis = this.GraphPane.Y2AxisList[i];
						HandleScroll( axis, e.NewValue, scroll.Min, scroll.Max, vScrollBar1.LargeChange,
										!axis.Scale.IsReverse );
					}
				}

				ApplyToAllPanes( this.GraphPane );

				if ( _zoomState != null ) // && ! clearZoomState && scrollBar.Capture )
				{
					// Provide Callback to notify the user of scroll events
					if ( this.ScrollProgressEvent != null )
						this.ScrollProgressEvent( this, vScrollBar1, _zoomState,
									new ZoomState( this.GraphPane, ZoomState.StateType.Scroll ) );
				}
/*				else if ( clearZoomState && _zoomState != null && // !scrollBar.Capture &&
								_zoomState.IsChanged( this.GraphPane ) )
				{
					//this.GraphPane.ZoomStack.Push( _zoomState );
					ZoomStatePush( this.GraphPane );

					// Provide Callback to notify the user of pan events
					if ( this.ScrollDoneEvent != null )
						this.ScrollDoneEvent( this, scrollBar, _zoomState,
									new ZoomState( this.GraphPane, ZoomState.StateType.Scroll ) );
				}

				if ( clearZoomState )
					_zoomState = null;
*/
			}
		}

		private void ApplyToAllPanes( GraphPane primaryPane )
		{
			foreach ( GraphPane pane in _masterPane._paneList )
			{
				if ( pane != primaryPane )
				{
					if ( _isSynchronizeXAxes )
						Synchronize( primaryPane.XAxis, pane.XAxis );
					if ( _isSynchronizeYAxes )
						Synchronize( primaryPane.YAxis, pane.YAxis );
				}
			}
		}

		private void Synchronize( Axis source, Axis dest )
		{
			dest._scale._min = source._scale._min;
			dest._scale._max = source._scale._max;
			dest._scale._majorStep = source._scale._majorStep;
			dest._scale._minorStep = source._scale._minorStep;
			dest._scale._minAuto = source._scale._minAuto;
			dest._scale._maxAuto = source._scale._maxAuto;
			dest._scale._majorStepAuto = source._scale._majorStepAuto;
			dest._scale._minorStepAuto = source._scale._minorStepAuto;
		}

		private void hScrollBar1_Scroll( object sender, ScrollEventArgs e )
		{
			if ( this.GraphPane != null )
			{
				HandleScroll( this.GraphPane.XAxis, e.NewValue, _xScrollRange.Min, _xScrollRange.Max,
							hScrollBar1.LargeChange, this.GraphPane.XAxis.Scale.IsReverse );
			}

			ApplyToAllPanes( this.GraphPane );

			if ( _zoomState != null && this.GraphPane != null )
			{
				// Provide Callback to notify the user of pan events
				if ( this.ScrollProgressEvent != null )
					this.ScrollProgressEvent( this, hScrollBar1, _zoomState,
								new ZoomState( this.GraphPane, ZoomState.StateType.Scroll ) );
			}
		}

/*
		/// <summary>
		/// Use the MouseCaptureChanged as an indicator for the start and end of a scrolling operation
		/// </summary>
		private void ScrollBarMouseCaptureChanged( object sender, EventArgs e )
		{
			ScrollBar scrollBar = sender as ScrollBar;
			if ( scrollBar != null )
			{
				// If this is the start of a new scroll, then Capture will be true
				if ( scrollBar.Capture )
				{
					// save the original zoomstate
					//_zoomState = new ZoomState( this.GraphPane, ZoomState.StateType.Scroll );
					ZoomStateSave( this.GraphPane, ZoomState.StateType.Scroll );
				}
				else
				{
					// push the prior saved zoomstate, since the scale ranges have already been changed on
					// the fly during the scrolling operation
					if ( _zoomState != null && _zoomState.IsChanged( this.GraphPane ) )
					{
						//this.GraphPane.ZoomStack.Push( _zoomState );
						ZoomStatePush( this.GraphPane );

						// Provide Callback to notify the user of pan events
						if ( this.ScrollDoneEvent != null )
							this.ScrollDoneEvent( this, scrollBar, _zoomState,
										new ZoomState( this.GraphPane, ZoomState.StateType.Scroll ) );

						_zoomState = null;
					}
				}
			}
		}
*/
		private void HandleScroll( Axis axis, int newValue, double scrollMin, double scrollMax,
									int largeChange, bool reverse )
		{
			if ( axis != null )
			{
				if ( scrollMin > axis._scale._min )
					scrollMin = axis._scale._min;
				if ( scrollMax < axis._scale._max )
					scrollMax = axis._scale._max;

				int span = _ScrollControlSpan - largeChange;
				if ( span <= 0 )
					return;

				if ( reverse )
					newValue = span - newValue;

				Scale scale = axis._scale;

				double delta = scale._maxLinearized - scale._minLinearized;
				double scrollMin2 = scale.Linearize( scrollMax ) - delta;
				scrollMin = scale.Linearize( scrollMin );
				//scrollMax = scale.Linearize( scrollMax );
				double val = scrollMin + (double)newValue / (double)span *
						( scrollMin2 - scrollMin );
				scale._minLinearized = val;
				scale._maxLinearized = val + delta;
/*
				if ( axis.Scale.IsLog )
				{
					double ratio = axis._scale._max / axis._scale._min;
					double scrollMin2 = scrollMax / ratio;

					double val = scrollMin * Math.Exp( (double)newValue / (double)span *
								( Math.Log( scrollMin2 ) - Math.Log( scrollMin ) ) );
					axis._scale._min = val;
					axis._scale._max = val * ratio;
				}
				else
				{
					double delta = axis._scale._max - axis._scale._min;
					double scrollMin2 = scrollMax - delta;

					double val = scrollMin + (double)newValue / (double)span *
								( scrollMin2 - scrollMin );
					axis._scale._min = val;
					axis._scale._max = val + delta;
				}
*/
				this.Invalidate();
			}
		}

		/// <summary>
		/// Sets the value of the scroll range properties (see <see cref="ScrollMinX" />,
		/// <see cref="ScrollMaxX" />, <see cref="YScrollRangeList" />, and 
		/// <see cref="Y2ScrollRangeList" /> based on the actual range of the data for
		/// each corresponding <see cref="Axis" />.
		/// </summary>
		/// <remarks>
		/// This method is called automatically by <see cref="AxisChange" /> if
		/// <see cref="IsAutoScrollRange" />
		/// is true.  Note that this will not be called if you call AxisChange directly from the
		/// <see cref="GraphPane" />.  For example, zedGraphControl1.AxisChange() works properly, but
		/// zedGraphControl1.GraphPane.AxisChange() does not.</remarks>
		public void SetScrollRangeFromData()
		{
			if ( this.GraphPane != null )
			{
				double grace = CalcScrollGrace( this.GraphPane.XAxis.Scale._rangeMin,
							this.GraphPane.XAxis.Scale._rangeMax );

				_xScrollRange.Min = this.GraphPane.XAxis.Scale._rangeMin - grace;
				_xScrollRange.Max = this.GraphPane.XAxis.Scale._rangeMax + grace;
				_xScrollRange.IsScrollable = true;

				for ( int i = 0; i < this.GraphPane.YAxisList.Count; i++ )
				{
					Axis axis = this.GraphPane.YAxisList[i];
					grace = CalcScrollGrace( axis.Scale._rangeMin, axis.Scale._rangeMax );
					ScrollRange range = new ScrollRange( axis.Scale._rangeMin - grace,
						axis.Scale._rangeMax + grace, _yScrollRangeList[i].IsScrollable );

					if ( i >= _yScrollRangeList.Count )
						_yScrollRangeList.Add( range );
					else
						_yScrollRangeList[i] = range;
				}

				for ( int i = 0; i < this.GraphPane.Y2AxisList.Count; i++ )
				{
					Axis axis = this.GraphPane.Y2AxisList[i];
					grace = CalcScrollGrace( axis.Scale._rangeMin, axis.Scale._rangeMax );
					ScrollRange range = new ScrollRange( axis.Scale._rangeMin - grace,
							axis.Scale._rangeMax + grace, _y2ScrollRangeList[i].IsScrollable );

					if ( i >= _y2ScrollRangeList.Count )
						_y2ScrollRangeList.Add( range );
					else
						_y2ScrollRangeList[i] = range;
				}

				//this.GraphPane.CurveList.GetRange( out scrollMinX, out scrollMaxX,
				//		out scrollMinY, out scrollMaxY, out scrollMinY2, out scrollMaxY2, false, false,
				//		this.GraphPane );
			}
		}

		private double CalcScrollGrace( double min, double max )
		{
			if ( Math.Abs( max - min ) < 1e-30 )
			{
				if ( Math.Abs( max ) < 1e-30 )
					return _scrollGrace;
				else
					return max * _scrollGrace;
			}
			else
				return ( max - min ) * _scrollGrace;
		}

		private void SetScroll( ScrollBar scrollBar, Axis axis, double scrollMin, double scrollMax )
		{
			if ( scrollBar != null && axis != null )
			{
				scrollBar.Minimum = 0;
				scrollBar.Maximum = _ScrollControlSpan - 1;

				if ( scrollMin > axis._scale._min )
					scrollMin = axis._scale._min;
				if ( scrollMax < axis._scale._max )
					scrollMax = axis._scale._max;

				int val = 0;

				Scale scale = axis._scale;
				double minLinearized = scale._minLinearized;
				double maxLinearized = scale._maxLinearized;
				scrollMin = scale.Linearize( scrollMin );
				scrollMax = scale.Linearize( scrollMax );

				double scrollMin2 = scrollMax - ( maxLinearized - minLinearized );
				/*
				if ( axis.Scale.IsLog )
					scrollMin2 = scrollMax / ( axis._scale._max / axis._scale._min );
				else
					scrollMin2 = scrollMax - ( axis._scale._max - axis._scale._min );
				*/
				if ( scrollMin >= scrollMin2 )
				{
					//scrollBar.Visible = false;
					scrollBar.Enabled = false;
					scrollBar.Value = 0;
				}
				else
				{
					double ratio = ( maxLinearized - minLinearized ) / ( scrollMax - scrollMin );

					/*
					if ( axis.Scale.IsLog )
						ratio = ( Math.Log( axis._scale._max ) - Math.Log( axis._scale._min ) ) /
									( Math.Log( scrollMax ) - Math.Log( scrollMin ) );
					else
						ratio = ( axis._scale._max - axis._scale._min ) / ( scrollMax - scrollMin );
					*/

					int largeChange = (int)( ratio * _ScrollControlSpan + 0.5 );
					if ( largeChange < 1 )
						largeChange = 1;
					scrollBar.LargeChange = largeChange;

					int smallChange = largeChange / _ScrollSmallRatio;
					if ( smallChange < 1 )
						smallChange = 1;
					scrollBar.SmallChange = smallChange;

					int span = _ScrollControlSpan - largeChange;

					val = (int)( ( minLinearized - scrollMin ) / ( scrollMin2 - scrollMin ) *
									span + 0.5 );
					/*
					if ( axis.Scale.IsLog )
						val = (int)( ( Math.Log( axis._scale._min ) - Math.Log( scrollMin ) ) /
								( Math.Log( scrollMin2 ) - Math.Log( scrollMin ) ) * span + 0.5 );
					else
						val = (int)( ( axis._scale._min - scrollMin ) / ( scrollMin2 - scrollMin ) *
								span + 0.5 );
					*/
					if ( val < 0 )
						val = 0;
					else if ( val > span )
						val = span;

					//if ( ( axis is XAxis && axis.IsReverse ) || ( ( ! axis is XAxis ) && ! axis.IsReverse ) )
					if ( ( axis is XAxis ) == axis.Scale.IsReverse )
						val = span - val;

					if ( val < scrollBar.Minimum )
						val = scrollBar.Minimum;
					if ( val > scrollBar.Maximum )
						val = scrollBar.Maximum;

					scrollBar.Value = val;
					scrollBar.Enabled = true;
					//scrollBar.Visible = true;
				}
			}
		}

	#endregion

	#region ContextMenu

		// Revision: JCarpenter 10/06
		/// <summary>
		/// Public enumeration that specifies the type of 
		/// object present at the Context Menu's mouse location
		/// </summary>
		public enum ContextMenuObjectState
		{
			/// <summary>
			/// The object is an Inactive Curve Item at the Context Menu's mouse position
			/// </summary>
			InactiveSelection,
			/// <summary>
			/// The object is an active Curve Item at the Context Menu's mouse position
			/// </summary>
			ActiveSelection,
			/// <summary>
			/// There is no selectable object present at the Context Menu's mouse position
			/// </summary>
			Background
		}

		//Revision: JCarpenter 10/06
		/// <summary>
		/// Find the object currently under the mouse cursor, and return its state.
		/// </summary>
		private ContextMenuObjectState GetObjectState()
		{
			ContextMenuObjectState objState = ContextMenuObjectState.Background;

			// Determine object state
			Point mousePt = this.PointToClient( Control.MousePosition );
			int iPt;
			GraphPane pane;
			object nearestObj;

			using ( Graphics g = this.CreateGraphics() )
			{
				if ( this.MasterPane.FindNearestPaneObject( mousePt, g, out pane,
						out nearestObj, out iPt ) )
				{
					CurveItem item = nearestObj as CurveItem;

					if ( item != null && iPt >= 0 )
					{
						if ( item.IsSelected )
							objState = ContextMenuObjectState.ActiveSelection;
						else
							objState = ContextMenuObjectState.InactiveSelection;
					}
				}
			}

			return objState;
		}

		/// <summary>
		/// protected method to handle the popup context menu in the <see cref="ZedGraphControl"/>.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		protected void ContextMenu_Popup( object sender, System.EventArgs e )
		{
			//Revision: JCarpenter 10/06
			ContextMenuObjectState objState = GetObjectState();

			if ( this.contextMenu != null && this._masterPane != null )
			{
				contextMenu.MenuItems.Clear();
				this._isZooming = false;
				this._isPanning = false;
				Cursor.Current = Cursors.Default;

				this._menuClickPt = this.PointToClient( Control.MousePosition );
				GraphPane pane = this._masterPane.FindPane( this._menuClickPt );

				if ( this._isShowContextMenu )
				{
					MenuItem menuItem;
					int index = 0;

					menuItem = new MenuItem();
					menuItem.Index = index++;
					//menuItem.Tag = "copy";
					string menuStr = this._resourceManager.GetString( "copy" );
					menuItem.Text = menuStr;
					this.contextMenu.MenuItems.Add( menuItem );
					menuItem.Click += new System.EventHandler( this.MenuClick_Copy );

					menuItem = new MenuItem();
					menuItem.Index = index++;
					//menuItem.Tag = "save_as";
					menuStr = _resourceManager.GetString( "save_as" );
					menuItem.Text = menuStr;
					this.contextMenu.MenuItems.Add( menuItem );
					menuItem.Click += new System.EventHandler( this.MenuClick_SaveAs );

					menuItem = new MenuItem();
					menuItem.Index = index++;
					//menuItem.Tag = "page_setup";
					menuStr = _resourceManager.GetString( "page_setup" );
					menuItem.Text = menuStr;
					this.contextMenu.MenuItems.Add( menuItem );
					menuItem.Click += new System.EventHandler( this.MenuClick_PageSetup );

					menuItem = new MenuItem();
					menuItem.Index = index++;
					//menuItem.Tag = "print";
					menuStr = _resourceManager.GetString( "print" );
					menuItem.Text = menuStr;
					this.contextMenu.MenuItems.Add( menuItem );
					menuItem.Click += new System.EventHandler( this.MenuClick_Print );

					menuItem = new MenuItem();
					menuItem.Index = index++;
					//menuItem.Tag = "show_val";
					menuStr = _resourceManager.GetString( "show_val" );
					menuItem.Text = menuStr;
					menuItem.Checked = this.IsShowPointValues;
					this.contextMenu.MenuItems.Add( menuItem );
					menuItem.Click += new System.EventHandler( this.MenuClick_ShowValues );

					menuItem = new MenuItem();
					menuItem.Index = index++;
					//menuItem.Tag = "unzoom";

					if ( pane == null || pane.ZoomStack.IsEmpty )
						menuStr = _resourceManager.GetString( "unzoom" );
					else
					{
						switch ( pane.ZoomStack.Top.Type )
						{
							case ZoomState.StateType.Zoom:
							case ZoomState.StateType.WheelZoom:
								menuStr = _resourceManager.GetString( "unzoom" );
								break;
							case ZoomState.StateType.Pan:
								menuStr = _resourceManager.GetString( "unpan" );
								break;
							case ZoomState.StateType.Scroll:
								menuStr = _resourceManager.GetString( "unscroll" );
								break;
						}
					}

					//menuItem.Text = "Un-" + ( ( pane == null || pane.zoomStack.IsEmpty ) ?
					//	"Zoom" : pane.zoomStack.Top.TypeString );
					menuItem.Text = menuStr;
					this.contextMenu.MenuItems.Add( menuItem );
					menuItem.Click += new EventHandler( this.MenuClick_ZoomOut );
					if ( pane == null || pane.ZoomStack.IsEmpty )
						menuItem.Enabled = false;

					menuItem = new MenuItem();
					menuItem.Index = index++;
					//menuItem.Tag = "undo_all";
					menuStr = _resourceManager.GetString( "undo_all" );
					menuItem.Text = menuStr;
					this.contextMenu.MenuItems.Add( menuItem );
					menuItem.Click += new EventHandler( this.MenuClick_ZoomOutAll );
					if ( pane == null || pane.ZoomStack.IsEmpty )
						menuItem.Enabled = false;

					menuItem = new MenuItem();
					menuItem.Index = index++;
					//menuItem.Tag = "set_default";
					menuStr = _resourceManager.GetString( "set_default" );
					menuItem.Text = menuStr;
					this.contextMenu.MenuItems.Add( menuItem );
					menuItem.Click += new EventHandler( this.MenuClick_RestoreScale );
					if ( pane == null )
						menuItem.Enabled = false;

					// Provide Callback for User to edit the context menu
					//Revision: JCarpenter 10/06 - add ContextMenuObjectState objState
					if ( this.ContextMenuBuilder != null )
						this.ContextMenuBuilder( this, this.contextMenu, this._menuClickPt, objState );
				}
			}
		}

		/// <summary>
		/// Handler for the "Copy" context menu item.  Copies the current image to a bitmap on the
		/// clipboard.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		protected void MenuClick_Copy( System.Object sender, System.EventArgs e )
		{
			Copy( _isShowCopyMessage );
		}

		/// <summary>
		/// Handler for the "Copy" context menu item.  Copies the current image to a bitmap on the
		/// clipboard.
		/// </summary>
		/// <param name="isShowMessage">boolean value that determines whether or not a prompt will be
		/// displayed.  true to show a message of "Image Copied to ClipBoard".</param>
		public void Copy( bool isShowMessage )
		{
			if ( _masterPane != null )
			{
				//Clipboard.SetDataObject( _masterPane.GetImage(), true );

				// Threaded copy mode to avoid crash with MTA
				// Contributed by Dave Moor
				Thread ct = new Thread( new ThreadStart( this.ClipboardCopyThread ) );
				//ct.ApartmentState = ApartmentState.STA;
				ct.ApartmentState = ApartmentState.STA;
				//ct.SetApartmentState( ApartmentState.STA );
				ct.Start();
				ct.Join();

				if ( isShowMessage )
				{
					string str = _resourceManager.GetString( "copied_to_clip" );
					//MessageBox.Show( "Image Copied to ClipBoard" );
					MessageBox.Show( str );
				}
			}
		}

		/// <summary>
		/// A threaded version of the copy method to avoid crash with MTA
		/// </summary>
		private void ClipboardCopyThread()
		{
			Clipboard.SetDataObject( _masterPane.GetImage(), true );
		}

		/// <summary>
		/// Handler for the "Save Image As" context menu item.  Copies the current image to the selected
		/// file.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		protected void MenuClick_SaveAs( System.Object sender, System.EventArgs e )
		{
			SaveAs();
		}

		/// <summary>
		/// Handler for the "Save Image As" context menu item.  Copies the current image to the selected
		/// file in either the Emf (vector), or a variety of Bitmap formats.
		/// </summary>
		/// <remarks>
		/// Note that <see cref="SaveAsBitmap" /> and <see cref="SaveAsEmf" /> methods are provided
		/// which allow for Bitmap-only or Emf-only handling of the "Save As" context menu item.
		/// </remarks>
		public void SaveAs()
		{
			if ( _masterPane != null )
			{
				_saveFileDialog.Filter =
					"Emf Format (*.emf)|*.emf|" +
					"PNG Format (*.png)|*.png|" +
					"Gif Format (*.gif)|*.gif|" +
					"Jpeg Format (*.jpg)|*.jpg|" +
					"Tiff Format (*.tif)|*.tif|" +
					"Bmp Format (*.bmp)|*.bmp";

				if ( _saveFileDialog.ShowDialog() == DialogResult.OK )
				{
					Stream myStream = _saveFileDialog.OpenFile();
					if ( myStream != null )
					{
						if ( _saveFileDialog.FilterIndex == 1 )
						{
							myStream.Close();
							_masterPane.GetMetafile().Save( _saveFileDialog.FileName );
						}
						else
						{
							ImageFormat format = ImageFormat.Png;
							if ( _saveFileDialog.FilterIndex == 3 )
								format = ImageFormat.Gif;
							else if ( _saveFileDialog.FilterIndex == 4 )
								format = ImageFormat.Jpeg;
							else if ( _saveFileDialog.FilterIndex == 5 )
								format = ImageFormat.Tiff;
							else if ( _saveFileDialog.FilterIndex == 6 )
								format = ImageFormat.Bmp;

							_masterPane.GetImage().Save( myStream, format );
							myStream.Close();
						}
					}
				}
			}
		}

		/// <summary>
		/// Handler for the "Save Image As" context menu item.  Copies the current image to the selected
		/// Bitmap file.
		/// </summary>
		/// <remarks>
		/// Note that this handler saves as a bitmap only.  The default handler is
		/// <see cref="SaveAs" />, which allows for Bitmap or EMF formats
		/// </remarks>
		public void SaveAsBitmap()
		{
			if ( _masterPane != null )
			{
				_saveFileDialog.Filter =
					"PNG Format (*.png)|*.png|" +
					"Gif Format (*.gif)|*.gif|" +
					"Jpeg Format (*.jpg)|*.jpg|" +
					"Tiff Format (*.tif)|*.tif|" +
					"Bmp Format (*.bmp)|*.bmp";

				if ( _saveFileDialog.ShowDialog() == DialogResult.OK )
				{
					ImageFormat format = ImageFormat.Png;
					if ( _saveFileDialog.FilterIndex == 2 )
						format = ImageFormat.Gif;
					else if ( _saveFileDialog.FilterIndex == 3 )
						format = ImageFormat.Jpeg;
					else if ( _saveFileDialog.FilterIndex == 4 )
						format = ImageFormat.Tiff;
					else if ( _saveFileDialog.FilterIndex == 5 )
						format = ImageFormat.Bmp;

					Stream myStream = _saveFileDialog.OpenFile();
					if ( myStream != null )
					{
						_masterPane.GetImage().Save( myStream, format );
						myStream.Close();
					}
				}
			}
		}

		/// <summary>
		/// Handler for the "Save Image As" context menu item.  Copies the current image to the selected
		/// Emf format file.
		/// </summary>
		/// <remarks>
		/// Note that this handler saves as an Emf format only.  The default handler is
		/// <see cref="SaveAs" />, which allows for Bitmap or EMF formats.
		/// </remarks>
		public void SaveAsEmf()
		{
			if ( _masterPane != null )
			{
				_saveFileDialog.Filter = "Emf Format (*.emf)|*.emf";

				if ( _saveFileDialog.ShowDialog() == DialogResult.OK )
				{
					Stream myStream = _saveFileDialog.OpenFile();
					if ( myStream != null )
					{
						myStream.Close();
						_masterPane.GetMetafile().Save( _saveFileDialog.FileName );
					}
				}
			}
		}

		/// <summary>
		/// Handler for the "Show Values" context menu item.  Toggles the <see cref="IsShowPointValues"/>
		/// property, which activates the point value tooltips.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		protected void MenuClick_ShowValues( object sender, System.EventArgs e )
		{
			this.IsShowPointValues = !( (MenuItem)sender ).Checked;
		}

		/// <summary>
		/// Handler for the "Set Scale to Default" context menu item.  Sets the scale ranging to
		/// full auto mode for all axes.
		/// </summary>
		/// <remarks>
		/// This method differs from the <see cref="ZoomOutAll" /> method in that it sets the scales
		/// to full auto mode.  The <see cref="ZoomOutAll" /> method sets the scales to their initial
		/// setting prior to any user actions (which may or may not be full auto mode).
		/// </remarks>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		protected void MenuClick_RestoreScale( object sender, EventArgs e )
		{
			if ( _masterPane != null )
			{
				GraphPane pane = _masterPane.FindPane( _menuClickPt );
				RestoreScale( pane );
			}
		}

		/// <summary>
		/// Handler for the "Set Scale to Default" context menu item.  Sets the scale ranging to
		/// full auto mode for all axes.
		/// </summary>
		/// <remarks>
		/// This method differs from the <see cref="ZoomOutAll" /> method in that it sets the scales
		/// to full auto mode.  The <see cref="ZoomOutAll" /> method sets the scales to their initial
		/// setting prior to any user actions (which may or may not be full auto mode).
		/// </remarks>
		/// <param name="primaryPane">The <see cref="GraphPane" /> object which is to have the
		/// scale restored</param>
		public void RestoreScale( GraphPane primaryPane )
		{
			if ( primaryPane != null )
			{
				//Go ahead and save the old zoomstates, which provides an "undo"-like capability
				//ZoomState oldState = primaryPane.ZoomStack.Push( primaryPane, ZoomState.StateType.Zoom );
				ZoomState oldState = new ZoomState( primaryPane, ZoomState.StateType.Zoom );

				using ( Graphics g = this.CreateGraphics() )
				{
					if ( _isSynchronizeXAxes || _isSynchronizeYAxes )
					{
						foreach ( GraphPane pane in _masterPane._paneList )
						{
							pane.ZoomStack.Push( pane, ZoomState.StateType.Zoom );
							ResetAutoScale( pane, g );
						}
					}
					else
					{
						primaryPane.ZoomStack.Push( primaryPane, ZoomState.StateType.Zoom );
						ResetAutoScale( primaryPane, g );
					}

					// Provide Callback to notify the user of zoom events
					if ( this.ZoomEvent != null )
						this.ZoomEvent( this, oldState, new ZoomState( primaryPane, ZoomState.StateType.Zoom ) );

					//g.Dispose();
				}
				Refresh();
			}
		}

		private void ResetAutoScale( GraphPane pane, Graphics g )
		{
			pane.XAxis.ResetAutoScale( pane, g );
			foreach ( YAxis axis in pane.YAxisList )
				axis.ResetAutoScale( pane, g );
			foreach ( Y2Axis axis in pane.Y2AxisList )
				axis.ResetAutoScale( pane, g );
		}

/*
		public void RestoreScale( GraphPane primaryPane )
		{
			if ( primaryPane != null )
			{
				Graphics g = this.CreateGraphics();
				ZoomState oldState = new ZoomState( primaryPane, ZoomState.StateType.Zoom );
				//ZoomState newState = null;

				if ( _isSynchronizeXAxes || _isSynchronizeYAxes )
				{
					foreach ( GraphPane pane in _masterPane._paneList )
					{
						if ( pane == primaryPane )
						{
							pane.XAxis.ResetAutoScale( pane, g );
							foreach ( YAxis axis in pane.YAxisList )
								axis.ResetAutoScale( pane, g );
							foreach ( Y2Axis axis in pane.Y2AxisList )
								axis.ResetAutoScale( pane, g );
						}
					}
				}
				else
				{
					primaryPane.XAxis.ResetAutoScale( primaryPane, g );
					foreach ( YAxis axis in primaryPane.YAxisList )
						axis.ResetAutoScale( primaryPane, g );
					foreach ( Y2Axis axis in primaryPane.Y2AxisList )
						axis.ResetAutoScale( primaryPane, g );
				}

				// Provide Callback to notify the user of zoom events
				if ( this.ZoomEvent != null )
					this.ZoomEvent( this, oldState, new ZoomState( primaryPane, ZoomState.StateType.Zoom ) );

				g.Dispose();
				Refresh();
			}
		}
*/
/*
		public void ZoomOutAll( GraphPane primaryPane )
		{
			if ( primaryPane != null && !primaryPane.ZoomStack.IsEmpty )
			{
				ZoomState.StateType type = primaryPane.ZoomStack.Top.Type;

				ZoomState oldState = new ZoomState( primaryPane, type );
				//ZoomState newState = pane.ZoomStack.PopAll( pane );
				ZoomState newState = null;
				if ( _isSynchronizeXAxes || _isSynchronizeYAxes )
				{
					foreach ( GraphPane pane in _masterPane._paneList )
					{
						ZoomState state = pane.ZoomStack.PopAll( pane );
						if ( pane == primaryPane )
							newState = state;
					}
				}
				else
					newState = primaryPane.ZoomStack.PopAll( primaryPane );

				// Provide Callback to notify the user of zoom events
				if ( this.ZoomEvent != null )
					this.ZoomEvent( this, oldState, newState );

				Refresh();
			}
		}

*/

		/// <summary>
		/// Handler for the "UnZoom/UnPan" context menu item.  Restores the scale ranges to the values
		/// before the last zoom or pan operation.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		protected void MenuClick_ZoomOut( System.Object sender, System.EventArgs e )
		{
			if ( _masterPane != null )
			{
				GraphPane pane = _masterPane.FindPane( _menuClickPt );
				ZoomOut( pane );
			}
		}

		/// <summary>
		/// Handler for the "UnZoom/UnPan" context menu item.  Restores the scale ranges to the values
		/// before the last zoom, pan, or scroll operation.
		/// </summary>
		/// <remarks>
		/// Triggers a <see cref="ZoomEvent" /> for any type of undo (including pan, scroll, zoom, and
		/// wheelzoom).  This method will affect all the
		/// <see cref="GraphPane" /> objects in the <see cref="MasterPane" /> if
		/// <see cref="IsSynchronizeXAxes" /> or <see cref="IsSynchronizeYAxes" /> is true.
		/// </remarks>
		/// <param name="primaryPane">The primary <see cref="GraphPane" /> object which is to be
		/// zoomed out</param>
		public void ZoomOut( GraphPane primaryPane )
		{
			if ( primaryPane != null && !primaryPane.ZoomStack.IsEmpty )
			{
				ZoomState.StateType type = primaryPane.ZoomStack.Top.Type;

				ZoomState oldState = new ZoomState( primaryPane, type );
				ZoomState newState = null;
				if ( _isSynchronizeXAxes || _isSynchronizeYAxes )
				{
					foreach ( GraphPane pane in _masterPane._paneList )
					{
						ZoomState state = pane.ZoomStack.Pop( pane );
						if ( pane == primaryPane )
							newState = state;
					}
				}
				else
					newState = primaryPane.ZoomStack.Pop( primaryPane );

				// Provide Callback to notify the user of zoom events
				if ( this.ZoomEvent != null )
					this.ZoomEvent( this, oldState, newState );

				Refresh();
			}
		}

		/// <summary>
		/// Handler for the "Undo All Zoom/Pan" context menu item.  Restores the scale ranges to the values
		/// before all zoom and pan operations
		/// </summary>
		/// <remarks>
		/// This method differs from the <see cref="RestoreScale" /> method in that it sets the scales
		/// to their initial setting prior to any user actions.  The <see cref="RestoreScale" /> method
		/// sets the scales to full auto mode (regardless of what the initial setting may have been).
		/// </remarks>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		protected void MenuClick_ZoomOutAll( System.Object sender, System.EventArgs e )
		{
			if ( _masterPane != null )
			{
				GraphPane pane = _masterPane.FindPane( _menuClickPt );
				ZoomOutAll( pane );
			}
		}

		/// <summary>
		/// Handler for the "Undo All Zoom/Pan" context menu item.  Restores the scale ranges to the values
		/// before all zoom and pan operations
		/// </summary>
		/// <remarks>
		/// This method differs from the <see cref="RestoreScale" /> method in that it sets the scales
		/// to their initial setting prior to any user actions.  The <see cref="RestoreScale" /> method
		/// sets the scales to full auto mode (regardless of what the initial setting may have been).
		/// </remarks>
		/// <param name="primaryPane">The <see cref="GraphPane" /> object which is to be zoomed out</param>
		public void ZoomOutAll( GraphPane primaryPane )
		{
			if ( primaryPane != null && !primaryPane.ZoomStack.IsEmpty )
			{
				ZoomState.StateType type = primaryPane.ZoomStack.Top.Type;

				ZoomState oldState = new ZoomState( primaryPane, type );
				//ZoomState newState = pane.ZoomStack.PopAll( pane );
				ZoomState newState = null;
				if ( _isSynchronizeXAxes || _isSynchronizeYAxes )
				{
					foreach ( GraphPane pane in _masterPane._paneList )
					{
						ZoomState state = pane.ZoomStack.PopAll( pane );
						if ( pane == primaryPane )
							newState = state;
					}
				}
				else
					newState = primaryPane.ZoomStack.PopAll( primaryPane );

				// Provide Callback to notify the user of zoom events
				if ( this.ZoomEvent != null )
					this.ZoomEvent( this, oldState, newState );

				Refresh();
			}
		}

	#endregion

	#region Printing

		/// <summary>
		/// Handler for the "Page Setup..." context menu item.   Displays a
		/// <see cref="PageSetupDialog" />.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		protected void MenuClick_PageSetup( object sender, EventArgs e )
		{
			DoPageSetup();
		}

		/// <summary>
		/// Handler for the "Print..." context menu item.   Displays a
		/// <see cref="PrintDialog" />.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		protected void MenuClick_Print( object sender, EventArgs e )
		{
			DoPrint();
		}

		/// <summary>
		/// Rendering method used by the print context menu items
		/// </summary>
		/// <param name="sender">The applicable <see cref="PrintDocument" />.</param>
		/// <param name="e">A <see cref="PrintPageEventArgs" /> instance providing
		/// page bounds, margins, and a Graphics instance for this printed output.
		/// </param>
		private void Graph_PrintPage( object sender, PrintPageEventArgs e )
		{
			PrintDocument pd = sender as PrintDocument;

			MasterPane mPane = this.MasterPane;
			bool[] isPenSave = new bool[mPane.PaneList.Count + 1];
			bool[] isFontSave = new bool[mPane.PaneList.Count + 1];
			isPenSave[0] = mPane.IsPenWidthScaled;
			isFontSave[0] = mPane.IsFontsScaled;
			for ( int i = 0; i < mPane.PaneList.Count; i++ )
			{
				isPenSave[i + 1] = mPane[i].IsPenWidthScaled;
				isFontSave[i + 1] = mPane[i].IsFontsScaled;
				if ( _isPrintScaleAll )
				{
					mPane[i].IsPenWidthScaled = true;
					mPane[i].IsFontsScaled = true;
				}
			}

			RectangleF saveRect = mPane.Rect;
			SizeF newSize = mPane.Rect.Size;
			if ( _isPrintFillPage && _isPrintKeepAspectRatio )
			{
				float xRatio = (float)e.MarginBounds.Width / (float)newSize.Width;
				float yRatio = (float)e.MarginBounds.Height / (float)newSize.Height;
				float ratio = Math.Min( xRatio, yRatio );

				newSize.Width *= ratio;
				newSize.Height *= ratio;
			}
			else if ( _isPrintFillPage )
				newSize = e.MarginBounds.Size;

			mPane.ReSize( e.Graphics, new RectangleF( e.MarginBounds.Left,
				e.MarginBounds.Top, newSize.Width, newSize.Height ) );
			mPane.Draw( e.Graphics );

			using ( Graphics g = this.CreateGraphics() )
			{
				mPane.ReSize( g, saveRect );
				//g.Dispose();
			}

			mPane.IsPenWidthScaled = isPenSave[0];
			mPane.IsFontsScaled = isFontSave[0];
			for ( int i = 0; i < mPane.PaneList.Count; i++ )
			{
				mPane[i].IsPenWidthScaled = isPenSave[i + 1];
				mPane[i].IsFontsScaled = isFontSave[i + 1];
			}
		}

		/// <summary>
		/// Gets or sets the <see cref="System.Drawing.Printing.PrintDocument" /> instance
		/// that is used for all of the context menu printing functions.
		/// </summary>
		public PrintDocument PrintDocument
		{
			get
			{
				// Add a try/catch pair since the users of the control can't catch this one
				try
				{
					if ( _pdSave == null )
					{
						_pdSave = new PrintDocument();
						_pdSave.PrintPage += new PrintPageEventHandler( Graph_PrintPage );
					}
				}
				catch ( Exception exception )
				{
					MessageBox.Show( exception.Message );
				}

				return _pdSave;
			}
			set { _pdSave = value; }
		}

		/// <summary>
		/// Display a <see cref="PageSetupDialog" /> to the user, allowing them to modify
		/// the print settings for this <see cref="ZedGraphControl" />.
		/// </summary>
		public void DoPageSetup()
		{
			PrintDocument pd = PrintDocument;

			// Add a try/catch pair since the users of the control can't catch this one
			try
			{
				if ( pd != null )
				{
					//pd.PrintPage += new PrintPageEventHandler( GraphPrintPage );
					PageSetupDialog setupDlg = new PageSetupDialog();
					setupDlg.Document = pd;

					if ( setupDlg.ShowDialog() == DialogResult.OK )
					{
						pd.PrinterSettings = setupDlg.PrinterSettings;
						pd.DefaultPageSettings = setupDlg.PageSettings;

						// BUG in PrintDocument!!!  Converts in/mm repeatedly
						// http://support.microsoft.com/?id=814355
						// from http://www.vbinfozine.com/tpagesetupdialog.shtml, by Palo Mraz
						//if ( System.Globalization.RegionInfo.CurrentRegion.IsMetric )
						//{
						//	setupDlg.Document.DefaultPageSettings.Margins = PrinterUnitConvert.Convert(
						//	setupDlg.Document.DefaultPageSettings.Margins,
						//	PrinterUnit.Display, PrinterUnit.TenthsOfAMillimeter );
						//}
					}
				}
			}

			catch ( Exception exception )
			{
				MessageBox.Show( exception.Message );
			}
		}

		/// <summary>
		/// Display a <see cref="PrintDialog" /> to the user, allowing them to select a
		/// printer and print the <see cref="MasterPane" /> contained in this
		/// <see cref="ZedGraphControl" />.
		/// </summary>
		public void DoPrint()
		{
			// Add a try/catch pair since the users of the control can't catch this one
			try
			{
				PrintDocument pd = PrintDocument;

				if ( pd != null )
				{
					//pd.PrintPage += new PrintPageEventHandler( Graph_PrintPage );
					PrintDialog pDlg = new PrintDialog();
					pDlg.Document = pd;
					if ( pDlg.ShowDialog() == DialogResult.OK )
						pd.Print();
				}
			}
			catch ( Exception exception )
			{
				MessageBox.Show( exception.Message );
			}

		}

		/// <summary>
		/// Display a <see cref="PrintPreviewDialog" />, allowing the user to preview and
		/// subsequently print the <see cref="MasterPane" /> contained in this
		/// <see cref="ZedGraphControl" />.
		/// </summary>
		public void DoPrintPreview()
		{
			// Add a try/catch pair since the users of the control can't catch this one
			try
			{
				PrintDocument pd = PrintDocument;

				if ( pd != null )
				{
					PrintPreviewDialog ppd = new PrintPreviewDialog();
					//pd.PrintPage += new PrintPageEventHandler( Graph_PrintPage );
					ppd.Document = pd;
					ppd.Show();
				}
			}
			catch ( Exception exception )
			{
				MessageBox.Show( exception.Message );
			}
		}

	#endregion

	#region Zoom States

		/// <summary>
		/// Save the current states of the GraphPanes to a separate collection.  Save a single
		/// (<see paramref="primaryPane" />) GraphPane if the panes are not synchronized
		/// (see <see cref="IsSynchronizeXAxes" /> and <see cref="IsSynchronizeYAxes" />),
		/// or save a list of states for all GraphPanes if the panes are synchronized.
		/// </summary>
		/// <param name="primaryPane">The primary GraphPane on which zoom/pan/scroll operations
		/// are taking place</param>
		/// <param name="type">The <see cref="ZoomState.StateType" /> that describes the
		/// current operation</param>
		/// <returns>The <see cref="ZoomState" /> that corresponds to the
		/// <see paramref="primaryPane" />.
		/// </returns>
		private ZoomState ZoomStateSave( GraphPane primaryPane, ZoomState.StateType type )
		{
			ZoomStateClear();

			if ( _isSynchronizeXAxes || _isSynchronizeYAxes )
			{
				foreach ( GraphPane pane in _masterPane._paneList )
				{
					ZoomState state = new ZoomState( pane, type );
					if ( pane == primaryPane )
						_zoomState = state;
					_zoomStateStack.Add( state );
				}
			}
			else
				_zoomState = new ZoomState( primaryPane, type );

			return _zoomState;
		}

		/// <summary>
		/// Restore the states of the GraphPanes to a previously saved condition (via
		/// <see cref="ZoomStateSave" />.  This is essentially an "undo" for live
		/// pan and scroll actions.  Restores a single
		/// (<see paramref="primaryPane" />) GraphPane if the panes are not synchronized
		/// (see <see cref="IsSynchronizeXAxes" /> and <see cref="IsSynchronizeYAxes" />),
		/// or save a list of states for all GraphPanes if the panes are synchronized.
		/// </summary>
		/// <param name="primaryPane">The primary GraphPane on which zoom/pan/scroll operations
		/// are taking place</param>
		private void ZoomStateRestore( GraphPane primaryPane )
		{
			if ( _isSynchronizeXAxes || _isSynchronizeYAxes )
			{
				for ( int i = 0; i < _masterPane._paneList.Count; i++ )
				{
					if ( i < _zoomStateStack.Count )
						_zoomStateStack[i].ApplyState( _masterPane._paneList[i] );
				}
			}
			else if ( _zoomState != null )
				_zoomState.ApplyState( primaryPane );

			ZoomStateClear();
		}

		/// <summary>
		/// Place the previously saved states of the GraphPanes on the individual GraphPane
		/// <see cref="ZedGraph.GraphPane.ZoomStack" /> collections.  This provides for an
		/// option to undo the state change at a later time.  Save a single
		/// (<see paramref="primaryPane" />) GraphPane if the panes are not synchronized
		/// (see <see cref="IsSynchronizeXAxes" /> and <see cref="IsSynchronizeYAxes" />),
		/// or save a list of states for all GraphPanes if the panes are synchronized.
		/// </summary>
		/// <param name="primaryPane">The primary GraphPane on which zoom/pan/scroll operations
		/// are taking place</param>
		/// <returns>The <see cref="ZoomState" /> that corresponds to the
		/// <see paramref="primaryPane" />.
		/// </returns>
		private void ZoomStatePush( GraphPane primaryPane )
		{
			if ( _isSynchronizeXAxes || _isSynchronizeYAxes )
			{
				for ( int i = 0; i < _masterPane._paneList.Count; i++ )
				{
					if ( i < _zoomStateStack.Count )
						_masterPane._paneList[i].ZoomStack.Add( _zoomStateStack[i] );
				}
			}
			else if ( _zoomState != null )
				primaryPane.ZoomStack.Add( _zoomState );

			ZoomStateClear();
		}

		/// <summary>
		/// Clear the collection of saved states.
		/// </summary>
		private void ZoomStateClear()
		{
			_zoomStateStack.Clear();
			_zoomState = null;
		}

		/// <summary>
		/// Clear all states from the undo stack for each GraphPane.
		/// </summary>
		private void ZoomStatePurge()
		{
			foreach ( GraphPane pane in _masterPane._paneList )
				pane.ZoomStack.Clear();
		}

	#endregion

	}
}

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
Engineer
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