//============================================================================
//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 DrawGraph
{
/*
///
///
///
public struct DrawingThreadData
{
///
///
///
public Graphics _g;
///
///
///
public MasterPane _masterPane;
// public DrawingThread( Graphics g, MasterPane masterPane )
// {
// _g = g;
// _masterPane = masterPane;
// }
}
*/
///
/// The ZedGraphControl class provides a UserControl interface to the
/// 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
/// property.
///
/// John Champion revised by Jerry Vos
/// $Revision: 3.82 $ $Date: 2007/04/16 00:03:06 $
public partial class ZedGraphControl : UserControl
{
#region Private Fields
///
/// This private field contains the instance for the MasterPane object of this control.
/// You can access the MasterPane object through the public property
/// . This is nulled when this Control is
/// disposed.
///
private MasterPane _masterPane;
///
/// private field that determines if anti-aliased drawing will be forced on. Use the
/// public property to access this value.
///
private bool _isAntiAlias = false;
///
/// private field that determines whether or not tooltips will be displayed
/// when the mouse hovers over data values. Use the public property
/// to access this value.
///
private bool _isShowPointValues = false;
///
/// 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 to access this value.
///
private bool _isShowCursorValues = false;
///
/// private field that determines the format for displaying tooltip values.
/// This format is passed to .
/// Use the public property to access this
/// value.
///
private string _pointValueFormat = PointPair.DefaultFormat;
///
/// private field that determines whether or not the context menu will be available. Use the
/// public property to access this value.
///
private bool _isShowContextMenu = true;
///
/// 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 to access this value.
///
///
/// Note that, if this value is set to false, the user will receive no indicative feedback
/// in response to a Copy action.
///
private bool _isShowCopyMessage = true;
private SaveFileDialog _saveFileDialog = new SaveFileDialog();
///
/// private field that determines whether the settings of
/// and
/// will be overridden to true during printing operations.
///
///
/// 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 is true, but the default
/// setting for is false.
///
///
/// A value of true will cause both and
/// to be temporarily set to true during
/// printing operations.
///
private bool _isPrintScaleAll = true;
///
/// private field that determines whether or not the visible aspect ratio of the
/// will be preserved
/// when printing this .
///
private bool _isPrintKeepAspectRatio = true;
///
/// private field that determines whether or not the
/// dimensions will be expanded to fill the
/// available space when printing this .
///
///
/// If is also true, then the
/// dimensions will be expanded to fit as large
/// a space as possible while still honoring the visible aspect ratio.
///
private bool _isPrintFillPage = true;
///
/// private field that determines the format for displaying tooltip date values.
/// This format is passed to .
/// Use the public property to access this
/// value.
///
private string _pointDateFormat = XDate.DefaultFormatStr;
///
/// private value that determines whether or not zooming is enabled for the control in the
/// vertical direction. Use the public property to access this
/// value.
///
private bool _isEnableVZoom = true;
///
/// private value that determines whether or not zooming is enabled for the control in the
/// horizontal direction. Use the public property to access this
/// value.
///
private bool _isEnableHZoom = true;
///
/// private value that determines whether or not point editing is enabled in the
/// vertical direction. Use the public property to access this
/// value.
///
private bool _isEnableVEdit = false;
///
/// private value that determines whether or not point editing is enabled in the
/// horizontal direction. Use the public property to access this
/// value.
///
private bool _isEnableHEdit = false;
///
/// private value that determines whether or not panning is allowed for the control in the
/// horizontal direction. Use the
/// public property to access this value.
///
private bool _isEnableHPan = true;
///
/// private value that determines whether or not panning is allowed for the control in the
/// vertical direction. Use the
/// public property to access this value.
///
private bool _isEnableVPan = true;
// Revision: JCarpenter 10/06
///
/// Internal variable that indicates if the control can manage selections.
///
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;
///
/// private field that stores a instance, which maintains
/// a persistent selection of printer options.
///
///
/// This is needed so that a "Print" action utilizes the settings from a prior
/// "Page Setup" action.
private PrintDocument _pdSave = null;
//private PrinterSettings printSave = null;
//private PageSettings pageSave = null;
///
/// This private field contains a list of selected CurveItems.
///
//private List _selection = new List();
private Selection _selection = new Selection();
#endregion
#region Fields: Buttons & Keys Properties
///
/// Gets or sets a value that determines which Mouse button will be used to click on
/// linkable objects
///
///
private MouseButtons _linkButtons = MouseButtons.Left;
///
/// Gets or sets a value that determines which modifier keys will be used to click
/// on linkable objects
///
///
private Keys _linkModifierKeys = Keys.Alt;
///
/// Gets or sets a value that determines which Mouse button will be used to edit point
/// data values
///
///
/// This setting only applies if and/or
/// are true.
///
///
private MouseButtons _editButtons = MouseButtons.Right;
///
/// Gets or sets a value that determines which modifier keys will be used to edit point
/// data values
///
///
/// This setting only applies if and/or
/// are true.
///
///
private Keys _editModifierKeys = Keys.Alt;
///
/// Gets or sets a value that determines which mouse button will be used to select
/// 's.
///
///
/// This setting only applies if is true.
///
///
private MouseButtons _selectButtons = MouseButtons.Left;
///
/// Gets or sets a value that determines which modifier keys will be used to select
/// 's.
///
///
/// This setting only applies if is true.
///
///
private Keys _selectModifierKeys = Keys.Shift;
private Keys _selectAppendModifierKeys = Keys.Shift | Keys.Control;
///
/// Gets or sets a value that determines which Mouse button will be used to perform
/// zoom operations
///
///
/// This setting only applies if and/or
/// are true.
///
///
///
///
private MouseButtons _zoomButtons = MouseButtons.Left;
///
/// Gets or sets a value that determines which modifier keys will be used to perform
/// zoom operations
///
///
/// This setting only applies if and/or
/// are true.
///
///
///
///
private Keys _zoomModifierKeys = Keys.None;
///
/// Gets or sets a value that determines which Mouse button will be used as a
/// secondary option to perform zoom operations
///
///
/// This setting only applies if and/or
/// are true.
///
///
///
///
private MouseButtons _zoomButtons2 = MouseButtons.None;
///
/// Gets or sets a value that determines which modifier keys will be used as a
/// secondary option to perform zoom operations
///
///
/// This setting only applies if and/or
/// are true.
///
///
///
///
private Keys _zoomModifierKeys2 = Keys.None;
///
/// Gets or sets a value that determines which Mouse button will be used to perform
/// panning operations
///
///
/// This setting only applies if and/or
/// 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).
///
///
///
///
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
///
/// Gets or sets a value that determines which modifier keys will be used to perform
/// panning operations
///
///
/// This setting only applies if and/or
/// 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).
///
///
///
///
private Keys _panModifierKeys = Keys.Control;
///
/// Gets or sets a value that determines which Mouse button will be used as a
/// secondary option to perform panning operations
///
///
/// This setting only applies if and/or
/// 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).
///
///
///
///
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
///
/// Gets or sets a value that determines which modifier keys will be used as a
/// secondary option to perform panning operations
///
///
/// This setting only applies if and/or
/// 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).
///
///
///
///
private Keys _panModifierKeys2 = Keys.None;
#endregion
#region Button and Key Properties
///
/// Gets or sets a value that determines which mouse button will be used as a primary option
/// to trigger a zoom event.
///
///
/// This value is combined with to determine the actual zoom combination.
/// A secondary zoom button/key combination option is available via and
/// . To not use this button/key combination, set the value
/// of to .
///
[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; }
}
///
/// Gets or sets a value that determines which mouse button will be used as the secondary option
/// to trigger a zoom event.
///
///
/// This value is combined with to determine the actual zoom combination.
/// The primary zoom button/key combination option is available via and
/// . To not use this button/key combination, set the value
/// of to .
///
[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; }
}
///
/// Gets or sets a value that determines which modifier keys will be used as a primary option
/// to trigger a zoom event.
///
///
/// This value is combined with to determine the actual zoom combination.
/// A secondary zoom button/key combination option is available via and
/// . To not use this button/key combination, set the value
/// of to .
///
[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; }
}
///
/// Gets or sets a value that determines which modifier keys will be used as a secondary option
/// to trigger a zoom event.
///
///
/// This value is combined with to determine the actual zoom combination.
/// A primary zoom button/key combination option is available via and
/// . To not use this button/key combination, set the value
/// of to .
///
[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; }
}
///
/// Gets or sets a value that determines which mouse button will be used as a primary option
/// to trigger a pan event.
///
///
/// This value is combined with to determine the actual pan combination.
/// A secondary pan button/key combination option is available via and
/// . To not use this button/key combination, set the value
/// of to .
///
[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; }
}
///
/// Gets or sets a value that determines which mouse button will be used as the secondary option
/// to trigger a pan event.
///
///
/// This value is combined with to determine the actual pan combination.
/// The primary pan button/key combination option is available via and
/// . To not use this button/key combination, set the value
/// of to .
///
[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.
///
/// Gets or sets a value that determines which modifier keys will be used as a primary option
/// to trigger a pan event.
///
///
/// This value is combined with to determine the actual pan combination.
/// A secondary pan button/key combination option is available via and
/// . To not use this button/key combination, set the value
/// of to .
///
[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; }
}
///
/// Gets or sets a value that determines which modifier keys will be used as a secondary option
/// to trigger a pan event.
///
///
/// This value is combined with to determine the actual pan combination.
/// A primary pan button/key combination option is available via and
/// . To not use this button/key combination, set the value
/// of to .
///
[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; }
}
///
/// Gets or sets a value that determines which Mouse button will be used to edit point
/// data values
///
///
/// This setting only applies if and/or
/// are true.
///
///
[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; }
}
///
/// Gets or sets a value that determines which modifier keys will be used to edit point
/// data values
///
///
/// This setting only applies if and/or
/// are true.
///
///
[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; }
}
///
/// Gets or sets a value that determines which Mouse button will be used to
/// select 's.
///
///
/// This setting only applies if is true.
///
///
[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; }
}
///
/// Gets or sets a value that determines which Modifier keys will be used to
/// select 's.
///
///
/// This setting only applies if is true.
///
///
[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; }
}
///
/// Gets or sets a value that determines which Modifier keys will be used to
/// append a to the selection list.
///
[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; }
}
///
/// Gets or sets a value that determines which Mouse button will be used to click
/// on linkable objects
///
///
///
// ///
[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; }
}
///
/// Gets or sets a value that determines which modifier keys will be used to click
/// on linkable objects
///
///
///
// ///
[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
///
/// Internal variable that indicates the control is currently being zoomed.
///
private bool _isZooming = false;
///
/// Internal variable that indicates the control is currently being panned.
///
private bool _isPanning = false;
///
/// Internal variable that indicates a point value is currently being edited.
///
private bool _isEditing = false;
// Revision: JCarpenter 10/06
///
/// Internal variable that indicates the control is currently using selection.
///
private bool _isSelecting = false;
///
/// Internal variable that stores the reference for the Pane that is
/// currently being zoomed or panned.
///
private GraphPane _dragPane = null;
///
/// Internal variable that stores a rectangle which is either the zoom rectangle, or the incremental
/// pan amount since the last mousemove event.
///
private Point _dragStartPt;
private Point _dragEndPt;
private int _dragIndex;
private CurveItem _dragCurve;
private PointPair _dragStartPair;
///
/// private field that stores the state of the scale ranges prior to starting a panning action.
///
private ZoomState _zoomState;
private ZoomStateStack _zoomStateStack;
//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
///
/// A delegate that allows subscribing methods to append or modify the context menu.
///
/// The source object
/// A reference to the object
/// that contains the context menu.
///
/// The point at which the mouse was clicked
/// The current context menu state
///
public delegate void ContextMenuBuilderEventHandler(ZedGraphControl sender,
ContextMenuStrip menuStrip, Point mousePt, ContextMenuObjectState objState);
///
/// Subscribe to this event to be able to modify the ZedGraph context menu.
///
///
/// 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 parameter.
///
[Bindable(true), Category("Events"),
Description("Subscribe to this event to be able to modify the ZedGraph context menu")]
public event ContextMenuBuilderEventHandler ContextMenuBuilder;
///
/// A delegate that allows notification of zoom and pan events.
///
/// The source object
/// A object that corresponds to the state of the
/// before the zoom or pan event.
/// A object that corresponds to the state of the
/// after the zoom or pan event
///
public delegate void ZoomEventHandler(ZedGraphControl sender, ZoomState oldState,
ZoomState newState);
///
/// Subscribe to this event to be notified when the is zoomed or panned by the user,
/// either via a mouse drag operation or by the context menu commands.
///
[Bindable(true), Category("Events"),
Description("Subscribe to this event to be notified when the graph is zoomed or panned")]
public event ZoomEventHandler ZoomEvent;
///
/// A delegate that allows notification of scroll events.
///
/// The source object
/// The source object
/// A object that corresponds to the state of the
/// before the scroll event.
/// A object that corresponds to the state of the
/// after the scroll event
///
public delegate void ScrollDoneHandler(ZedGraphControl sender, ScrollBar scrollBar,
ZoomState oldState, ZoomState newState);
///
/// Subscribe to this event to be notified when the is scrolled by the user
/// using the scrollbars.
///
[Bindable(true), Category("Events"),
Description("Subscribe this event to be notified when a scroll operation using the scrollbars is completed")]
public event ScrollDoneHandler ScrollDoneEvent;
///
/// A delegate that allows notification of scroll events.
///
/// The source object
/// The source object
/// A object that corresponds to the state of the
/// before the scroll event.
/// A object that corresponds to the state of the
/// after the scroll event
///
public delegate void ScrollProgressHandler(ZedGraphControl sender, ScrollBar scrollBar,
ZoomState oldState, ZoomState newState);
///
/// Subscribe to this event to be notified when the is scrolled by the user
/// using the scrollbars.
///
[Bindable(true), Category("Events"),
Description("Subscribe this event to be notified continuously as a scroll operation is taking place")]
public event ScrollProgressHandler ScrollProgressEvent;
///
/// A delegate that receives notification after a point-edit operation is completed.
///
/// The source object
/// The object that contains the
/// point that has been edited
/// The object that contains the point
/// that has been edited
/// The integer index of the edited within the
/// of the selected
///
///
public delegate string PointEditHandler(ZedGraphControl sender, GraphPane pane,
CurveItem curve, int iPt);
///
/// Subscribe to this event to receive notifcation and/or respond after a data
/// point has been edited via and .
///
///
/// To subscribe to this event, use the following in your Form_Load method:
/// zedGraphControl1.PointEditEvent +=
/// new ZedGraphControl.PointEditHandler( MyPointEditHandler );
/// Add this method to your Form1.cs:
///
/// private string MyPointEditHandler( object sender, GraphPane pane, CurveItem curve, int iPt )
/// {
/// PointPair pt = curve[iPt];
/// return "This value is " + pt.Y.ToString("f2") + " gallons";
/// }
///
[Bindable(true), Category("Events"),
Description("Subscribe to this event to respond to data point edit actions")]
public event PointEditHandler PointEditEvent;
///
/// A delegate that allows custom formatting of the point value tooltips
///
/// The source object
/// The object that contains the point value of interest
/// The object that contains the point value of interest
/// The integer index of the selected within the
/// of the selected
///
public delegate string PointValueHandler(ZedGraphControl sender, GraphPane pane,
CurveItem curve, int iPt);
///
/// Subscribe to this event to provide custom formatting for the tooltips
///
///
/// To subscribe to this event, use the following in your FormLoad method:
/// zedGraphControl1.PointValueEvent +=
/// new ZedGraphControl.PointValueHandler( MyPointValueHandler );
/// Add this method to your Form1.cs:
///
/// 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
/// }
///
[Bindable(true), Category("Events"),
Description("Subscribe to this event to provide custom-formatting for data point tooltips")]
public event PointValueHandler PointValueEvent;
///
/// A delegate that allows notification of mouse events on Graph objects.
///
/// The source object
/// A corresponding to this event
///
///
/// Return true if you have handled the mouse event entirely, and you do not
/// want the to do any further action (e.g., starting
/// a zoom operation). Return false if ZedGraph should go ahead and process the
/// mouse event.
///
public delegate bool ZedMouseEventHandler(ZedGraphControl sender, MouseEventArgs e);
///
/// Subscribe to this event to provide notification of MouseDown clicks on graph
/// objects
///
///
/// This event provides for a notification when the mouse is clicked on an object
/// within any of the associated
/// with this . This event will use the
/// method to determine which object
/// was clicked. The boolean value that you return from this handler determines whether
/// or not the will do any further handling of the
/// MouseDown event (see ). Return true if you have
/// handled the MouseDown event entirely, and you do not
/// want the to do any further action (e.g., starting
/// a zoom operation). Return false if ZedGraph should go ahead and process the
/// MouseDown event.
///
[Bindable(true), Category("Events"),
Description("Subscribe to be notified when the left mouse button is clicked down")]
public event ZedMouseEventHandler MouseDownEvent;
///
/// 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.
///
[Bindable(false), Browsable(false)]
public new event MouseEventHandler MouseDown;
///
/// 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.
///
[Bindable(false), Browsable(false)]
public new event MouseEventHandler MouseUp;
///
/// 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.
///
[Bindable(false), Browsable(false)]
private new event MouseEventHandler MouseMove;
///
/// Subscribe to this event to provide notification of MouseUp clicks on graph
/// objects
///
///
/// This event provides for a notification when the mouse is clicked on an object
/// within any of the associated
/// with this . This event will use the
/// method to determine which object
/// was clicked. The boolean value that you return from this handler determines whether
/// or not the will do any further handling of the
/// MouseUp event (see ). Return true if you have
/// handled the MouseUp event entirely, and you do not
/// want the to do any further action (e.g., starting
/// a zoom operation). Return false if ZedGraph should go ahead and process the
/// MouseUp event.
///
[Bindable(true), Category("Events"),
Description("Subscribe to be notified when the left mouse button is released")]
public event ZedMouseEventHandler MouseUpEvent;
///
/// Subscribe to this event to provide notification of MouseMove events over graph
/// objects
///
///
/// 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 will do any further handling of the
/// MouseMove event (see ). Return true if you
/// have handled the MouseMove event entirely, and you do not
/// want the to do any further action.
/// Return false if ZedGraph should go ahead and process the MouseMove event.
///
[Bindable(true), Category("Events"),
Description("Subscribe to be notified when the mouse is moved inside the control")]
public event ZedMouseEventHandler MouseMoveEvent;
///
/// Subscribe to this event to provide notification of Double Clicks on graph
/// objects
///
///
/// This event provides for a notification when the mouse is double-clicked on an object
/// within any of the associated
/// with this . This event will use the
/// method to determine which object
/// was clicked. The boolean value that you return from this handler determines whether
/// or not the will do any further handling of the
/// DoubleClick event (see ). Return true if you have
/// handled the DoubleClick event entirely, and you do not
/// want the to do any further action.
/// Return false if ZedGraph should go ahead and process the
/// DoubleClick event.
///
[Bindable(true), Category("Events"),
Description("Subscribe to be notified when the left mouse button is double-clicked")]
public event ZedMouseEventHandler DoubleClickEvent;
///
/// A delegate that allows notification of clicks on ZedGraph objects that have
/// active links enabled
///
/// The source object
/// The source in which the click
/// occurred.
///
/// The source object which was clicked. This is typically
/// a type of if a curve point was clicked, or
/// a type of if a graph object was clicked.
///
/// The object, belonging to
/// , that contains the link information
///
/// An index value, typically used if a
/// was clicked, indicating the ordinal value of the actual point that was clicked.
///
///
/// Return true if you have handled the LinkEvent entirely, and you do not
/// want the to do any further action.
/// Return false if ZedGraph should go ahead and process the LinkEvent.
///
public delegate bool LinkEventHandler(ZedGraphControl sender, GraphPane pane,
object source, Link link, int index);
///
/// Subscribe to this event to be able to respond to mouse clicks within linked
/// objects.
///
///
/// Linked objects are typically either type objects or
/// type objects. These object types can include
/// hyperlink information allowing for "drill-down" type operation.
///
///
///
/// CurveItem.Link
/// GraphObj.Link
// ///
[Bindable(true), Category("Events"),
Description("Subscribe to be notified when a link-enabled item is clicked")]
public event LinkEventHandler LinkEvent;
#endregion
#region Constructors
///
/// Default Constructor
///
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("DrawGraph.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();
}
///
/// Clean up any resources being used.
///
/// true if the components should be
/// disposed, false otherwise
protected override void Dispose(bool disposing)
{
lock (this)
{
if (disposing)
{
if (components != null)
components.Dispose();
}
base.Dispose(disposing);
_masterPane = null;
}
}
#endregion
#region Properties
///
/// Gets or sets the property for the control
///
[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; }
}
*/
///
/// Gets or sets the property for the control
///
///
/// actually uses a object
/// to hold a list of objects. This property really only
/// accesses the first in the list. If there is more
/// than one , use the
/// indexer property to access any of the objects.
[
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);
}
}
}
}
///
/// 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 )
/// will be honored.
///
[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; }
}
///
/// Gets or sets a value that determines whether or not tooltips will be displayed
/// when the mouse hovers over data values.
///
/// The displayed values are taken from
/// if it is a type, or
/// otherwise (using the as a format string).
/// Additionally, the user can custom format the values using the
/// event. Note that
/// may be overridden by .
///
[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; }
}
///
/// 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
/// .
///
/// The displayed values are taken from the current mouse position, and formatted
/// according to and/or . If this
/// value is set to true, it overrides the setting.
///
[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; }
}
///
/// Gets or sets a value that determines whether or not editing of point data is allowed in
/// the horizontal direction.
///
///
/// Editing is done by holding down the Alt key, and left-clicking on an individual point of
/// a given to drag it to a new location. The Mouse and Key
/// combination for this mode are modifiable using and
/// .
///
///
///
///
[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; }
}
///
/// Gets or sets a value that determines whether or not editing of point data is allowed in
/// the vertical direction.
///
///
/// Editing is done by holding down the Alt key, and left-clicking on an individual point of
/// a given to drag it to a new location. The Mouse and Key
/// combination for this mode are modifiable using and
/// .
///
[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; }
}
///
/// Gets or sets a value that determines whether or not zooming is allowed for the control.
///
///
/// Zooming is done by left-clicking inside the to drag
/// out a rectangle, indicating the new scale ranges that will be part of the graph.
///
[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; }
}
///
/// Gets or sets a value that determines whether or not zooming is allowed for the control in
/// the horizontal direction.
///
///
/// Zooming is done by left-clicking inside the to drag
/// out a rectangle, indicating the new scale ranges that will be part of the graph.
///
[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; }
}
///
/// Gets or sets a value that determines whether or not zooming is allowed for the control in
/// the vertical direction.
///
///
/// Zooming is done by left-clicking inside the to drag
/// out a rectangle, indicating the new scale ranges that will be part of the graph.
///
[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; }
}
///
/// Gets or sets a value that determines whether or not panning is allowed for the control in
/// the horizontal direction.
///
///
/// Panning is done by clicking the middle mouse button (or holding down the shift key
/// while clicking the left mouse button) inside the and
/// dragging the mouse around to shift the scale ranges as desired.
///
///
[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; }
}
///
/// Gets or sets a value that determines whether or not panning is allowed for the control in
/// the vertical direction.
///
///
/// Panning is done by clicking the middle mouse button (or holding down the shift key
/// while clicking the left mouse button) inside the and
/// dragging the mouse around to shift the scale ranges as desired.
///
///
[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; }
}
///
/// Gets or sets a value that determines whether or not the context menu will be available.
///
/// The context menu is a menu that appears when you right-click on the
/// . It provides options for Zoom, Pan, AutoScale, Clipboard
/// Copy, and toggle .
///
/// true to allow the context menu, false to disable it
[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; }
}
///
/// Gets or sets a value that determines whether or not a message box will be shown
/// in response to a context menu "Copy" command.
///
///
/// Note that, if this property is set to false, the user will receive no
/// indicative feedback in response to a Copy action.
///
[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; }
}
///
/// Gets or sets the instance that will be used
/// by the "Save As..." context menu item.
///
///
/// This provides the opportunity to modify the dialog, such as setting the
/// property.
///
[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; }
}
///
/// Gets or sets a value that determines whether or not the visible aspect ratio of the
/// will be preserved
/// when printing this .
///
[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; }
}
///
/// Gets or sets a value that determines whether or not the
/// dimensions will be expanded to fill the
/// available space when printing this .
///
///
/// If is also true, then the
/// dimensions will be expanded to fit as large
/// a space as possible while still honoring the visible aspect ratio.
///
[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; }
}
///
/// Gets or sets a value that determines whether the settings of
/// and
/// will be overridden to true during printing operations.
///
///
/// 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 is true, but the default
/// setting for is false.
///
///
/// A value of true will cause both and
/// to be temporarily set to true during
/// printing operations.
///
[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; }
}
///
/// Gets or sets a value that controls whether or not the axis value range for the scroll
/// bars will be set automatically.
///
///
/// 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 at the
/// time that was last called. Note that a value of true
/// can override any setting of , ,
/// , ,
/// , and . Note also that you must
/// call from the for this to
/// work properly (e.g., don't call it directly from the .
/// Alternatively, you can call at anytime to set
/// the scroll bar range.
/// In most cases, you will probably want to disable
/// before activating this option.
///
[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; }
}
///
/// Set a "grace" value that leaves a buffer area around the data when
/// is true.
///
///
/// 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.
///
public double ScrollGrace
{
get { return _scrollGrace; }
set { _scrollGrace = value; }
}
///
/// Gets or sets a value that determines if the horizontal scroll bar will be visible.
///
/// 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 and ).
/// You can control the available range of scrolling with the and
/// properties. Note that the scroll range can be set automatically by
/// .
/// In most cases, you will probably want to disable
/// before activating this option.
///
/// A boolean value. true to display a horizontal scrollbar, false otherwise.
[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()); }
}
///
/// Gets or sets a value that determines if the vertical scroll bar will be visible.
///
/// 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 and ).
/// You can control the available range of scrolling with the and
/// properties.
/// Note that the vertical scroll bar only affects the ; it has no impact on
/// the . The panning options affect both the and
/// . Note also that the scroll range can be set automatically by
/// .
/// In most cases, you will probably want to disable
/// before activating this option.
///
/// A boolean value. true to display a vertical scrollbar, false otherwise.
[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()); }
}
///
/// Gets or sets a value that determines if the
/// ranges for all objects in the will
/// be forced to match.
///
///
/// If set to true (default is false), then all of the objects
/// in the associated with this
/// 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.
///
[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;
}
}
///
/// Gets or sets a value that determines if the
/// ranges for all objects in the will
/// be forced to match.
///
///
/// If set to true (default is false), then all of the objects
/// in the associated with this
/// 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.
///
[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;
}
}
///
/// Gets or sets a value that determines if the vertical scroll bar will affect the Y2 axis.
///
///
/// 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 and values are not set to the
/// same proportions as and with respect
/// to the actual and . Also note that
/// this property is actually just an alias to the
/// property of the first element of .
///
///
///
///
///
///
[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;
}
}
}
///
/// Access the for the Y axes.
///
///
/// This list maintains the user scale ranges for the scroll bars for each axis
/// in the . Each ordinal location in
/// corresponds to an equivalent ordinal location
/// in .
///
///
///
[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; }
}
///
/// Access the for the Y2 axes.
///
///
/// This list maintains the user scale ranges for the scroll bars for each axis
/// in the . Each ordinal location in
/// corresponds to an equivalent ordinal location
/// in .
///
///
///
[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; }
}
///
/// The minimum value for the X axis scroll range.
///
///
/// Effectively, the minimum endpoint of the scroll range will cause the
/// value to be set to . Note that this
/// value applies only to the scroll bar settings. Axis panning (see )
/// is not affected by this value. Note that this value can be overridden by
/// and .
///
/// A double value indicating the minimum axis 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; }
}
///
/// The maximum value for the X axis scroll range.
///
///
/// Effectively, the maximum endpoint of the scroll range will cause the
/// value to be set to . Note that this
/// value applies only to the scroll bar settings. Axis panning (see )
/// is not affected by this value. Note that this value can be overridden by
/// and .
///
/// A double value indicating the maximum axis 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; }
}
///
/// The minimum value for the Y axis scroll range.
///
///
/// Effectively, the minimum endpoint of the scroll range will cause the
/// value to be set to . Note that this
/// value applies only to the scroll bar settings. Axis panning (see )
/// is not affected by this value. Note that this value can be overridden by
/// and . Also note that
/// this property is actually just an alias to the
/// property of the first element of .
///
/// A double value indicating the minimum axis value
///
[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;
}
}
}
///
/// The maximum value for the Y axis scroll range.
///
///
/// Effectively, the maximum endpoint of the scroll range will cause the
/// value to be set to . Note that this
/// value applies only to the scroll bar settings. Axis panning (see )
/// is not affected by this value. Note that this value can be overridden by
/// and . Also note that
/// this property is actually just an alias to the
/// property of the first element of .
///
/// A double value indicating the maximum axis value
///
[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;
}
}
}
///
/// The minimum value for the Y2 axis scroll range.
///
///
/// Effectively, the minimum endpoint of the scroll range will cause the
/// value to be set to . Note that this
/// value applies only to the scroll bar settings. Axis panning (see )
/// is not affected by this value. Note that this value can be overridden by
/// and . Also note that
/// this property is actually just an alias to the
/// property of the first element of .
///
/// A double value indicating the minimum axis value
///
[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;
}
}
}
///
/// The maximum value for the Y2 axis scroll range.
///
///
/// Effectively, the maximum endpoint of the scroll range will cause the
/// value to be set to . Note that this
/// value applies only to the scroll bar settings. Axis panning (see )
/// is not affected by this value. Note that this value can be overridden by
/// and . Also note that
/// this property is actually just an alias to the
/// property of the first element of .
///
/// A double value indicating the maximum axis value
///
[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;
}
}
}
///
/// Returns true if the user is currently scrolling via the scrollbar, or
/// false if no scrolling is taking place.
///
///
/// This method just tests ScrollBar.Capture to see if the
/// mouse has been captured by the scroll bar. If so, scrolling is active.
///
public Boolean IsScrolling
{
get
{
if (hScrollBar1 != null && vScrollBar1 != null)
return hScrollBar1.Capture || vScrollBar1.Capture;
else
return false;
}
}
///
/// Gets or sets the format for displaying tooltip values.
/// This format is passed to .
///
///
/// Use the type
/// to determine the format strings.
///
[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; }
}
///
/// Gets or sets the format for displaying tooltip values.
/// This format is passed to .
///
///
/// Use the type
/// to determine the format strings.
///
[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; }
}
///
/// 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.
///
[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; }
}
///
/// 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.
///
[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; }
}
///
/// Gets the graph pane's current image.
///
///
///
/// When the control has been disposed before this call.
///
[Bindable(false), Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Image GetImage()
{
lock (this)
{
if (BeenDisposed || _masterPane == null || _masterPane[0] == null)
throw new ZException("The control has been disposed");
return _masterPane.GetImage();
}
}
///
/// 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 ZedGraphControl.GraphPane == null.
///
[Bindable(false), Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public bool BeenDisposed
{
get
{
lock (this) return _masterPane == null;
}
}
// Revision: JCarpenter 10/06
///
/// Readonly property that gets the list of selected CurveItems
///
public Selection Selection
{
get { return _selection; }
}
///
/// Gets or sets a value that determines whether or not selection is allowed for the control.
///
[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
///
/// Called by the system to update the control on-screen
///
///
/// A PaintEventArgs object containing the Graphics specifications
/// for this Paint event.
///
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;
/*
///
///
///
///
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
{
}
}
*/
///
/// Called when the control has been resized.
///
///
/// A reference to the control that has been resized.
///
///
/// An EventArgs object.
///
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();
}
}
/// This performs an axis change command on the graphPane.
///
///
/// This is the same as
/// ZedGraphControl.GraphPane.AxisChange( ZedGraphControl.CreateGraphics() ), however,
/// this method also calls if
/// is true.
///
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
///
/// Handle a MouseDown event in the
///
/// A reference to the
/// A instance
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];
}
}
}
///
/// Set the cursor according to the current mouse location.
///
protected void SetCursor()
{
SetCursor(this.PointToClient(Control.MousePosition));
}
///
/// Set the cursor according to the current mouse location.
///
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;
}
}
///
/// Handle a KeyUp event
///
/// The in which the KeyUp occurred.
/// A instance.
protected void ZedGraphControl_KeyUp(object sender, KeyEventArgs e)
{
SetCursor();
}
///
/// Handle the Key Events so ZedGraph can Escape out of a panning or zooming operation.
///
///
///
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();
}
}
///
/// Handle a MouseUp event in the
///
/// A reference to the
/// A instance
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;
}
///
/// Make a string label that corresponds to a user scale value.
///
/// The axis from which to obtain the scale value. This determines
/// if it's a date value, linear, log, etc.
/// The value to be made into a label
/// The ordinal position of the value
/// true to override the ordinal settings of the axis,
/// and prefer the actual value instead.
/// The string label.
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 "";
}
///
/// protected method for handling MouseMove events to display tooltips over
/// individual datapoints.
///
///
/// A reference to the control that has the MouseMove event.
///
///
/// A MouseEventArgs object.
///
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(curve.GetXAxis(pane), 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, x2, y, y2;
pane.ReverseTransform(mousePt, out x, out x2, 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
///
/// Handle a MouseWheel event in the
///
/// A reference to the
/// A instance
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();
}
}
}
///
/// Zoom a specified pane in or out according to the specified zoom fraction.
///
///
/// The zoom will occur on the , , and
/// only if the corresponding flag, or
/// , is true. Note that if there are multiple Y or Y2 axes, all of
/// them will be zoomed.
///
/// The instance to be zoomed.
/// 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.
/// The screen position about which the zoom will be centered. This
/// value is only used if is true.
///
/// true to cause the zoom to be centered on the point
/// , false to center on the .
///
/// true to force a refresh of the control, false to leave it unrefreshed
protected void ZoomPane(GraphPane pane, double zoomFraction, PointF centerPt,
bool isZoomOnCenter, bool isRefresh)
{
double x;
double x2;
double[] y;
double[] y2;
pane.ReverseTransform(centerPt, out x, out x2, out y, out y2);
if (_isEnableHZoom)
{
ZoomScale(pane.XAxis, zoomFraction, x, isZoomOnCenter);
ZoomScale(pane.X2Axis, zoomFraction, x2, 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();
}
///
/// Zoom a specified pane in or out according to the specified zoom fraction.
///
///
/// The zoom will occur on the , , and
/// only if the corresponding flag, or
/// , is true. Note that if there are multiple Y or Y2 axes, all of
/// them will be zoomed.
///
/// The instance to be zoomed.
/// 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.
/// The screen position about which the zoom will be centered. This
/// value is only used if is true.
///
/// true to cause the zoom to be centered on the point
/// , false to center on the .
///
public void ZoomPane(GraphPane pane, double zoomFraction, PointF centerPt, bool isZoomOnCenter)
{
ZoomPane(pane, zoomFraction, centerPt, isZoomOnCenter, true);
}
///
/// Zoom the specified axis by the specified amount, with the center of the zoom at the
/// (optionally) specified point.
///
///
/// This method is used for MouseWheel zoom operations
/// The to be zoomed.
/// 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.
/// The location for the center of the zoom. This is only used if
/// is true.
/// true if the zoom is to be centered at the
/// screen position, false for the zoom to be centered within
/// the .
///
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, xx1, xx2;
double[] y1, y2, yy1, yy2;
//PointF endPoint = mousePt;
//PointF startPoint = ( (Control)sender ).PointToClient( this.dragRect.Location );
_dragPane.ReverseTransform(_dragStartPt, out x1, out xx1, out y1, out yy1);
_dragPane.ReverseTransform(mousePt, out x2, out xx2, out y2, out yy2);
if (_isEnableHPan)
{
PanScale(_dragPane.XAxis, x1, x2);
PanScale(_dragPane.X2Axis, xx1, xx2);
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();
}
}
///
/// Handle a panning operation for the specified .
///
/// The to be panned
/// The value where the pan started. The scale range
/// will be shifted by the difference between and
/// .
///
/// The value where the pan ended. The scale range
/// will be shifted by the difference between and
/// .
///
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.IsX2Axis, _dragCurve.IsY2Axis,
_dragCurve.YAxisIndex, out curX, out curY);
double startX, startY;
_dragPane.ReverseTransform(_dragStartPt, _dragCurve.IsX2Axis, _dragCurve.IsY2Axis,
_dragCurve.YAxisIndex, out startX, out startY);
// calculate the new scale values for the point
PointPair newPt = new PointPair(_dragStartPair);
Scale xScale = _dragCurve.GetXAxis(_dragPane)._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, xx1, xx2;
double[] y1, y2, yy1, yy2;
//PointF startPoint = ( (Control)sender ).PointToClient( this.dragRect.Location );
_dragPane.ReverseTransform(_dragStartPt, out x1, out xx1, out y1, out yy1);
_dragPane.ReverseTransform(mousePtF, out x2, out xx2, 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;
_dragPane.X2Axis._scale._min = Math.Min(xx1, xx2);
_dragPane.X2Axis._scale._minAuto = false;
_dragPane.X2Axis._scale._max = Math.Max(xx1, xx2);
_dragPane.X2Axis._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
///
/// Perform selection on curves within the drag pane, or under the mouse click.
///
///
///
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, xx1, xx2;
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 xx1, out y1, out yy1);
_dragPane.ReverseTransform(mousePtF, out x2, out xx2, 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));
}
}
///
/// Use the MouseCaptureChanged as an indicator for the start and end of a scrolling operation
///
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();
}
}
///
/// Sets the value of the scroll range properties (see ,
/// , , and
/// based on the actual range of the data for
/// each corresponding .
///
///
/// This method is called automatically by if
///
/// is true. Note that this will not be called if you call AxisChange directly from the
/// . For example, zedGraphControl1.AxisChange() works properly, but
/// zedGraphControl1.GraphPane.AxisChange() does not.
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
///
/// Public enumeration that specifies the type of
/// object present at the Context Menu's mouse location
///
public enum ContextMenuObjectState
{
///
/// The object is an Inactive Curve Item at the Context Menu's mouse position
///
InactiveSelection,
///
/// The object is an active Curve Item at the Context Menu's mouse position
///
ActiveSelection,
///
/// There is no selectable object present at the Context Menu's mouse position
///
Background
}
//Revision: JCarpenter 10/06
///
/// Find the object currently under the mouse cursor, and return its state.
///
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;
}
///
/// protected method to handle the popup context menu in the .
///
///
///
private void contextMenuStrip1_Opening(object sender, CancelEventArgs e)
{
// disable context menu by default
e.Cancel = true;
ContextMenuStrip menuStrip = sender as ContextMenuStrip;
//Revision: JCarpenter 10/06
ContextMenuObjectState objState = GetObjectState();
if (_masterPane != null && menuStrip != null)
{
menuStrip.Items.Clear();
_isZooming = false;
_isPanning = false;
Cursor.Current = Cursors.Default;
_menuClickPt = this.PointToClient(Control.MousePosition);
GraphPane pane = _masterPane.FindPane(_menuClickPt);
if (_isShowContextMenu)
{
string menuStr = string.Empty;
//ToolStripMenuItem item = new ToolStripMenuItem();
//item.Name = "copy";
//item.Tag = "copy";
//item.Text = _resourceManager.GetString( "copy" );
//item.Click += new System.EventHandler( this.MenuClick_Copy );
//menuStrip.Items.Add( item );
//ToolStripMenuItem item = new ToolStripMenuItem();
//item.Name = "±£´æÍ¼Æ¬";
//item.Tag = "±£´æÍ¼Æ¬";
//item.Text = _resourceManager.GetString("save_as");
//item.Click += new System.EventHandler(this.MenuClick_SaveAs);
//menuStrip.Items.Add(item);
//item = new ToolStripMenuItem();
//item.Name = "page_setup";
//item.Tag = "page_setup";
//item.Text = _resourceManager.GetString( "page_setup" );
//item.Click += new System.EventHandler( this.MenuClick_PageSetup );
//menuStrip.Items.Add( item );
//item = new ToolStripMenuItem();
//item.Name = "print";
//item.Tag = "print";
//item.Text = _resourceManager.GetString( "print" );
//item.Click += new System.EventHandler( this.MenuClick_Print );
//menuStrip.Items.Add( item );
//item = new ToolStripMenuItem();
//item.Name = "show_val";
//item.Tag = "show_val";
//item.Text = _resourceManager.GetString( "show_val" );
//item.Click += new System.EventHandler( this.MenuClick_ShowValues );
//item.Checked = this.IsShowPointValues;
//menuStrip.Items.Add( item );
//item = new ToolStripMenuItem();
//item.Name = "unzoom";
//item.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 );
//item.Text = menuStr;
//item.Click += new EventHandler( this.MenuClick_ZoomOut );
//if ( pane == null || pane.ZoomStack.IsEmpty )
// item.Enabled = false;
//menuStrip.Items.Add( item );
//item = new ToolStripMenuItem();
//item.Name = "undo_all";
//item.Tag = "undo_all";
//menuStr = _resourceManager.GetString( "undo_all" );
//item.Text = menuStr;
//item.Click += new EventHandler( this.MenuClick_ZoomOutAll );
//if ( pane == null || pane.ZoomStack.IsEmpty )
// item.Enabled = false;
//menuStrip.Items.Add( item );
//item = new ToolStripMenuItem();
//item.Name = "set_default";
//item.Tag = "set_default";
//menuStr = _resourceManager.GetString( "set_default" );
//item.Text = menuStr;
//item.Click += new EventHandler( this.MenuClick_RestoreScale );
//if ( pane == null )
// item.Enabled = false;
//menuStrip.Items.Add( item );
// if e.Cancel is set to false, the context menu does not display
// it is initially set to false because the context menu has no items
e.Cancel = false;
// Provide Callback for User to edit the context menu
//Revision: JCarpenter 10/06 - add ContextMenuObjectState objState
if (this.ContextMenuBuilder != null)
this.ContextMenuBuilder(this, menuStrip, _menuClickPt, objState);
}
}
}
///
/// Handler for the "Copy" context menu item. Copies the current image to a bitmap on the
/// clipboard.
///
///
///
protected void MenuClick_Copy(System.Object sender, System.EventArgs e)
{
Copy(_isShowCopyMessage);
}
///
/// Handler for the "Copy" context menu item. Copies the current image to a bitmap on the
/// clipboard.
///
/// boolean value that determines whether or not a prompt will be
/// displayed. true to show a message of "Image Copied to ClipBoard".
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.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);
}
}
}
///
/// A threaded version of the copy method to avoid crash with MTA
///
private void ClipboardCopyThread()
{
Clipboard.SetDataObject(_masterPane.GetImage(), true);
}
///
/// Handler for the "Save Image As" context menu item. Copies the current image to the selected
/// file.
///
///
///
protected void MenuClick_SaveAs(System.Object sender, System.EventArgs e)
{
SaveAs();
}
///
/// 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.
///
///
/// Note that and methods are provided
/// which allow for Bitmap-only or Emf-only handling of the "Save As" context menu item.
///
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();
}
}
}
}
}
///
/// Handler for the "Save Image As" context menu item. Copies the current image to the selected
/// Bitmap file.
///
///
/// Note that this handler saves as a bitmap only. The default handler is
/// , which allows for Bitmap or EMF formats
///
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();
}
}
}
}
///
/// Handler for the "Save Image As" context menu item. Copies the current image to the selected
/// Emf format file.
///
///
/// Note that this handler saves as an Emf format only. The default handler is
/// , which allows for Bitmap or EMF formats.
///
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);
}
}
}
}
///
/// Handler for the "Show Values" context menu item. Toggles the
/// property, which activates the point value tooltips.
///
///
///
protected void MenuClick_ShowValues(object sender, System.EventArgs e)
{
ToolStripMenuItem item = sender as ToolStripMenuItem;
if (item != null)
this.IsShowPointValues = !item.Checked;
}
///
/// Handler for the "Set Scale to Default" context menu item. Sets the scale ranging to
/// full auto mode for all axes.
///
///
/// This method differs from the method in that it sets the scales
/// to full auto mode. The method sets the scales to their initial
/// setting prior to any user actions (which may or may not be full auto mode).
///
///
///
protected void MenuClick_RestoreScale(object sender, EventArgs e)
{
if (_masterPane != null)
{
GraphPane pane = _masterPane.FindPane(_menuClickPt);
RestoreScale(pane);
}
}
///
/// Handler for the "Set Scale to Default" context menu item. Sets the scale ranging to
/// full auto mode for all axes.
///
///
/// This method differs from the method in that it sets the scales
/// to full auto mode. The method sets the scales to their initial
/// setting prior to any user actions (which may or may not be full auto mode).
///
/// The object which is to have the
/// scale restored
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);
pane.X2Axis.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();
}
}
*/
///
/// Handler for the "UnZoom/UnPan" context menu item. Restores the scale ranges to the values
/// before the last zoom or pan operation.
///
///
///
protected void MenuClick_ZoomOut(System.Object sender, System.EventArgs e)
{
if (_masterPane != null)
{
GraphPane pane = _masterPane.FindPane(_menuClickPt);
ZoomOut(pane);
}
}
///
/// Handler for the "UnZoom/UnPan" context menu item. Restores the scale ranges to the values
/// before the last zoom, pan, or scroll operation.
///
///
/// Triggers a for any type of undo (including pan, scroll, zoom, and
/// wheelzoom). This method will affect all the
/// objects in the if
/// or is true.
///
/// The primary object which is to be
/// zoomed out
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();
}
}
///
/// Handler for the "Undo All Zoom/Pan" context menu item. Restores the scale ranges to the values
/// before all zoom and pan operations
///
///
/// This method differs from the method in that it sets the scales
/// to their initial setting prior to any user actions. The method
/// sets the scales to full auto mode (regardless of what the initial setting may have been).
///
///
///
protected void MenuClick_ZoomOutAll(System.Object sender, System.EventArgs e)
{
if (_masterPane != null)
{
GraphPane pane = _masterPane.FindPane(_menuClickPt);
ZoomOutAll(pane);
}
}
///
/// Handler for the "Undo All Zoom/Pan" context menu item. Restores the scale ranges to the values
/// before all zoom and pan operations
///
///
/// This method differs from the method in that it sets the scales
/// to their initial setting prior to any user actions. The method
/// sets the scales to full auto mode (regardless of what the initial setting may have been).
///
/// The object which is to be zoomed out
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
///
/// Handler for the "Page Setup..." context menu item. Displays a
/// .
///
///
///
protected void MenuClick_PageSetup(object sender, EventArgs e)
{
DoPageSetup();
}
///
/// Handler for the "Print..." context menu item. Displays a
/// .
///
///
///
protected void MenuClick_Print(object sender, EventArgs e)
{
DoPrint();
}
///
/// Rendering method used by the print context menu items
///
/// The applicable .
/// A instance providing
/// page bounds, margins, and a Graphics instance for this printed output.
///
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];
}
}
///
/// Gets or sets the instance
/// that is used for all of the context menu printing functions.
///
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; }
}
///
/// Display a to the user, allowing them to modify
/// the print settings for this .
///
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);
}
}
///
/// Display a to the user, allowing them to select a
/// printer and print the contained in this
/// .
///
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);
}
}
///
/// Display a , allowing the user to preview and
/// subsequently print the contained in this
/// .
///
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(this);
}
}
catch (Exception exception)
{
MessageBox.Show(exception.Message);
}
}
#endregion
#region Zoom States
///
/// Save the current states of the GraphPanes to a separate collection. Save a single
/// () GraphPane if the panes are not synchronized
/// (see and ),
/// or save a list of states for all GraphPanes if the panes are synchronized.
///
/// The primary GraphPane on which zoom/pan/scroll operations
/// are taking place
/// The that describes the
/// current operation
/// The that corresponds to the
/// .
///
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;
}
///
/// Restore the states of the GraphPanes to a previously saved condition (via
/// . This is essentially an "undo" for live
/// pan and scroll actions. Restores a single
/// () GraphPane if the panes are not synchronized
/// (see and ),
/// or save a list of states for all GraphPanes if the panes are synchronized.
///
/// The primary GraphPane on which zoom/pan/scroll operations
/// are taking place
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();
}
///
/// Place the previously saved states of the GraphPanes on the individual GraphPane
/// collections. This provides for an
/// option to undo the state change at a later time. Save a single
/// () GraphPane if the panes are not synchronized
/// (see and ),
/// or save a list of states for all GraphPanes if the panes are synchronized.
///
/// The primary GraphPane on which zoom/pan/scroll operations
/// are taking place
/// The that corresponds to the
/// .
///
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();
}
///
/// Clear the collection of saved states.
///
private void ZoomStateClear()
{
_zoomStateStack.Clear();
_zoomState = null;
}
///
/// Clear all states from the undo stack for each GraphPane.
///
private void ZoomStatePurge()
{
foreach (GraphPane pane in _masterPane._paneList)
pane.ZoomStack.Clear();
}
#endregion
}
}