AIMS/DrawGraph/ZedGraphControl.cs
2022-08-23 21:12:59 +08:00

4624 lines
198 KiB
C#
Raw Blame History

//============================================================================
//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
{
/*
/// <summary>
///
/// </summary>
public struct DrawingThreadData
{
/// <summary>
///
/// </summary>
public Graphics _g;
/// <summary>
///
/// </summary>
public MasterPane _masterPane;
// public DrawingThread( Graphics g, MasterPane masterPane )
// {
// _g = g;
// _masterPane = masterPane;
// }
}
*/
/// <summary>
/// The ZedGraphControl class provides a UserControl interface to the
/// <see cref="ZedGraph"/> class library. This allows ZedGraph to be installed
/// as a control in the Visual Studio toolbox. You can use the control by simply
/// dragging it onto a form in the Visual Studio form editor. All graph
/// attributes are accessible via the <see cref="ZedGraphControl.GraphPane"/>
/// property.
/// </summary>
/// <author> John Champion revised by Jerry Vos </author>
/// <version> $Revision: 3.82 $ $Date: 2007/04/16 00:03:06 $ </version>
public partial class ZedGraphControl : UserControl
{
#region Private Fields
/// <summary>
/// This private field contains the instance for the MasterPane object of this control.
/// You can access the MasterPane object through the public property
/// <see cref="ZedGraphControl.MasterPane"/>. This is nulled when this Control is
/// disposed.
/// </summary>
private MasterPane _masterPane;
/// <summary>
/// private field that determines if anti-aliased drawing will be forced on. Use the
/// public property <see cref="ZedGraphControl.IsAntiAlias"/> to access this value.
/// </summary>
private bool _isAntiAlias = false;
/// <summary>
/// private field that determines whether or not tooltips will be displayed
/// when the mouse hovers over data values. Use the public property
/// <see cref="IsShowPointValues"/> to access this value.
/// </summary>
private bool _isShowPointValues = false;
/// <summary>
/// private field that determines whether or not tooltips will be displayed
/// showing the scale values while the mouse is located within the ChartRect.
/// Use the public property <see cref="IsShowCursorValues"/> to access this value.
/// </summary>
private bool _isShowCursorValues = false;
/// <summary>
/// private field that determines the format for displaying tooltip values.
/// This format is passed to <see cref="PointPairBase.ToString(string)"/>.
/// Use the public property <see cref="PointValueFormat"/> to access this
/// value.
/// </summary>
private string _pointValueFormat = PointPair.DefaultFormat;
/// <summary>
/// private field that determines whether or not the context menu will be available. Use the
/// public property <see cref="IsShowContextMenu"/> to access this value.
/// </summary>
private bool _isShowContextMenu = true;
/// <summary>
/// private field that determines whether or not a message box will be shown in response to
/// a context menu "Copy" command. Use the
/// public property <see cref="IsShowCopyMessage"/> to access this value.
/// </summary>
/// <remarks>
/// Note that, if this value is set to false, the user will receive no indicative feedback
/// in response to a Copy action.
/// </remarks>
private bool _isShowCopyMessage = true;
private SaveFileDialog _saveFileDialog = new SaveFileDialog();
/// <summary>
/// private field that determines whether the settings of
/// <see cref="ZedGraph.PaneBase.IsFontsScaled" /> and <see cref="PaneBase.IsPenWidthScaled" />
/// will be overridden to true during printing operations.
/// </summary>
/// <remarks>
/// Printing involves pixel maps that are typically of a dramatically different dimension
/// than on-screen pixel maps. Therefore, it becomes more important to scale the fonts and
/// lines to give a printed image that looks like what is shown on-screen. The default
/// setting for <see cref="ZedGraph.PaneBase.IsFontsScaled" /> is true, but the default
/// setting for <see cref="PaneBase.IsPenWidthScaled" /> is false.
/// </remarks>
/// <value>
/// A value of true will cause both <see cref="ZedGraph.PaneBase.IsFontsScaled" /> and
/// <see cref="PaneBase.IsPenWidthScaled" /> to be temporarily set to true during
/// printing operations.
/// </value>
private bool _isPrintScaleAll = true;
/// <summary>
/// private field that determines whether or not the visible aspect ratio of the
/// <see cref="MasterPane" /> <see cref="PaneBase.Rect" /> will be preserved
/// when printing this <see cref="ZedGraphControl" />.
/// </summary>
private bool _isPrintKeepAspectRatio = true;
/// <summary>
/// private field that determines whether or not the <see cref="MasterPane" />
/// <see cref="PaneBase.Rect" /> dimensions will be expanded to fill the
/// available space when printing this <see cref="ZedGraphControl" />.
/// </summary>
/// <remarks>
/// If <see cref="IsPrintKeepAspectRatio" /> is also true, then the <see cref="MasterPane" />
/// <see cref="PaneBase.Rect" /> dimensions will be expanded to fit as large
/// a space as possible while still honoring the visible aspect ratio.
/// </remarks>
private bool _isPrintFillPage = true;
/// <summary>
/// private field that determines the format for displaying tooltip date values.
/// This format is passed to <see cref="XDate.ToString(string)"/>.
/// Use the public property <see cref="PointDateFormat"/> to access this
/// value.
/// </summary>
private string _pointDateFormat = XDate.DefaultFormatStr;
/// <summary>
/// private value that determines whether or not zooming is enabled for the control in the
/// vertical direction. Use the public property <see cref="IsEnableVZoom"/> to access this
/// value.
/// </summary>
private bool _isEnableVZoom = true;
/// <summary>
/// private value that determines whether or not zooming is enabled for the control in the
/// horizontal direction. Use the public property <see cref="IsEnableHZoom"/> to access this
/// value.
/// </summary>
private bool _isEnableHZoom = true;
/// <summary>
/// private value that determines whether or not point editing is enabled in the
/// vertical direction. Use the public property <see cref="IsEnableVEdit"/> to access this
/// value.
/// </summary>
private bool _isEnableVEdit = false;
/// <summary>
/// private value that determines whether or not point editing is enabled in the
/// horizontal direction. Use the public property <see cref="IsEnableHEdit"/> to access this
/// value.
/// </summary>
private bool _isEnableHEdit = false;
/// <summary>
/// private value that determines whether or not panning is allowed for the control in the
/// horizontal direction. Use the
/// public property <see cref="IsEnableHPan"/> to access this value.
/// </summary>
private bool _isEnableHPan = true;
/// <summary>
/// private value that determines whether or not panning is allowed for the control in the
/// vertical direction. Use the
/// public property <see cref="IsEnableVPan"/> to access this value.
/// </summary>
private bool _isEnableVPan = true;
// Revision: JCarpenter 10/06
/// <summary>
/// Internal variable that indicates if the control can manage selections.
/// </summary>
private bool _isEnableSelection = false;
private double _zoomStepFraction = 0.1;
private ScrollRange _xScrollRange;
private ScrollRangeList _yScrollRangeList;
private ScrollRangeList _y2ScrollRangeList;
private bool _isShowHScrollBar = false;
private bool _isShowVScrollBar = false;
//private bool isScrollY2 = false;
private bool _isAutoScrollRange = false;
private double _scrollGrace = 0.00; //0.05;
private bool _isSynchronizeXAxes = false;
private bool _isSynchronizeYAxes = false;
//private System.Windows.Forms.HScrollBar hScrollBar1;
//private System.Windows.Forms.VScrollBar vScrollBar1;
// The range of values to use the scroll control bars
private const int _ScrollControlSpan = int.MaxValue;
// The ratio of the largeChange to the smallChange for the scroll bars
private const int _ScrollSmallRatio = 10;
private bool _isZoomOnMouseCenter = false;
private ResourceManager _resourceManager;
/// <summary>
/// private field that stores a <see cref="PrintDocument" /> instance, which maintains
/// a persistent selection of printer options.
/// </summary>
/// <remarks>
/// This is needed so that a "Print" action utilizes the settings from a prior
/// "Page Setup" action.</remarks>
private PrintDocument _pdSave = null;
//private PrinterSettings printSave = null;
//private PageSettings pageSave = null;
/// <summary>
/// This private field contains a list of selected CurveItems.
/// </summary>
//private List<CurveItem> _selection = new List<CurveItem>();
private Selection _selection = new Selection();
#endregion
#region Fields: Buttons & Keys Properties
/// <summary>
/// Gets or sets a value that determines which Mouse button will be used to click on
/// linkable objects
/// </summary>
/// <seealso cref="LinkModifierKeys" />
private MouseButtons _linkButtons = MouseButtons.Left;
/// <summary>
/// Gets or sets a value that determines which modifier keys will be used to click
/// on linkable objects
/// </summary>
/// <seealso cref="LinkButtons" />
private Keys _linkModifierKeys = Keys.Alt;
/// <summary>
/// Gets or sets a value that determines which Mouse button will be used to edit point
/// data values
/// </summary>
/// <remarks>
/// This setting only applies if <see cref="IsEnableHEdit" /> and/or
/// <see cref="IsEnableVEdit" /> are true.
/// </remarks>
/// <seealso cref="EditModifierKeys" />
private MouseButtons _editButtons = MouseButtons.Right;
/// <summary>
/// Gets or sets a value that determines which modifier keys will be used to edit point
/// data values
/// </summary>
/// <remarks>
/// This setting only applies if <see cref="IsEnableHEdit" /> and/or
/// <see cref="IsEnableVEdit" /> are true.
/// </remarks>
/// <seealso cref="EditButtons" />
private Keys _editModifierKeys = Keys.Alt;
/// <summary>
/// Gets or sets a value that determines which mouse button will be used to select
/// <see cref="CurveItem" />'s.
/// </summary>
/// <remarks>
/// This setting only applies if <see cref="IsEnableSelection" /> is true.
/// </remarks>
/// <seealso cref="SelectModifierKeys" />
private MouseButtons _selectButtons = MouseButtons.Left;
/// <summary>
/// Gets or sets a value that determines which modifier keys will be used to select
/// <see cref="CurveItem" />'s.
/// </summary>
/// <remarks>
/// This setting only applies if <see cref="IsEnableSelection" /> is true.
/// </remarks>
/// <seealso cref="SelectButtons" />
private Keys _selectModifierKeys = Keys.Shift;
private Keys _selectAppendModifierKeys = Keys.Shift | Keys.Control;
/// <summary>
/// Gets or sets a value that determines which Mouse button will be used to perform
/// zoom operations
/// </summary>
/// <remarks>
/// This setting only applies if <see cref="IsEnableHZoom" /> and/or
/// <see cref="IsEnableVZoom" /> are true.
/// </remarks>
/// <seealso cref="ZoomModifierKeys" />
/// <seealso cref="ZoomButtons2" />
/// <seealso cref="ZoomModifierKeys2" />
private MouseButtons _zoomButtons = MouseButtons.Left;
/// <summary>
/// Gets or sets a value that determines which modifier keys will be used to perform
/// zoom operations
/// </summary>
/// <remarks>
/// This setting only applies if <see cref="IsEnableHZoom" /> and/or
/// <see cref="IsEnableVZoom" /> are true.
/// </remarks>
/// <seealso cref="ZoomButtons" />
/// <seealso cref="ZoomButtons2" />
/// <seealso cref="ZoomModifierKeys2" />
private Keys _zoomModifierKeys = Keys.None;
/// <summary>
/// Gets or sets a value that determines which Mouse button will be used as a
/// secondary option to perform zoom operations
/// </summary>
/// <remarks>
/// This setting only applies if <see cref="IsEnableHZoom" /> and/or
/// <see cref="IsEnableVZoom" /> are true.
/// </remarks>
/// <seealso cref="ZoomModifierKeys2" />
/// <seealso cref="ZoomButtons" />
/// <seealso cref="ZoomModifierKeys" />
private MouseButtons _zoomButtons2 = MouseButtons.None;
/// <summary>
/// Gets or sets a value that determines which modifier keys will be used as a
/// secondary option to perform zoom operations
/// </summary>
/// <remarks>
/// This setting only applies if <see cref="IsEnableHZoom" /> and/or
/// <see cref="IsEnableVZoom" /> are true.
/// </remarks>
/// <seealso cref="ZoomButtons" />
/// <seealso cref="ZoomButtons2" />
/// <seealso cref="ZoomModifierKeys2" />
private Keys _zoomModifierKeys2 = Keys.None;
/// <summary>
/// Gets or sets a value that determines which Mouse button will be used to perform
/// panning operations
/// </summary>
/// <remarks>
/// This setting only applies if <see cref="IsEnableHPan" /> and/or
/// <see cref="IsEnableVPan" /> are true. A Pan operation (dragging the graph with
/// the mouse) should not be confused with a scroll operation (using a scroll bar to
/// move the graph).
/// </remarks>
/// <seealso cref="PanModifierKeys" />
/// <seealso cref="PanButtons2" />
/// <seealso cref="PanModifierKeys2" />
private MouseButtons _panButtons = MouseButtons.Left;
// Setting this field to Keys.Shift here
// causes an apparent bug to crop up in VS 2003, by which it will have the value:
// "System.Windows.Forms.Keys.Shift+None", which won't compile
/// <summary>
/// Gets or sets a value that determines which modifier keys will be used to perform
/// panning operations
/// </summary>
/// <remarks>
/// This setting only applies if <see cref="IsEnableHPan" /> and/or
/// <see cref="IsEnableVPan" /> are true. A Pan operation (dragging the graph with
/// the mouse) should not be confused with a scroll operation (using a scroll bar to
/// move the graph).
/// </remarks>
/// <seealso cref="PanButtons" />
/// <seealso cref="PanButtons2" />
/// <seealso cref="PanModifierKeys2" />
private Keys _panModifierKeys = Keys.Control;
/// <summary>
/// Gets or sets a value that determines which Mouse button will be used as a
/// secondary option to perform panning operations
/// </summary>
/// <remarks>
/// This setting only applies if <see cref="IsEnableHPan" /> and/or
/// <see cref="IsEnableVPan" /> are true. A Pan operation (dragging the graph with
/// the mouse) should not be confused with a scroll operation (using a scroll bar to
/// move the graph).
/// </remarks>
/// <seealso cref="PanModifierKeys2" />
/// <seealso cref="PanButtons" />
/// <seealso cref="PanModifierKeys" />
private MouseButtons _panButtons2 = MouseButtons.Middle;
// Setting this field to Keys.Shift here
// causes an apparent bug to crop up in VS 2003, by which it will have the value:
// "System.Windows.Forms.Keys.Shift+None", which won't compile
/// <summary>
/// Gets or sets a value that determines which modifier keys will be used as a
/// secondary option to perform panning operations
/// </summary>
/// <remarks>
/// This setting only applies if <see cref="IsEnableHPan" /> and/or
/// <see cref="IsEnableVPan" /> are true. A Pan operation (dragging the graph with
/// the mouse) should not be confused with a scroll operation (using a scroll bar to
/// move the graph).
/// </remarks>
/// <seealso cref="PanButtons2" />
/// <seealso cref="PanButtons" />
/// <seealso cref="PanModifierKeys" />
private Keys _panModifierKeys2 = Keys.None;
#endregion
#region Button and Key Properties
/// <summary>
/// Gets or sets a value that determines which mouse button will be used as a primary option
/// to trigger a zoom event.
/// </summary>
/// <remarks>
/// This value is combined with <see cref="ZoomModifierKeys"/> to determine the actual zoom combination.
/// A secondary zoom button/key combination option is available via <see cref="ZoomButtons2"/> and
/// <see cref="ZoomModifierKeys2"/>. To not use this button/key combination, set the value
/// of <see cref="ZoomButtons"/> to <see cref="MouseButtons.None"/>.
/// </remarks>
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(MouseButtons.Left),
Description("Determines which mouse button is used as the primary for zooming")]
public MouseButtons ZoomButtons
{
get { return _zoomButtons; }
set { _zoomButtons = value; }
}
/// <summary>
/// Gets or sets a value that determines which mouse button will be used as the secondary option
/// to trigger a zoom event.
/// </summary>
/// <remarks>
/// This value is combined with <see cref="ZoomModifierKeys2"/> to determine the actual zoom combination.
/// The primary zoom button/key combination option is available via <see cref="ZoomButtons"/> and
/// <see cref="ZoomModifierKeys"/>. To not use this button/key combination, set the value
/// of <see cref="ZoomButtons2"/> to <see cref="MouseButtons.None"/>.
/// </remarks>
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(MouseButtons.None),
Description("Determines which mouse button is used as the secondary for zooming")]
public MouseButtons ZoomButtons2
{
get { return _zoomButtons2; }
set { _zoomButtons2 = value; }
}
/// <summary>
/// Gets or sets a value that determines which modifier keys will be used as a primary option
/// to trigger a zoom event.
/// </summary>
/// <remarks>
/// This value is combined with <see cref="ZoomButtons"/> to determine the actual zoom combination.
/// A secondary zoom button/key combination option is available via <see cref="ZoomButtons2"/> and
/// <see cref="ZoomModifierKeys2"/>. To not use this button/key combination, set the value
/// of <see cref="ZoomButtons"/> to <see cref="MouseButtons.None"/>.
/// </remarks>
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(Keys.None),
Description("Determines which modifier key used as the primary for zooming")]
public Keys ZoomModifierKeys
{
get { return _zoomModifierKeys; }
set { _zoomModifierKeys = value; }
}
/// <summary>
/// Gets or sets a value that determines which modifier keys will be used as a secondary option
/// to trigger a zoom event.
/// </summary>
/// <remarks>
/// This value is combined with <see cref="ZoomButtons2"/> to determine the actual zoom combination.
/// A primary zoom button/key combination option is available via <see cref="ZoomButtons"/> and
/// <see cref="ZoomModifierKeys"/>. To not use this button/key combination, set the value
/// of <see cref="ZoomButtons2"/> to <see cref="MouseButtons.None"/>.
/// </remarks>
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(Keys.None),
Description("Determines which modifier key used as the secondary for zooming")]
public Keys ZoomModifierKeys2
{
get { return _zoomModifierKeys2; }
set { _zoomModifierKeys2 = value; }
}
/// <summary>
/// Gets or sets a value that determines which mouse button will be used as a primary option
/// to trigger a pan event.
/// </summary>
/// <remarks>
/// This value is combined with <see cref="PanModifierKeys"/> to determine the actual pan combination.
/// A secondary pan button/key combination option is available via <see cref="PanButtons2"/> and
/// <see cref="PanModifierKeys2"/>. To not use this button/key combination, set the value
/// of <see cref="PanButtons"/> to <see cref="MouseButtons.None"/>.
/// </remarks>
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(MouseButtons.Left),
Description("Determines which mouse button is used as the primary for panning")]
public MouseButtons PanButtons
{
get { return _panButtons; }
set { _panButtons = value; }
}
/// <summary>
/// Gets or sets a value that determines which mouse button will be used as the secondary option
/// to trigger a pan event.
/// </summary>
/// <remarks>
/// This value is combined with <see cref="PanModifierKeys2"/> to determine the actual pan combination.
/// The primary pan button/key combination option is available via <see cref="PanButtons"/> and
/// <see cref="PanModifierKeys"/>. To not use this button/key combination, set the value
/// of <see cref="PanButtons2"/> to <see cref="MouseButtons.None"/>.
/// </remarks>
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(MouseButtons.Middle),
Description("Determines which mouse button is used as the secondary for panning")]
public MouseButtons PanButtons2
{
get { return _panButtons2; }
set { _panButtons2 = value; }
}
// NOTE: The default value of PanModifierKeys is Keys.Shift. Because of an apparent bug in
// VS 2003, the initial value set in InitializeComponent by the code wizard is "Keys.Shift+None"
// which will not compile. As a temporary workaround, I've hidden the value so that it won't
// have compile errors. This problem does not exist in VS 2005.
/// <summary>
/// Gets or sets a value that determines which modifier keys will be used as a primary option
/// to trigger a pan event.
/// </summary>
/// <remarks>
/// This value is combined with <see cref="PanButtons"/> to determine the actual pan combination.
/// A secondary pan button/key combination option is available via <see cref="PanButtons2"/> and
/// <see cref="PanModifierKeys2"/>. To not use this button/key combination, set the value
/// of <see cref="PanButtons"/> to <see cref="MouseButtons.None"/>.
/// </remarks>
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(Keys.Control),
Description("Determines which modifier key is used as the primary for panning")]
public Keys PanModifierKeys
{
get { return _panModifierKeys; }
set { _panModifierKeys = value; }
}
/// <summary>
/// Gets or sets a value that determines which modifier keys will be used as a secondary option
/// to trigger a pan event.
/// </summary>
/// <remarks>
/// This value is combined with <see cref="PanButtons2"/> to determine the actual pan combination.
/// A primary pan button/key combination option is available via <see cref="PanButtons"/> and
/// <see cref="PanModifierKeys"/>. To not use this button/key combination, set the value
/// of <see cref="PanButtons2"/> to <see cref="MouseButtons.None"/>.
/// </remarks>
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(Keys.None),
Description("Determines which modifier key is used as the secondary for panning")]
public Keys PanModifierKeys2
{
get { return _panModifierKeys2; }
set { _panModifierKeys2 = value; }
}
/// <summary>
/// Gets or sets a value that determines which Mouse button will be used to edit point
/// data values
/// </summary>
/// <remarks>
/// This setting only applies if <see cref="IsEnableHEdit" /> and/or
/// <see cref="IsEnableVEdit" /> are true.
/// </remarks>
/// <seealso cref="EditModifierKeys" />
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(MouseButtons.Right),
Description("Specify mouse button for point editing")]
public MouseButtons EditButtons
{
get { return _editButtons; }
set { _editButtons = value; }
}
/// <summary>
/// Gets or sets a value that determines which modifier keys will be used to edit point
/// data values
/// </summary>
/// <remarks>
/// This setting only applies if <see cref="IsEnableHEdit" /> and/or
/// <see cref="IsEnableVEdit" /> are true.
/// </remarks>
/// <seealso cref="EditButtons" />
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(Keys.Alt),
Description("Specify modifier key for point editing")]
public Keys EditModifierKeys
{
get { return _editModifierKeys; }
set { _editModifierKeys = value; }
}
/// <summary>
/// Gets or sets a value that determines which Mouse button will be used to
/// select <see cref="CurveItem" />'s.
/// </summary>
/// <remarks>
/// This setting only applies if <see cref="IsEnableSelection" /> is true.
/// </remarks>
/// <seealso cref="SelectModifierKeys" />
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(MouseButtons.Left),
Description("Specify mouse button for curve selection")]
public MouseButtons SelectButtons
{
get { return _selectButtons; }
set { _selectButtons = value; }
}
/// <summary>
/// Gets or sets a value that determines which Modifier keys will be used to
/// select <see cref="CurveItem" />'s.
/// </summary>
/// <remarks>
/// This setting only applies if <see cref="IsEnableSelection" /> is true.
/// </remarks>
/// <seealso cref="SelectButtons" />
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(Keys.Shift),
Description("Specify modifier key for curve selection")]
public Keys SelectModifierKeys
{
get { return _selectModifierKeys; }
set { _selectModifierKeys = value; }
}
/// <summary>
/// Gets or sets a value that determines which Modifier keys will be used to
/// append a <see cref="CurveItem" /> to the selection list.
/// </summary>
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(Keys.Shift | Keys.Alt),
Description("Specify modifier key for append curve selection")]
public Keys SelectAppendModifierKeys
{
get { return _selectAppendModifierKeys; }
}
/// <summary>
/// Gets or sets a value that determines which Mouse button will be used to click
/// on linkable objects
/// </summary>
/// <seealso cref="LinkModifierKeys" />
/// <seealso cref="LinkEvent"/>
// /// <seealso cref="ZedGraph.Web.IsImageMap"/>
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(MouseButtons.Left),
Description("Specify mouse button for clicking on linkable objects")]
public MouseButtons LinkButtons
{
get { return _linkButtons; }
set { _linkButtons = value; }
}
/// <summary>
/// Gets or sets a value that determines which modifier keys will be used to click
/// on linkable objects
/// </summary>
/// <seealso cref="LinkButtons" />
/// <seealso cref="LinkEvent"/>
// /// <seealso cref="ZedGraph.Web.IsImageMap"/>
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(Keys.Alt),
Description("Specify modifier key for clicking on linkable objects")]
public Keys LinkModifierKeys
{
get { return _linkModifierKeys; }
set { _linkModifierKeys = value; }
}
#endregion
#region Fields: Temporary state variables
/// <summary>
/// Internal variable that indicates the control is currently being zoomed.
/// </summary>
private bool _isZooming = false;
/// <summary>
/// Internal variable that indicates the control is currently being panned.
/// </summary>
private bool _isPanning = false;
/// <summary>
/// Internal variable that indicates a point value is currently being edited.
/// </summary>
private bool _isEditing = false;
// Revision: JCarpenter 10/06
/// <summary>
/// Internal variable that indicates the control is currently using selection.
/// </summary>
private bool _isSelecting = false;
/// <summary>
/// Internal variable that stores the <see cref="GraphPane"/> reference for the Pane that is
/// currently being zoomed or panned.
/// </summary>
private GraphPane _dragPane = null;
/// <summary>
/// Internal variable that stores a rectangle which is either the zoom rectangle, or the incremental
/// pan amount since the last mousemove event.
/// </summary>
private Point _dragStartPt;
private Point _dragEndPt;
private int _dragIndex;
private CurveItem _dragCurve;
private PointPair _dragStartPair;
/// <summary>
/// private field that stores the state of the scale ranges prior to starting a panning action.
/// </summary>
private ZoomState _zoomState;
private ZoomStateStack _zoomStateStack;
//temporarily save the location of a context menu click so we can use it for reference
// Note that Control.MousePosition ends up returning the position after the mouse has
// moved to the menu item within the context menu. Therefore, this point is saved so
// that we have the point at which the context menu was first right-clicked
internal Point _menuClickPt;
#endregion
#region Events
/// <summary>
/// A delegate that allows subscribing methods to append or modify the context menu.
/// </summary>
/// <param name="sender">The source <see cref="ZedGraphControl"/> object</param>
/// <param name="menuStrip">A reference to the <see cref="ContextMenuStrip"/> object
/// that contains the context menu.
/// </param>
/// <param name="mousePt">The point at which the mouse was clicked</param>
/// <param name="objState">The current context menu state</param>
/// <seealso cref="ContextMenuBuilder" />
public delegate void ContextMenuBuilderEventHandler(ZedGraphControl sender,
ContextMenuStrip menuStrip, Point mousePt, ContextMenuObjectState objState);
/// <summary>
/// Subscribe to this event to be able to modify the ZedGraph context menu.
/// </summary>
/// <remarks>
/// The context menu is built on the fly after a right mouse click. You can add menu items
/// to this menu by simply modifying the <see paramref="menu"/> parameter.
/// </remarks>
[Bindable(true), Category("Events"),
Description("Subscribe to this event to be able to modify the ZedGraph context menu")]
public event ContextMenuBuilderEventHandler ContextMenuBuilder;
/// <summary>
/// A delegate that allows notification of zoom and pan events.
/// </summary>
/// <param name="sender">The source <see cref="ZedGraphControl"/> object</param>
/// <param name="oldState">A <see cref="ZoomState"/> object that corresponds to the state of the
/// <see cref="GraphPane"/> before the zoom or pan event.</param>
/// <param name="newState">A <see cref="ZoomState"/> object that corresponds to the state of the
/// <see cref="GraphPane"/> after the zoom or pan event</param>
/// <seealso cref="ZoomEvent" />
public delegate void ZoomEventHandler(ZedGraphControl sender, ZoomState oldState,
ZoomState newState);
/// <summary>
/// Subscribe to this event to be notified when the <see cref="GraphPane"/> is zoomed or panned by the user,
/// either via a mouse drag operation or by the context menu commands.
/// </summary>
[Bindable(true), Category("Events"),
Description("Subscribe to this event to be notified when the graph is zoomed or panned")]
public event ZoomEventHandler ZoomEvent;
/// <summary>
/// A delegate that allows notification of scroll events.
/// </summary>
/// <param name="sender">The source <see cref="ZedGraphControl"/> object</param>
/// <param name="scrollBar">The source <see cref="ScrollBar"/> object</param>
/// <param name="oldState">A <see cref="ZoomState"/> object that corresponds to the state of the
/// <see cref="GraphPane"/> before the scroll event.</param>
/// <param name="newState">A <see cref="ZoomState"/> object that corresponds to the state of the
/// <see cref="GraphPane"/> after the scroll event</param>
/// <seealso cref="ZoomEvent" />
public delegate void ScrollDoneHandler(ZedGraphControl sender, ScrollBar scrollBar,
ZoomState oldState, ZoomState newState);
/// <summary>
/// Subscribe to this event to be notified when the <see cref="GraphPane"/> is scrolled by the user
/// using the scrollbars.
/// </summary>
[Bindable(true), Category("Events"),
Description("Subscribe this event to be notified when a scroll operation using the scrollbars is completed")]
public event ScrollDoneHandler ScrollDoneEvent;
/// <summary>
/// A delegate that allows notification of scroll events.
/// </summary>
/// <param name="sender">The source <see cref="ZedGraphControl"/> object</param>
/// <param name="scrollBar">The source <see cref="ScrollBar"/> object</param>
/// <param name="oldState">A <see cref="ZoomState"/> object that corresponds to the state of the
/// <see cref="GraphPane"/> before the scroll event.</param>
/// <param name="newState">A <see cref="ZoomState"/> object that corresponds to the state of the
/// <see cref="GraphPane"/> after the scroll event</param>
/// <seealso cref="ZoomEvent" />
public delegate void ScrollProgressHandler(ZedGraphControl sender, ScrollBar scrollBar,
ZoomState oldState, ZoomState newState);
/// <summary>
/// Subscribe to this event to be notified when the <see cref="GraphPane"/> is scrolled by the user
/// using the scrollbars.
/// </summary>
[Bindable(true), Category("Events"),
Description("Subscribe this event to be notified continuously as a scroll operation is taking place")]
public event ScrollProgressHandler ScrollProgressEvent;
/// <summary>
/// A delegate that receives notification after a point-edit operation is completed.
/// </summary>
/// <param name="sender">The source <see cref="ZedGraphControl"/> object</param>
/// <param name="pane">The <see cref="GraphPane"/> object that contains the
/// point that has been edited</param>
/// <param name="curve">The <see cref="CurveItem"/> object that contains the point
/// that has been edited</param>
/// <param name="iPt">The integer index of the edited <see cref="PointPair"/> within the
/// <see cref="IPointList"/> of the selected <see cref="CurveItem"/>
/// </param>
/// <seealso cref="PointValueEvent" />
public delegate string PointEditHandler(ZedGraphControl sender, GraphPane pane,
CurveItem curve, int iPt);
/// <summary>
/// Subscribe to this event to receive notifcation and/or respond after a data
/// point has been edited via <see cref="IsEnableHEdit" /> and <see cref="IsEnableVEdit" />.
/// </summary>
/// <example>
/// <para>To subscribe to this event, use the following in your Form_Load method:</para>
/// <code>zedGraphControl1.PointEditEvent +=
/// new ZedGraphControl.PointEditHandler( MyPointEditHandler );</code>
/// <para>Add this method to your Form1.cs:</para>
/// <code>
/// private string MyPointEditHandler( object sender, GraphPane pane, CurveItem curve, int iPt )
/// {
/// PointPair pt = curve[iPt];
/// return "This value is " + pt.Y.ToString("f2") + " gallons";
/// }</code>
/// </example>
[Bindable(true), Category("Events"),
Description("Subscribe to this event to respond to data point edit actions")]
public event PointEditHandler PointEditEvent;
/// <summary>
/// A delegate that allows custom formatting of the point value tooltips
/// </summary>
/// <param name="sender">The source <see cref="ZedGraphControl"/> object</param>
/// <param name="pane">The <see cref="GraphPane"/> object that contains the point value of interest</param>
/// <param name="curve">The <see cref="CurveItem"/> object that contains the point value of interest</param>
/// <param name="iPt">The integer index of the selected <see cref="PointPair"/> within the
/// <see cref="IPointList"/> of the selected <see cref="CurveItem"/></param>
/// <seealso cref="PointValueEvent" />
public delegate string PointValueHandler(ZedGraphControl sender, GraphPane pane,
CurveItem curve, int iPt);
/// <summary>
/// Subscribe to this event to provide custom formatting for the tooltips
/// </summary>
/// <example>
/// <para>To subscribe to this event, use the following in your FormLoad method:</para>
/// <code>zedGraphControl1.PointValueEvent +=
/// new ZedGraphControl.PointValueHandler( MyPointValueHandler );</code>
/// <para>Add this method to your Form1.cs:</para>
/// <code>
/// private string MyPointValueHandler( object sender, GraphPane pane, CurveItem curve, int iPt )
/// {
/// #region
/// PointPair pt = curve[iPt];
/// return "This value is " + pt.Y.ToString("f2") + " gallons";
/// #endregion
/// }</code>
/// </example>
[Bindable(true), Category("Events"),
Description("Subscribe to this event to provide custom-formatting for data point tooltips")]
public event PointValueHandler PointValueEvent;
/// <summary>
/// A delegate that allows notification of mouse events on Graph objects.
/// </summary>
/// <param name="sender">The source <see cref="ZedGraphControl"/> object</param>
/// <param name="e">A <see cref="MouseEventArgs" /> corresponding to this event</param>
/// <seealso cref="MouseDownEvent" />
/// <returns>
/// Return true if you have handled the mouse event entirely, and you do not
/// want the <see cref="ZedGraphControl"/> to do any further action (e.g., starting
/// a zoom operation). Return false if ZedGraph should go ahead and process the
/// mouse event.
/// </returns>
public delegate bool ZedMouseEventHandler(ZedGraphControl sender, MouseEventArgs e);
/// <summary>
/// Subscribe to this event to provide notification of MouseDown clicks on graph
/// objects
/// </summary>
/// <remarks>
/// This event provides for a notification when the mouse is clicked on an object
/// within any <see cref="GraphPane"/> of the <see cref="MasterPane"/> associated
/// with this <see cref="ZedGraphControl" />. This event will use the
/// <see cref="ZedGraph.MasterPane.FindNearestPaneObject"/> method to determine which object
/// was clicked. The boolean value that you return from this handler determines whether
/// or not the <see cref="ZedGraphControl"/> will do any further handling of the
/// MouseDown event (see <see cref="ZedMouseEventHandler" />). Return true if you have
/// handled the MouseDown event entirely, and you do not
/// want the <see cref="ZedGraphControl"/> to do any further action (e.g., starting
/// a zoom operation). Return false if ZedGraph should go ahead and process the
/// MouseDown event.
/// </remarks>
[Bindable(true), Category("Events"),
Description("Subscribe to be notified when the left mouse button is clicked down")]
public event ZedMouseEventHandler MouseDownEvent;
/// <summary>
/// Hide the standard control MouseDown event so that the ZedGraphControl.MouseDownEvent
/// can be used. This is so that the user must return true/false in order to indicate
/// whether or not we should respond to the event.
/// </summary>
[Bindable(false), Browsable(false)]
public new event MouseEventHandler MouseDown;
/// <summary>
/// Hide the standard control MouseUp event so that the ZedGraphControl.MouseUpEvent
/// can be used. This is so that the user must return true/false in order to indicate
/// whether or not we should respond to the event.
/// </summary>
[Bindable(false), Browsable(false)]
public new event MouseEventHandler MouseUp;
/// <summary>
/// Hide the standard control MouseMove event so that the ZedGraphControl.MouseMoveEvent
/// can be used. This is so that the user must return true/false in order to indicate
/// whether or not we should respond to the event.
/// </summary>
[Bindable(false), Browsable(false)]
private new event MouseEventHandler MouseMove;
/// <summary>
/// Subscribe to this event to provide notification of MouseUp clicks on graph
/// objects
/// </summary>
/// <remarks>
/// This event provides for a notification when the mouse is clicked on an object
/// within any <see cref="GraphPane"/> of the <see cref="MasterPane"/> associated
/// with this <see cref="ZedGraphControl" />. This event will use the
/// <see cref="ZedGraph.MasterPane.FindNearestPaneObject"/> method to determine which object
/// was clicked. The boolean value that you return from this handler determines whether
/// or not the <see cref="ZedGraphControl"/> will do any further handling of the
/// MouseUp event (see <see cref="ZedMouseEventHandler" />). Return true if you have
/// handled the MouseUp event entirely, and you do not
/// want the <see cref="ZedGraphControl"/> to do any further action (e.g., starting
/// a zoom operation). Return false if ZedGraph should go ahead and process the
/// MouseUp event.
/// </remarks>
[Bindable(true), Category("Events"),
Description("Subscribe to be notified when the left mouse button is released")]
public event ZedMouseEventHandler MouseUpEvent;
/// <summary>
/// Subscribe to this event to provide notification of MouseMove events over graph
/// objects
/// </summary>
/// <remarks>
/// This event provides for a notification when the mouse is moving over on the control.
/// The boolean value that you return from this handler determines whether
/// or not the <see cref="ZedGraphControl"/> will do any further handling of the
/// MouseMove event (see <see cref="ZedMouseEventHandler" />). Return true if you
/// have handled the MouseMove event entirely, and you do not
/// want the <see cref="ZedGraphControl"/> to do any further action.
/// Return false if ZedGraph should go ahead and process the MouseMove event.
/// </remarks>
[Bindable(true), Category("Events"),
Description("Subscribe to be notified when the mouse is moved inside the control")]
public event ZedMouseEventHandler MouseMoveEvent;
/// <summary>
/// Subscribe to this event to provide notification of Double Clicks on graph
/// objects
/// </summary>
/// <remarks>
/// This event provides for a notification when the mouse is double-clicked on an object
/// within any <see cref="GraphPane"/> of the <see cref="MasterPane"/> associated
/// with this <see cref="ZedGraphControl" />. This event will use the
/// <see cref="ZedGraph.MasterPane.FindNearestPaneObject"/> method to determine which object
/// was clicked. The boolean value that you return from this handler determines whether
/// or not the <see cref="ZedGraphControl"/> will do any further handling of the
/// DoubleClick event (see <see cref="ZedMouseEventHandler" />). Return true if you have
/// handled the DoubleClick event entirely, and you do not
/// want the <see cref="ZedGraphControl"/> to do any further action.
/// Return false if ZedGraph should go ahead and process the
/// DoubleClick event.
/// </remarks>
[Bindable(true), Category("Events"),
Description("Subscribe to be notified when the left mouse button is double-clicked")]
public event ZedMouseEventHandler DoubleClickEvent;
/// <summary>
/// A delegate that allows notification of clicks on ZedGraph objects that have
/// active links enabled
/// </summary>
/// <param name="sender">The source <see cref="ZedGraphControl"/> object</param>
/// <param name="pane">The source <see cref="GraphPane" /> in which the click
/// occurred.
/// </param>
/// <param name="source">The source object which was clicked. This is typically
/// a type of <see cref="CurveItem" /> if a curve point was clicked, or
/// a type of <see cref="GraphObj" /> if a graph object was clicked.
/// </param>
/// <param name="link">The <see cref="Link" /> object, belonging to
/// <paramref name="source" />, that contains the link information
/// </param>
/// <param name="index">An index value, typically used if a <see cref="CurveItem" />
/// was clicked, indicating the ordinal value of the actual point that was clicked.
/// </param>
/// <returns>
/// Return true if you have handled the LinkEvent entirely, and you do not
/// want the <see cref="ZedGraphControl"/> to do any further action.
/// Return false if ZedGraph should go ahead and process the LinkEvent.
/// </returns>
public delegate bool LinkEventHandler(ZedGraphControl sender, GraphPane pane,
object source, Link link, int index);
/// <summary>
/// Subscribe to this event to be able to respond to mouse clicks within linked
/// objects.
/// </summary>
/// <remarks>
/// Linked objects are typically either <see cref="GraphObj" /> type objects or
/// <see cref="CurveItem" /> type objects. These object types can include
/// hyperlink information allowing for "drill-down" type operation.
/// </remarks>
/// <seealso cref="LinkEventHandler"/>
/// <seealso cref="Link" />
/// <seealso cref="CurveItem.Link">CurveItem.Link</seealso>
/// <seealso cref="GraphObj.Link">GraphObj.Link</seealso>
// /// <seealso cref="ZedGraph.Web.IsImageMap" />
[Bindable(true), Category("Events"),
Description("Subscribe to be notified when a link-enabled item is clicked")]
public event LinkEventHandler LinkEvent;
#endregion
#region Constructors
/// <summary>
/// Default Constructor
/// </summary>
public ZedGraphControl()
{
InitializeComponent();
// These commands do nothing, but they get rid of the compiler warnings for
// unused events
bool b = MouseDown == null || MouseUp == null || MouseMove == null;
// Link in these events from the base class, since we disable them from this class.
base.MouseDown += new System.Windows.Forms.MouseEventHandler(this.ZedGraphControl_MouseDown);
base.MouseUp += new System.Windows.Forms.MouseEventHandler(this.ZedGraphControl_MouseUp);
base.MouseMove += new System.Windows.Forms.MouseEventHandler(this.ZedGraphControl_MouseMove);
//this.MouseWheel += new System.Windows.Forms.MouseEventHandler( this.ZedGraphControl_MouseWheel );
// Use double-buffering for flicker-free updating:
SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint
| ControlStyles.DoubleBuffer | ControlStyles.ResizeRedraw, true);
//isTransparentBackground = false;
//SetStyle( ControlStyles.Opaque, false );
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
//this.BackColor = Color.Transparent;
_resourceManager = new ResourceManager("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();
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if the components should be
/// disposed, false otherwise</param>
protected override void Dispose(bool disposing)
{
lock (this)
{
if (disposing)
{
if (components != null)
components.Dispose();
}
base.Dispose(disposing);
_masterPane = null;
}
}
#endregion
#region Properties
/// <summary>
/// Gets or sets the <see cref="ZedGraph.MasterPane"/> property for the control
/// </summary>
[Bindable(false), Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public MasterPane MasterPane
{
get { lock (this) return _masterPane; }
set { lock (this) _masterPane = value; }
}
// Testing for Designer attribute
/*
Class1 _class1 = null;
[ Bindable( true ), Browsable( true ), Category( "Data" ), NotifyParentProperty( true ),
DesignerSerializationVisibility( DesignerSerializationVisibility.Content ),
Description( "My Class1 Test" )]
public Class1 Class1
{
get { if ( _class1 == null ) _class1 = new Class1(); return _class1; }
set { _class1 = value; }
}
*/
/// <summary>
/// Gets or sets the <see cref="ZedGraph.GraphPane"/> property for the control
/// </summary>
/// <remarks>
/// <see cref="ZedGraphControl"/> actually uses a <see cref="MasterPane"/> object
/// to hold a list of <see cref="GraphPane"/> objects. This property really only
/// accesses the first <see cref="GraphPane"/> in the list. If there is more
/// than one <see cref="GraphPane"/>, use the <see cref="MasterPane"/>
/// indexer property to access any of the <see cref="GraphPane"/> objects.</remarks>
[
Bindable(false), Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
]
//[
// Bindable( true ), Browsable( true ), Category( "Data" ), NotifyParentProperty( true ),
// AttributeProvider( typeof( GraphPane ) ),
// Description("Access to the primary GraphPane object associated with this control")
//]
public GraphPane GraphPane
{
get
{
// Just return the first GraphPane in the list
lock (this)
{
if (_masterPane != null && _masterPane.PaneList.Count > 0)
return _masterPane[0];
else
return null;
}
}
set
{
lock (this)
{
//Clear the list, and replace it with the specified Graphpane
if (_masterPane != null)
{
_masterPane.PaneList.Clear();
_masterPane.Add(value);
}
}
}
}
/// <summary>
/// Gets or sets a value that determines if all drawing operations for this control
/// will be forced to operate in Anti-alias mode. Note that if this value is set to
/// "true", it overrides the setting for sub-objects. Otherwise, the sub-object settings
/// (such as <see cref="FontSpec.IsAntiAlias"/>)
/// will be honored.
/// </summary>
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(false),
Description("true to force all objects to be draw in anti-alias mode")]
public bool IsAntiAlias
{
get { return _isAntiAlias; }
set { _isAntiAlias = value; }
}
/// <summary>
/// Gets or sets a value that determines whether or not tooltips will be displayed
/// when the mouse hovers over data values.
/// </summary>
/// <remarks>The displayed values are taken from <see cref="PointPair.Tag"/>
/// if it is a <see cref="System.String"/> type, or <see cref="PointPairBase.ToString()"/>
/// otherwise (using the <see cref="PointValueFormat" /> as a format string).
/// Additionally, the user can custom format the values using the
/// <see cref="PointValueEvent" /> event. Note that <see cref="IsShowPointValues" />
/// may be overridden by <see cref="IsShowCursorValues" />.
/// </remarks>
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(false),
Description("true to display tooltips when the mouse hovers over data points")]
public bool IsShowPointValues
{
get { return _isShowPointValues; }
set { _isShowPointValues = value; }
}
/// <summary>
/// Gets or sets a value that determines whether or not tooltips will be displayed
/// showing the current scale values when the mouse is within the
/// <see cref="Chart.Rect" />.
/// </summary>
/// <remarks>The displayed values are taken from the current mouse position, and formatted
/// according to <see cref="PointValueFormat" /> and/or <see cref="PointDateFormat" />. If this
/// value is set to true, it overrides the <see cref="IsShowPointValues" /> setting.
/// </remarks>
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(false),
Description("true to display tooltips showing the current mouse position within the Chart area")]
public bool IsShowCursorValues
{
get { return _isShowCursorValues; }
set { _isShowCursorValues = value; }
}
/// <summary>
/// Gets or sets a value that determines whether or not editing of point data is allowed in
/// the horizontal direction.
/// </summary>
/// <remarks>
/// Editing is done by holding down the Alt key, and left-clicking on an individual point of
/// a given <see cref="CurveItem" /> to drag it to a new location. The Mouse and Key
/// combination for this mode are modifiable using <see cref="EditButtons" /> and
/// <see cref="EditModifierKeys" />.
/// </remarks>
/// <seealso cref="EditButtons" />
/// <seealso cref="EditModifierKeys" />
/// <seealso cref="IsEnableVEdit" />
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(false),
Description("true to allow horizontal editing by alt-left-click-drag")]
public bool IsEnableHEdit
{
get { return _isEnableHEdit; }
set { _isEnableHEdit = value; }
}
/// <summary>
/// Gets or sets a value that determines whether or not editing of point data is allowed in
/// the vertical direction.
/// </summary>
/// <remarks>
/// Editing is done by holding down the Alt key, and left-clicking on an individual point of
/// a given <see cref="CurveItem" /> to drag it to a new location. The Mouse and Key
/// combination for this mode are modifiable using <see cref="EditButtons" /> and
/// <see cref="EditModifierKeys" />.
/// </remarks>
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(false),
Description("true to allow vertical editing by alt-left-click-drag")]
public bool IsEnableVEdit
{
get { return _isEnableVEdit; }
set { _isEnableVEdit = value; }
}
/// <summary>
/// Gets or sets a value that determines whether or not zooming is allowed for the control.
/// </summary>
/// <remarks>
/// Zooming is done by left-clicking inside the <see cref="Chart.Rect"/> to drag
/// out a rectangle, indicating the new scale ranges that will be part of the graph.
/// </remarks>
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(true),
Description("true to allow horizontal and vertical zooming by left-click-drag")]
public bool IsEnableZoom
{
set { _isEnableHZoom = value; _isEnableVZoom = value; }
}
/// <summary>
/// Gets or sets a value that determines whether or not zooming is allowed for the control in
/// the horizontal direction.
/// </summary>
/// <remarks>
/// Zooming is done by left-clicking inside the <see cref="Chart.Rect"/> to drag
/// out a rectangle, indicating the new scale ranges that will be part of the graph.
/// </remarks>
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(true),
Description("true to allow horizontal zooming by left-click-drag")]
public bool IsEnableHZoom
{
get { return _isEnableHZoom; }
set { _isEnableHZoom = value; }
}
/// <summary>
/// Gets or sets a value that determines whether or not zooming is allowed for the control in
/// the vertical direction.
/// </summary>
/// <remarks>
/// Zooming is done by left-clicking inside the <see cref="Chart.Rect"/> to drag
/// out a rectangle, indicating the new scale ranges that will be part of the graph.
/// </remarks>
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(true),
Description("true to allow vertical zooming by left-click-drag")]
public bool IsEnableVZoom
{
get { return _isEnableVZoom; }
set { _isEnableVZoom = value; }
}
/// <summary>
/// Gets or sets a value that determines whether or not panning is allowed for the control in
/// the horizontal direction.
/// </summary>
/// <remarks>
/// Panning is done by clicking the middle mouse button (or holding down the shift key
/// while clicking the left mouse button) inside the <see cref="Chart.Rect"/> and
/// dragging the mouse around to shift the scale ranges as desired.
/// </remarks>
/// <seealso cref="IsEnableVPan"/>
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(true),
Description("true to allow horizontal panning by middle-mouse-drag or shift-left-drag")]
public bool IsEnableHPan
{
get { return _isEnableHPan; }
set { _isEnableHPan = value; }
}
/// <summary>
/// Gets or sets a value that determines whether or not panning is allowed for the control in
/// the vertical direction.
/// </summary>
/// <remarks>
/// Panning is done by clicking the middle mouse button (or holding down the shift key
/// while clicking the left mouse button) inside the <see cref="Chart.Rect"/> and
/// dragging the mouse around to shift the scale ranges as desired.
/// </remarks>
/// <seealso cref="IsEnableHPan"/>
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(true),
Description("true to allow vertical panning by middle-mouse-drag or shift-left-drag")]
public bool IsEnableVPan
{
get { return _isEnableVPan; }
set { _isEnableVPan = value; }
}
/// <summary>
/// Gets or sets a value that determines whether or not the context menu will be available.
/// </summary>
/// <remarks>The context menu is a menu that appears when you right-click on the
/// <see cref="ZedGraphControl"/>. It provides options for Zoom, Pan, AutoScale, Clipboard
/// Copy, and toggle <see cref="IsShowPointValues"/>.
/// </remarks>
/// <value>true to allow the context menu, false to disable it</value>
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(true),
Description("true to enable the right mouse button context menu")]
public bool IsShowContextMenu
{
get { return _isShowContextMenu; }
set { _isShowContextMenu = value; }
}
/// <summary>
/// Gets or sets a value that determines whether or not a message box will be shown
/// in response to a context menu "Copy" command.
/// </summary>
/// <remarks>
/// Note that, if this property is set to false, the user will receive no
/// indicative feedback in response to a Copy action.
/// </remarks>
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(true),
Description("true to show a message box after a 'Copy' context menu action completes")]
public bool IsShowCopyMessage
{
get { return _isShowCopyMessage; }
set { _isShowCopyMessage = value; }
}
/// <summary>
/// Gets or sets the <see cref="SaveFileDialog" /> instance that will be used
/// by the "Save As..." context menu item.
/// </summary>
/// <remarks>
/// This provides the opportunity to modify the dialog, such as setting the
/// <see cref="FileDialog.InitialDirectory" /> property.
/// </remarks>
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(true),
Description("Provides access to the SaveFileDialog for the 'Save As' menu item")]
public SaveFileDialog SaveFileDialog
{
get { return _saveFileDialog; }
set { _saveFileDialog = value; }
}
/// <summary>
/// Gets or sets a value that determines whether or not the visible aspect ratio of the
/// <see cref="MasterPane" /> <see cref="PaneBase.Rect" /> will be preserved
/// when printing this <see cref="ZedGraphControl" />.
/// </summary>
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(true),
Description("true to preserve the displayed aspect ratio when printing")]
public bool IsPrintKeepAspectRatio
{
get { return _isPrintKeepAspectRatio; }
set { _isPrintKeepAspectRatio = value; }
}
/// <summary>
/// Gets or sets a value that determines whether or not the <see cref="MasterPane" />
/// <see cref="PaneBase.Rect" /> dimensions will be expanded to fill the
/// available space when printing this <see cref="ZedGraphControl" />.
/// </summary>
/// <remarks>
/// If <see cref="IsPrintKeepAspectRatio" /> is also true, then the <see cref="MasterPane" />
/// <see cref="PaneBase.Rect" /> dimensions will be expanded to fit as large
/// a space as possible while still honoring the visible aspect ratio.
/// </remarks>
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(true),
Description("true to resize to fill the page when printing")]
public bool IsPrintFillPage
{
get { return _isPrintFillPage; }
set { _isPrintFillPage = value; }
}
/// <summary>
/// Gets or sets a value that determines whether the settings of
/// <see cref="ZedGraph.PaneBase.IsFontsScaled" /> and <see cref="PaneBase.IsPenWidthScaled" />
/// will be overridden to true during printing operations.
/// </summary>
/// <remarks>
/// Printing involves pixel maps that are typically of a dramatically different dimension
/// than on-screen pixel maps. Therefore, it becomes more important to scale the fonts and
/// lines to give a printed image that looks like what is shown on-screen. The default
/// setting for <see cref="ZedGraph.PaneBase.IsFontsScaled" /> is true, but the default
/// setting for <see cref="PaneBase.IsPenWidthScaled" /> is false.
/// </remarks>
/// <value>
/// A value of true will cause both <see cref="ZedGraph.PaneBase.IsFontsScaled" /> and
/// <see cref="PaneBase.IsPenWidthScaled" /> to be temporarily set to true during
/// printing operations.
/// </value>
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(true),
Description("true to force font and pen width scaling when printing")]
public bool IsPrintScaleAll
{
get { return _isPrintScaleAll; }
set { _isPrintScaleAll = value; }
}
/// <summary>
/// Gets or sets a value that controls whether or not the axis value range for the scroll
/// bars will be set automatically.
/// </summary>
/// <remarks>
/// If this value is set to true, then the range of the scroll bars will be set automatically
/// to the actual range of the data as returned by <see cref="CurveList.GetRange" /> at the
/// time that <see cref="AxisChange" /> was last called. Note that a value of true
/// can override any setting of <see cref="ScrollMinX" />, <see cref="ScrollMaxX" />,
/// <see cref="ScrollMinY" />, <see cref="ScrollMaxY" />,
/// <see cref="ScrollMinY2" />, and <see cref="ScrollMaxY2" />. Note also that you must
/// call <see cref="AxisChange" /> from the <see cref="ZedGraphControl" /> for this to
/// work properly (e.g., don't call it directly from the <see cref="GraphPane" />.
/// Alternatively, you can call <see cref="SetScrollRangeFromData" /> at anytime to set
/// the scroll bar range.<br />
/// <b>In most cases, you will probably want to disable
/// <see cref="ZedGraph.GraphPane.IsBoundedRanges" /> before activating this option.</b>
/// </remarks>
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(false),
Description("true to automatically set the scroll bar range to the actual data range")]
public bool IsAutoScrollRange
{
get { return _isAutoScrollRange; }
set { _isAutoScrollRange = value; }
}
/// <summary>
/// Set a "grace" value that leaves a buffer area around the data when
/// <see cref="IsAutoScrollRange" /> is true.
/// </summary>
/// <remarks>
/// This value represents a fraction of the total range around each axis. For example, if the
/// axis ranges from 0 to 100, then a 0.05 value for ScrollGrace would set the scroll range
/// to -5 to 105.
/// </remarks>
public double ScrollGrace
{
get { return _scrollGrace; }
set { _scrollGrace = value; }
}
/// <summary>
/// Gets or sets a value that determines if the horizontal scroll bar will be visible.
/// </summary>
/// <remarks>This scroll bar allows the display to be scrolled in the horizontal direction.
/// Another option is display panning, in which the user can move the display around by
/// clicking directly on it and dragging (see <see cref="IsEnableHPan"/> and <see cref="IsEnableVPan"/>).
/// You can control the available range of scrolling with the <see cref="ScrollMinX"/> and
/// <see cref="ScrollMaxX"/> properties. Note that the scroll range can be set automatically by
/// <see cref="IsAutoScrollRange" />.<br />
/// <b>In most cases, you will probably want to disable
/// <see cref="ZedGraph.GraphPane.IsBoundedRanges" /> before activating this option.</b>
/// </remarks>
/// <value>A boolean value. true to display a horizontal scrollbar, false otherwise.</value>
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(false),
Description("true to display the horizontal scroll bar")]
public bool IsShowHScrollBar
{
get { return _isShowHScrollBar; }
set { _isShowHScrollBar = value; ZedGraphControl_ReSize(this, new EventArgs()); }
}
/// <summary>
/// Gets or sets a value that determines if the vertical scroll bar will be visible.
/// </summary>
/// <remarks>This scroll bar allows the display to be scrolled in the vertical direction.
/// Another option is display panning, in which the user can move the display around by
/// clicking directly on it and dragging (see <see cref="IsEnableHPan"/> and <see cref="IsEnableVPan"/>).
/// You can control the available range of scrolling with the <see cref="ScrollMinY"/> and
/// <see cref="ScrollMaxY"/> properties.
/// Note that the vertical scroll bar only affects the <see cref="YAxis"/>; it has no impact on
/// the <see cref="Y2Axis"/>. The panning options affect both the <see cref="YAxis"/> and
/// <see cref="Y2Axis"/>. Note also that the scroll range can be set automatically by
/// <see cref="IsAutoScrollRange" />.<br />
/// <b>In most cases, you will probably want to disable
/// <see cref="ZedGraph.GraphPane.IsBoundedRanges" /> before activating this option.</b>
/// </remarks>
/// <value>A boolean value. true to display a vertical scrollbar, false otherwise.</value>
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(false),
Description("true to display the vertical scroll bar")]
public bool IsShowVScrollBar
{
get { return _isShowVScrollBar; }
set { _isShowVScrollBar = value; ZedGraphControl_ReSize(this, new EventArgs()); }
}
/// <summary>
/// Gets or sets a value that determines if the <see cref="XAxis" /> <see cref="Scale" />
/// ranges for all <see cref="GraphPane" /> objects in the <see cref="MasterPane" /> will
/// be forced to match.
/// </summary>
/// <remarks>
/// If set to true (default is false), then all of the <see cref="GraphPane" /> objects
/// in the <see cref="MasterPane" /> associated with this <see cref="ZedGraphControl" />
/// will be forced to have matching scale ranges for the x axis. That is, zoom, pan,
/// and scroll operations will result in zoom/pan/scroll for all graphpanes simultaneously.
/// </remarks>
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(false),
Description("true to force the X axis ranges for all GraphPanes to match")]
public bool IsSynchronizeXAxes
{
get { return _isSynchronizeXAxes; }
set
{
if (_isSynchronizeXAxes != value)
ZoomStatePurge();
_isSynchronizeXAxes = value;
}
}
/// <summary>
/// Gets or sets a value that determines if the <see cref="YAxis" /> <see cref="Scale" />
/// ranges for all <see cref="GraphPane" /> objects in the <see cref="MasterPane" /> will
/// be forced to match.
/// </summary>
/// <remarks>
/// If set to true (default is false), then all of the <see cref="GraphPane" /> objects
/// in the <see cref="MasterPane" /> associated with this <see cref="ZedGraphControl" />
/// will be forced to have matching scale ranges for the y axis. That is, zoom, pan,
/// and scroll operations will result in zoom/pan/scroll for all graphpanes simultaneously.
/// </remarks>
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(false),
Description("true to force the Y axis ranges for all GraphPanes to match")]
public bool IsSynchronizeYAxes
{
get { return _isSynchronizeYAxes; }
set
{
if (_isSynchronizeYAxes != value)
ZoomStatePurge();
_isSynchronizeYAxes = value;
}
}
/// <summary>
/// Gets or sets a value that determines if the vertical scroll bar will affect the Y2 axis.
/// </summary>
/// <remarks>
/// The vertical scroll bar is automatically associated with the Y axis. With this value, you
/// can choose to include or exclude the Y2 axis with the scrolling. Note that the Y2 axis
/// scrolling is handled as a secondary. The vertical scroll bar position always reflects
/// the status of the Y axis. This can cause the Y2 axis to "jump" when first scrolled if
/// the <see cref="ScrollMinY2" /> and <see cref="ScrollMaxY2" /> values are not set to the
/// same proportions as <see cref="ScrollMinY" /> and <see cref="ScrollMaxY" /> with respect
/// to the actual <see cref="Scale.Min"/> and <see cref="Scale.Max" />. Also note that
/// this property is actually just an alias to the <see cref="ScrollRange.IsScrollable" />
/// property of the first element of <see cref="YScrollRangeList" />.
/// </remarks>
/// <seealso cref="IsShowVScrollBar"/>
/// <seealso cref="ScrollMinY2"/>
/// <seealso cref="ScrollMaxY2"/>
/// <seealso cref="YScrollRangeList" />
/// <seealso cref="Y2ScrollRangeList" />
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(false),
Description("true to scroll the Y2 axis along with the Y axis")]
public bool IsScrollY2
{
get
{
if (_y2ScrollRangeList != null && _y2ScrollRangeList.Count > 0)
return _y2ScrollRangeList[0].IsScrollable;
else
return false;
}
set
{
if (_y2ScrollRangeList != null && _y2ScrollRangeList.Count > 0)
{
ScrollRange tmp = _y2ScrollRangeList[0];
tmp.IsScrollable = value;
_y2ScrollRangeList[0] = tmp;
}
}
}
/// <summary>
/// Access the <see cref="ScrollRangeList" /> for the Y axes.
/// </summary>
/// <remarks>
/// This list maintains the user scale ranges for the scroll bars for each axis
/// in the <see cref="ZedGraph.GraphPane.YAxisList" />. Each ordinal location in
/// <see cref="YScrollRangeList" /> corresponds to an equivalent ordinal location
/// in <see cref="ZedGraph.GraphPane.YAxisList" />.
/// </remarks>
/// <seealso cref="ScrollMinY" />
/// <seealso cref="ScrollMaxY" />
[Bindable(true), Category("Display"), NotifyParentProperty(true)]
[Description("Sets the manual scroll bar ranges for the collection of Y axes")]
public ScrollRangeList YScrollRangeList
{
get { return _yScrollRangeList; }
}
/// <summary>
/// Access the <see cref="ScrollRangeList" /> for the Y2 axes.
/// </summary>
/// <remarks>
/// This list maintains the user scale ranges for the scroll bars for each axis
/// in the <see cref="ZedGraph.GraphPane.Y2AxisList" />. Each ordinal location in
/// <see cref="Y2ScrollRangeList" /> corresponds to an equivalent ordinal location
/// in <see cref="ZedGraph.GraphPane.Y2AxisList" />.
/// </remarks>
/// <seealso cref="ScrollMinY2" />
/// <seealso cref="ScrollMaxY2" />
[Bindable(true), Category("Display"), NotifyParentProperty(true)]
[Description("Sets the manual scroll bar ranges for the collection of Y2 axes")]
public ScrollRangeList Y2ScrollRangeList
{
get { return _y2ScrollRangeList; }
}
/// <summary>
/// The minimum value for the X axis scroll range.
/// </summary>
/// <remarks>
/// Effectively, the minimum endpoint of the scroll range will cause the
/// <see cref="Scale.Min"/> value to be set to <see cref="ScrollMinX"/>. Note that this
/// value applies only to the scroll bar settings. Axis panning (see <see cref="IsEnableHPan"/>)
/// is not affected by this value. Note that this value can be overridden by
/// <see cref="IsAutoScrollRange" /> and <see cref="SetScrollRangeFromData" />.
/// </remarks>
/// <value>A double value indicating the minimum axis value</value>
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(0),
Description("Sets the manual scroll minimum value for the X axis")]
public double ScrollMinX
{
get { return _xScrollRange.Min; }
set { _xScrollRange.Min = value; }
}
/// <summary>
/// The maximum value for the X axis scroll range.
/// </summary>
/// <remarks>
/// Effectively, the maximum endpoint of the scroll range will cause the
/// <see cref="Scale.Max"/> value to be set to <see cref="ScrollMaxX"/>. Note that this
/// value applies only to the scroll bar settings. Axis panning (see <see cref="IsEnableHPan"/>)
/// is not affected by this value. Note that this value can be overridden by
/// <see cref="IsAutoScrollRange" /> and <see cref="SetScrollRangeFromData" />.
/// </remarks>
/// <value>A double value indicating the maximum axis value</value>
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(0),
Description("Sets the manual scroll maximum value for the X axis")]
public double ScrollMaxX
{
get { return _xScrollRange.Max; }
set { _xScrollRange.Max = value; }
}
/// <summary>
/// The minimum value for the Y axis scroll range.
/// </summary>
/// <remarks>
/// Effectively, the minimum endpoint of the scroll range will cause the
/// <see cref="Scale.Min"/> value to be set to <see cref="ScrollMinY"/>. Note that this
/// value applies only to the scroll bar settings. Axis panning (see <see cref="IsEnableVPan"/>)
/// is not affected by this value. Note that this value can be overridden by
/// <see cref="IsAutoScrollRange" /> and <see cref="SetScrollRangeFromData" />. Also note that
/// this property is actually just an alias to the <see cref="ScrollRange.Min" />
/// property of the first element of <see cref="YScrollRangeList" />.
/// </remarks>
/// <value>A double value indicating the minimum axis value</value>
/// <seealso cref="YScrollRangeList" />
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(0),
Description("Sets the manual scroll minimum value for the Y axis")]
public double ScrollMinY
{
get
{
if (_yScrollRangeList != null && _yScrollRangeList.Count > 0)
return _yScrollRangeList[0].Min;
else
return double.NaN;
}
set
{
if (_yScrollRangeList != null && _yScrollRangeList.Count > 0)
{
ScrollRange tmp = _yScrollRangeList[0];
tmp.Min = value;
_yScrollRangeList[0] = tmp;
}
}
}
/// <summary>
/// The maximum value for the Y axis scroll range.
/// </summary>
/// <remarks>
/// Effectively, the maximum endpoint of the scroll range will cause the
/// <see cref="Scale.Max"/> value to be set to <see cref="ScrollMaxY"/>. Note that this
/// value applies only to the scroll bar settings. Axis panning (see <see cref="IsEnableVPan"/>)
/// is not affected by this value. Note that this value can be overridden by
/// <see cref="IsAutoScrollRange" /> and <see cref="SetScrollRangeFromData" />. Also note that
/// this property is actually just an alias to the <see cref="ScrollRange.Max" />
/// property of the first element of <see cref="YScrollRangeList" />.
/// </remarks>
/// <value>A double value indicating the maximum axis value</value>
/// <seealso cref="YScrollRangeList" />
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(0),
Description("Sets the manual scroll maximum value for the Y axis")]
public double ScrollMaxY
{
get
{
if (_yScrollRangeList != null && _yScrollRangeList.Count > 0)
return _yScrollRangeList[0].Max;
else
return double.NaN;
}
set
{
if (_yScrollRangeList != null && _yScrollRangeList.Count > 0)
{
ScrollRange tmp = _yScrollRangeList[0];
tmp.Max = value;
_yScrollRangeList[0] = tmp;
}
}
}
/// <summary>
/// The minimum value for the Y2 axis scroll range.
/// </summary>
/// <remarks>
/// Effectively, the minimum endpoint of the scroll range will cause the
/// <see cref="Scale.Min"/> value to be set to <see cref="ScrollMinY2"/>. Note that this
/// value applies only to the scroll bar settings. Axis panning (see <see cref="IsEnableVPan"/>)
/// is not affected by this value. Note that this value can be overridden by
/// <see cref="IsAutoScrollRange" /> and <see cref="SetScrollRangeFromData" />. Also note that
/// this property is actually just an alias to the <see cref="ScrollRange.Min" />
/// property of the first element of <see cref="Y2ScrollRangeList" />.
/// </remarks>
/// <value>A double value indicating the minimum axis value</value>
/// <seealso cref="Y2ScrollRangeList" />
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(0),
Description("Sets the manual scroll minimum value for the Y2 axis")]
public double ScrollMinY2
{
get
{
if (_y2ScrollRangeList != null && _y2ScrollRangeList.Count > 0)
return _y2ScrollRangeList[0].Min;
else
return double.NaN;
}
set
{
if (_y2ScrollRangeList != null && _y2ScrollRangeList.Count > 0)
{
ScrollRange tmp = _y2ScrollRangeList[0];
tmp.Min = value;
_y2ScrollRangeList[0] = tmp;
}
}
}
/// <summary>
/// The maximum value for the Y2 axis scroll range.
/// </summary>
/// <remarks>
/// Effectively, the maximum endpoint of the scroll range will cause the
/// <see cref="Scale.Max"/> value to be set to <see cref="ScrollMaxY2"/>. Note that this
/// value applies only to the scroll bar settings. Axis panning (see <see cref="IsEnableVPan"/>)
/// is not affected by this value. Note that this value can be overridden by
/// <see cref="IsAutoScrollRange" /> and <see cref="SetScrollRangeFromData" />. Also note that
/// this property is actually just an alias to the <see cref="ScrollRange.Max" />
/// property of the first element of <see cref="Y2ScrollRangeList" />.
/// </remarks>
/// <value>A double value indicating the maximum axis value</value>
/// <seealso cref="Y2ScrollRangeList" />
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(0),
Description("Sets the manual scroll maximum value for the Y2 axis")]
public double ScrollMaxY2
{
get
{
if (_y2ScrollRangeList != null && _y2ScrollRangeList.Count > 0)
return _y2ScrollRangeList[0].Max;
else
return double.NaN;
}
set
{
if (_y2ScrollRangeList != null && _y2ScrollRangeList.Count > 0)
{
ScrollRange tmp = _y2ScrollRangeList[0];
tmp.Max = value;
_y2ScrollRangeList[0] = tmp;
}
}
}
/// <summary>
/// Returns true if the user is currently scrolling via the scrollbar, or
/// false if no scrolling is taking place.
/// </summary>
/// <remarks>
/// This method just tests ScrollBar.Capture to see if the
/// mouse has been captured by the scroll bar. If so, scrolling is active.
/// </remarks>
public Boolean IsScrolling
{
get
{
if (hScrollBar1 != null && vScrollBar1 != null)
return hScrollBar1.Capture || vScrollBar1.Capture;
else
return false;
}
}
/// <summary>
/// Gets or sets the format for displaying tooltip values.
/// This format is passed to <see cref="PointPairBase.ToString(string)"/>.
/// </summary>
/// <remarks>
/// Use the <see cref="System.Globalization.NumberFormatInfo" /> type
/// to determine the format strings.
/// </remarks>
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(PointPair.DefaultFormat),
Description("Sets the numeric display format string for the point value tooltips")]
public string PointValueFormat
{
get { return _pointValueFormat; }
set { _pointValueFormat = value; }
}
/// <summary>
/// Gets or sets the format for displaying tooltip values.
/// This format is passed to <see cref="XDate.ToString(string)"/>.
/// </summary>
/// <remarks>
/// Use the <see cref="System.Globalization.DateTimeFormatInfo" /> type
/// to determine the format strings.
/// </remarks>
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(XDate.DefaultFormatStr),
Description("Sets the date display format for the point value tooltips")]
public string PointDateFormat
{
get { return _pointDateFormat; }
set { _pointDateFormat = value; }
}
/// <summary>
/// Gets or sets the step size fraction for zooming with the mouse wheel.
/// A value of 0.1 will result in a 10% zoom step for each mouse wheel movement.
/// </summary>
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(0.1),
Description("Sets the step size fraction for zooming with the mouse wheel")]
public double ZoomStepFraction
{
get { return _zoomStepFraction; }
set { _zoomStepFraction = value; }
}
/// <summary>
/// Gets or sets a boolean value that determines if zooming with the wheel mouse
/// is centered on the mouse location, or centered on the existing graph.
/// </summary>
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(false),
Description("true to center the mouse wheel zoom at the current mouse location")]
public bool IsZoomOnMouseCenter
{
get { return _isZoomOnMouseCenter; }
set { _isZoomOnMouseCenter = value; }
}
/// <summary>
/// Gets the graph pane's current image.
/// <seealso cref="Bitmap"/>
/// </summary>
/// <exception cref="ZException">
/// When the control has been disposed before this call.
/// </exception>
[Bindable(false), Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Image GetImage()
{
lock (this)
{
if (BeenDisposed || _masterPane == null || _masterPane[0] == null)
throw new ZException("The control has been disposed");
return _masterPane.GetImage();
}
}
/// <summary>
/// This checks if the control has been disposed. This is synonymous with
/// the graph pane having been nulled or disposed. Therefore this is the
/// same as <c>ZedGraphControl.GraphPane == null</c>.
/// </summary>
[Bindable(false), Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public bool BeenDisposed
{
get
{
lock (this) return _masterPane == null;
}
}
// Revision: JCarpenter 10/06
/// <summary>
/// Readonly property that gets the list of selected CurveItems
/// </summary>
public Selection Selection
{
get { return _selection; }
}
/// <summary>
/// Gets or sets a value that determines whether or not selection is allowed for the control.
/// </summary>
[Bindable(true), Category("Display"), NotifyParentProperty(true),
DefaultValue(false),
Description("true to allow selecting Curves")]
public bool IsEnableSelection
{
get { return _isEnableSelection; }
set
{
_isEnableSelection = value;
/*
if ( value )
{
this.Cursor = Cursors.Default;
this.IsEnableZoom = false;
}
else
{
this.Cursor = Cursors.Cross;
this.IsEnableZoom = true;
}
*/
}
}
#endregion
#region Methods
/// <summary>
/// Called by the system to update the control on-screen
/// </summary>
/// <param name="e">
/// A PaintEventArgs object containing the Graphics specifications
/// for this Paint event.
/// </param>
protected override void OnPaint(PaintEventArgs e)
{
lock (this)
{
if (BeenDisposed || _masterPane == null || this.GraphPane == null)
return;
if (hScrollBar1 != null && this.GraphPane != null &&
vScrollBar1 != null && _yScrollRangeList != null)
{
SetScroll(hScrollBar1, this.GraphPane.XAxis, _xScrollRange.Min, _xScrollRange.Max);
SetScroll(vScrollBar1, this.GraphPane.YAxis, _yScrollRangeList[0].Min,
_yScrollRangeList[0].Max);
}
SmoothingMode sModeSave = e.Graphics.SmoothingMode;
TextRenderingHint sHintSave = e.Graphics.TextRenderingHint;
if (_isAntiAlias)
{
e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
e.Graphics.TextRenderingHint = TextRenderingHint.AntiAlias;
}
base.OnPaint(e);
// Add a try/catch pair since the users of the control can't catch this one
try { _masterPane.Draw(e.Graphics); }
catch { }
e.Graphics.SmoothingMode = sModeSave;
e.Graphics.TextRenderingHint = sHintSave;
}
/*
// first, see if an old thread is still running
if ( t != null && t.IsAlive )
{
t.Abort();
}
//dt = new DrawingThread( e.Graphics, _masterPane );
//g = e.Graphics;
// Fire off the new thread
t = new Thread( new ParameterizedThreadStart( DoDrawingThread ) );
//ct.ApartmentState = ApartmentState.STA;
//ct.SetApartmentState( ApartmentState.STA );
DrawingThreadData dtd;
dtd._g = e.Graphics;
dtd._masterPane = _masterPane;
t.Start( dtd );
//ct.Join();
*/
}
// Thread t = null;
//DrawingThread dt = null;
/*
/// <summary>
///
/// </summary>
/// <param name="dtdobj"></param>
public void DoDrawingThread( object dtdobj )
{
try
{
DrawingThreadData dtd = (DrawingThreadData) dtdobj;
if ( dtd._g != null && dtd._masterPane != null )
dtd._masterPane.Draw( dtd._g );
// else
// {
// using ( Graphics g2 = CreateGraphics() )
// _masterPane.Draw( g2 );
// }
}
catch
{
}
}
*/
/// <summary>
/// Called when the control has been resized.
/// </summary>
/// <param name="sender">
/// A reference to the control that has been resized.
/// </param>
/// <param name="e">
/// An EventArgs object.
/// </param>
protected void ZedGraphControl_ReSize(object sender, System.EventArgs e)
{
lock (this)
{
if (BeenDisposed || _masterPane == null)
return;
Size newSize = this.Size;
if (_isShowHScrollBar)
{
hScrollBar1.Visible = true;
newSize.Height -= this.hScrollBar1.Size.Height;
hScrollBar1.Location = new Point(0, newSize.Height);
hScrollBar1.Size = new Size(newSize.Width, hScrollBar1.Height);
}
else
hScrollBar1.Visible = false;
if (_isShowVScrollBar)
{
vScrollBar1.Visible = true;
newSize.Width -= this.vScrollBar1.Size.Width;
vScrollBar1.Location = new Point(newSize.Width, 0);
vScrollBar1.Size = new Size(vScrollBar1.Width, newSize.Height);
}
else
vScrollBar1.Visible = false;
using (Graphics g = this.CreateGraphics())
{
_masterPane.ReSize(g, new RectangleF(0, 0, newSize.Width, newSize.Height));
//g.Dispose();
}
this.Invalidate();
}
}
/// <summary>This performs an axis change command on the graphPane.
/// </summary>
/// <remarks>
/// This is the same as
/// <c>ZedGraphControl.GraphPane.AxisChange( ZedGraphControl.CreateGraphics() )</c>, however,
/// this method also calls <see cref="SetScrollRangeFromData" /> if <see cref="IsAutoScrollRange" />
/// is true.
/// </remarks>
public virtual void AxisChange()
{
lock (this)
{
if (BeenDisposed || _masterPane == null)
return;
using (Graphics g = this.CreateGraphics())
{
_masterPane.AxisChange(g);
//g.Dispose();
}
if (_isAutoScrollRange)
SetScrollRangeFromData();
}
}
#endregion
#region Mouse Events
/// <summary>
/// Handle a MouseDown event in the <see cref="ZedGraphControl" />
/// </summary>
/// <param name="sender">A reference to the <see cref="ZedGraphControl" /></param>
/// <param name="e">A <see cref="MouseEventArgs" /> instance</param>
protected void ZedGraphControl_MouseDown(object sender, MouseEventArgs e)
{
_isPanning = false;
_isZooming = false;
_isEditing = false;
_isSelecting = false;
_dragPane = null;
Point mousePt = new Point(e.X, e.Y);
// Callback for doubleclick events
if (_masterPane != null && e.Clicks > 1 && this.DoubleClickEvent != null)
{
if (this.DoubleClickEvent(this, e))
return;
}
// Provide Callback for MouseDown events
if (_masterPane != null && this.MouseDownEvent != null)
{
if (this.MouseDownEvent(this, e))
return;
}
if (e.Clicks > 1 || _masterPane == null)
return;
// First, see if the click is within a Linkable object within any GraphPane
GraphPane pane = this.MasterPane.FindPane(mousePt);
if (pane != null &&
e.Button == _linkButtons && Control.ModifierKeys == _linkModifierKeys)
{
object source;
Link link;
int index;
using (Graphics g = this.CreateGraphics())
{
float scaleFactor = pane.CalcScaleFactor();
if (pane.FindLinkableObject(mousePt, g, scaleFactor, out source, out link, out index))
{
if (LinkEvent != null && LinkEvent(this, pane, source, link, index))
return;
string url;
CurveItem curve = source as CurveItem;
if (curve != null)
url = link.MakeCurveItemUrl(pane, curve, index);
else
url = link._url;
if (url != string.Empty)
{
System.Diagnostics.Process.Start(url);
// linkable objects override any other actions with mouse
return;
}
}
//g.Dispose();
}
}
// Second, Check to see if it's within a Chart Rect
pane = this.MasterPane.FindChartRect(mousePt);
//Rectangle rect = new Rectangle( mousePt, new Size( 1, 1 ) );
if (pane != null &&
(_isEnableHPan || _isEnableVPan) &&
((e.Button == _panButtons && Control.ModifierKeys == _panModifierKeys) ||
(e.Button == _panButtons2 && Control.ModifierKeys == _panModifierKeys2)))
{
_isPanning = true;
_dragStartPt = mousePt;
_dragPane = pane;
//_zoomState = new ZoomState( _dragPane, ZoomState.StateType.Pan );
ZoomStateSave(_dragPane, ZoomState.StateType.Pan);
}
else if (pane != null && (_isEnableHZoom || _isEnableVZoom) &&
((e.Button == _zoomButtons && Control.ModifierKeys == _zoomModifierKeys) ||
(e.Button == _zoomButtons2 && Control.ModifierKeys == _zoomModifierKeys2)))
{
_isZooming = true;
_dragStartPt = mousePt;
_dragEndPt = mousePt;
_dragEndPt.Offset(1, 1);
_dragPane = pane;
ZoomStateSave(_dragPane, ZoomState.StateType.Zoom);
}
//Revision: JCarpenter 10/06
else if (pane != null && _isEnableSelection && e.Button == _selectButtons &&
(Control.ModifierKeys == _selectModifierKeys ||
Control.ModifierKeys == _selectAppendModifierKeys))
{
_isSelecting = true;
_dragStartPt = mousePt;
_dragEndPt = mousePt;
_dragEndPt.Offset(1, 1);
_dragPane = pane;
}
else if (pane != null && (_isEnableHEdit || _isEnableVEdit) &&
(e.Button == EditButtons && Control.ModifierKeys == EditModifierKeys))
{
// find the point that was clicked, and make sure the point list is editable
// and that it's a primary Y axis (the first Y or Y2 axis)
if (pane.FindNearestPoint(mousePt, out _dragCurve, out _dragIndex) &&
_dragCurve.Points is IPointListEdit)
{
_isEditing = true;
_dragPane = pane;
_dragStartPt = mousePt;
_dragStartPair = _dragCurve[_dragIndex];
}
}
}
/// <summary>
/// Set the cursor according to the current mouse location.
/// </summary>
protected void SetCursor()
{
SetCursor(this.PointToClient(Control.MousePosition));
}
/// <summary>
/// Set the cursor according to the current mouse location.
/// </summary>
protected void SetCursor(Point mousePt)
{
if (_masterPane != null)
{
GraphPane pane = _masterPane.FindChartRect(mousePt);
if ((_isEnableHPan || _isEnableVPan) && (Control.ModifierKeys == Keys.Shift || _isPanning) &&
(pane != null || _isPanning))
this.Cursor = Cursors.Hand;
else if ((_isEnableVZoom || _isEnableHZoom) && (pane != null || _isZooming))
this.Cursor = Cursors.Cross;
else if (_isEnableSelection && (pane != null || _isSelecting))
this.Cursor = Cursors.Cross;
else
this.Cursor = Cursors.Default;
// else if ( isZoomMode || isPanMode )
// this.Cursor = Cursors.No;
}
}
/// <summary>
/// Handle a KeyUp event
/// </summary>
/// <param name="sender">The <see cref="ZedGraphControl" /> in which the KeyUp occurred.</param>
/// <param name="e">A <see cref="KeyEventArgs" /> instance.</param>
protected void ZedGraphControl_KeyUp(object sender, KeyEventArgs e)
{
SetCursor();
}
/// <summary>
/// Handle the Key Events so ZedGraph can Escape out of a panning or zooming operation.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void ZedGraphControl_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
{
SetCursor();
if (e.KeyCode == Keys.Escape)
{
if (_isPanning)
HandlePanCancel();
if (_isZooming)
HandleZoomCancel();
if (_isEditing)
HandleEditCancel();
//if ( _isSelecting )
// Esc always cancels the selection
HandleSelectionCancel();
_isZooming = false;
_isPanning = false;
_isEditing = false;
_isSelecting = false;
Refresh();
}
}
/// <summary>
/// Handle a MouseUp event in the <see cref="ZedGraphControl" />
/// </summary>
/// <param name="sender">A reference to the <see cref="ZedGraphControl" /></param>
/// <param name="e">A <see cref="MouseEventArgs" /> instance</param>
protected void ZedGraphControl_MouseUp(object sender, MouseEventArgs e)
{
// Provide Callback for MouseUp events
if (_masterPane != null && this.MouseUpEvent != null)
{
if (this.MouseUpEvent(this, e))
return;
}
if (_masterPane != null && _dragPane != null)
{
// If the MouseUp event occurs, the user is done dragging.
if (_isZooming)
HandleZoomFinish(sender, e);
else if (_isPanning)
HandlePanFinish();
else if (_isEditing)
HandleEditFinish();
//Revision: JCarpenter 10/06
else if (_isSelecting)
HandleSelectionFinish(sender, e);
}
// Reset the rectangle.
//dragStartPt = new Rectangle( 0, 0, 0, 0 );
_dragPane = null;
_isZooming = false;
_isPanning = false;
_isEditing = false;
_isSelecting = false;
Cursor.Current = Cursors.Default;
}
/// <summary>
/// Make a string label that corresponds to a user scale value.
/// </summary>
/// <param name="axis">The axis from which to obtain the scale value. This determines
/// if it's a date value, linear, log, etc.</param>
/// <param name="val">The value to be made into a label</param>
/// <param name="iPt">The ordinal position of the value</param>
/// <param name="isOverrideOrdinal">true to override the ordinal settings of the axis,
/// and prefer the actual value instead.</param>
/// <returns>The string label.</returns>
protected string MakeValueLabel(Axis axis, double val, int iPt, bool isOverrideOrdinal)
{
if (axis != null)
{
if (axis.Scale.IsDate || axis.Scale.Type == AxisType.DateAsOrdinal)
{
return XDate.ToString(val, _pointDateFormat);
}
else if (axis._scale.IsText && axis._scale._textLabels != null)
{
int i = iPt;
if (isOverrideOrdinal)
i = (int)(val - 0.5);
if (i >= 0 && i < axis._scale._textLabels.Length)
return axis._scale._textLabels[i];
else
return (i + 1).ToString();
}
else if (axis.Scale.IsAnyOrdinal && axis.Scale.Type != AxisType.LinearAsOrdinal
&& !isOverrideOrdinal)
{
return iPt.ToString(_pointValueFormat);
}
else
return val.ToString(_pointValueFormat);
}
else
return "";
}
/// <summary>
/// protected method for handling MouseMove events to display tooltips over
/// individual datapoints.
/// </summary>
/// <param name="sender">
/// A reference to the control that has the MouseMove event.
/// </param>
/// <param name="e">
/// A MouseEventArgs object.
/// </param>
protected void ZedGraphControl_MouseMove(object sender, MouseEventArgs e)
{
if (_masterPane != null)
{
// Provide Callback for MouseMove events
if (this.MouseMoveEvent != null && this.MouseMoveEvent(this, e))
return;
Point mousePt = new Point(e.X, e.Y);
//Point tempPt = this.PointToClient( Control.MousePosition );
SetCursor(mousePt);
// If the mouse is being dragged,
// undraw and redraw the rectangle as the mouse moves.
if (_isZooming)
HandleZoomDrag(mousePt);
else if (_isPanning)
HandlePanDrag(mousePt);
else if (_isEditing)
HandleEditDrag(mousePt);
else if (_isShowCursorValues)
HandleCursorValues(mousePt);
else if (_isShowPointValues)
HandlePointValues(mousePt);
//Revision: JCarpenter 10/06
else if (_isSelecting)
HandleZoomDrag(mousePt);
}
}
private Point HandlePointValues(Point mousePt)
{
int iPt;
GraphPane pane;
object nearestObj;
using (Graphics g = this.CreateGraphics())
{
if (_masterPane.FindNearestPaneObject(mousePt,
g, out pane, out nearestObj, out iPt))
{
if (nearestObj is CurveItem && iPt >= 0)
{
CurveItem curve = (CurveItem)nearestObj;
// Provide Callback for User to customize the tooltips
if (this.PointValueEvent != null)
{
string label = this.PointValueEvent(this, pane, curve, iPt);
if (label != null && label.Length > 0)
{
this.pointToolTip.SetToolTip(this, label);
this.pointToolTip.Active = true;
}
else
this.pointToolTip.Active = false;
}
else
{
if (curve is PieItem)
{
this.pointToolTip.SetToolTip(this,
((PieItem)curve).Value.ToString(_pointValueFormat));
}
// else if ( curve is OHLCBarItem || curve is JapaneseCandleStickItem )
// {
// StockPt spt = (StockPt)curve.Points[iPt];
// this.pointToolTip.SetToolTip( this, ( (XDate) spt.Date ).ToString( "MM/dd/yyyy" ) + "\nOpen: $" +
// spt.Open.ToString( "N2" ) +
// "\nHigh: $" +
// spt.High.ToString( "N2" ) + "\nLow: $" +
// spt.Low.ToString( "N2" ) + "\nClose: $" +
// spt.Close.ToString
// ( "N2" ) );
// }
else
{
PointPair pt = curve.Points[iPt];
if (pt.Tag is string)
this.pointToolTip.SetToolTip(this, (string)pt.Tag);
else
{
double xVal, yVal, lowVal;
ValueHandler valueHandler = new ValueHandler(pane, false);
if ((curve is BarItem || curve is ErrorBarItem || curve is HiLowBarItem)
&& pane.BarSettings.Base != BarBase.X)
valueHandler.GetValues(curve, iPt, out yVal, out lowVal, out xVal);
else
valueHandler.GetValues(curve, iPt, out xVal, out lowVal, out yVal);
string xStr = MakeValueLabel(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
/// <summary>
/// Handle a MouseWheel event in the <see cref="ZedGraphControl" />
/// </summary>
/// <param name="sender">A reference to the <see cref="ZedGraphControl" /></param>
/// <param name="e">A <see cref="MouseEventArgs" /> instance</param>
protected void ZedGraphControl_MouseWheel(object sender, MouseEventArgs e)
{
if ((_isEnableVZoom || _isEnableHZoom) && _masterPane != null)
{
GraphPane pane = this.MasterPane.FindChartRect(new PointF(e.X, e.Y));
if (pane != null && e.Delta != 0)
{
ZoomState oldState = ZoomStateSave(pane, ZoomState.StateType.WheelZoom);
//ZoomState oldState = pane.ZoomStack.Push( pane, ZoomState.StateType.Zoom );
PointF centerPoint = new PointF(e.X, e.Y);
double zoomFraction = (1 + (e.Delta < 0 ? 1.0 : -1.0) * ZoomStepFraction);
ZoomPane(pane, zoomFraction, centerPoint, _isZoomOnMouseCenter, false);
ApplyToAllPanes(pane);
using (Graphics g = this.CreateGraphics())
{
// always AxisChange() the dragPane
pane.AxisChange(g);
foreach (GraphPane tempPane in _masterPane._paneList)
{
if (tempPane != pane && (_isSynchronizeXAxes || _isSynchronizeYAxes))
tempPane.AxisChange(g);
}
}
ZoomStatePush(pane);
// Provide Callback to notify the user of zoom events
if (this.ZoomEvent != null)
this.ZoomEvent(this, oldState, new ZoomState(pane, ZoomState.StateType.WheelZoom));
this.Refresh();
}
}
}
/// <summary>
/// Zoom a specified pane in or out according to the specified zoom fraction.
/// </summary>
/// <remarks>
/// The zoom will occur on the <see cref="XAxis" />, <see cref="YAxis" />, and
/// <see cref="Y2Axis" /> only if the corresponding flag, <see cref="IsEnableHZoom" /> or
/// <see cref="IsEnableVZoom" />, is true. Note that if there are multiple Y or Y2 axes, all of
/// them will be zoomed.
/// </remarks>
/// <param name="pane">The <see cref="GraphPane" /> instance to be zoomed.</param>
/// <param name="zoomFraction">The fraction by which to zoom, less than 1 to zoom in, greater than
/// 1 to zoom out. For example, 0.9 will zoom in such that the scale is 90% of what it was
/// originally.</param>
/// <param name="centerPt">The screen position about which the zoom will be centered. This
/// value is only used if <see paramref="isZoomOnCenter" /> is true.
/// </param>
/// <param name="isZoomOnCenter">true to cause the zoom to be centered on the point
/// <see paramref="centerPt" />, false to center on the <see cref="Chart.Rect" />.
/// </param>
/// <param name="isRefresh">true to force a refresh of the control, false to leave it unrefreshed</param>
protected void ZoomPane(GraphPane pane, double zoomFraction, PointF centerPt,
bool isZoomOnCenter, bool isRefresh)
{
double x;
double 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();
}
/// <summary>
/// Zoom a specified pane in or out according to the specified zoom fraction.
/// </summary>
/// <remarks>
/// The zoom will occur on the <see cref="XAxis" />, <see cref="YAxis" />, and
/// <see cref="Y2Axis" /> only if the corresponding flag, <see cref="IsEnableHZoom" /> or
/// <see cref="IsEnableVZoom" />, is true. Note that if there are multiple Y or Y2 axes, all of
/// them will be zoomed.
/// </remarks>
/// <param name="pane">The <see cref="GraphPane" /> instance to be zoomed.</param>
/// <param name="zoomFraction">The fraction by which to zoom, less than 1 to zoom in, greater than
/// 1 to zoom out. For example, 0.9 will zoom in such that the scale is 90% of what it was
/// originally.</param>
/// <param name="centerPt">The screen position about which the zoom will be centered. This
/// value is only used if <see paramref="isZoomOnCenter" /> is true.
/// </param>
/// <param name="isZoomOnCenter">true to cause the zoom to be centered on the point
/// <see paramref="centerPt" />, false to center on the <see cref="Chart.Rect" />.
/// </param>
public void ZoomPane(GraphPane pane, double zoomFraction, PointF centerPt, bool isZoomOnCenter)
{
ZoomPane(pane, zoomFraction, centerPt, isZoomOnCenter, true);
}
/// <summary>
/// Zoom the specified axis by the specified amount, with the center of the zoom at the
/// (optionally) specified point.
/// </summary>
/// <remarks>
/// This method is used for MouseWheel zoom operations</remarks>
/// <param name="axis">The <see cref="Axis" /> to be zoomed.</param>
/// <param name="zoomFraction">The zoom fraction, less than 1.0 to zoom in, greater than 1.0 to
/// zoom out. That is, a value of 0.9 will zoom in such that the scale length is 90% of what
/// it previously was.</param>
/// <param name="centerVal">The location for the center of the zoom. This is only used if
/// <see paramref="IsZoomOnMouseCenter" /> is true.</param>
/// <param name="isZoomOnCenter">true if the zoom is to be centered at the
/// <see paramref="centerVal" /> screen position, false for the zoom to be centered within
/// the <see cref="Chart.Rect" />.
/// </param>
protected void ZoomScale(Axis axis, double zoomFraction, double centerVal, bool isZoomOnCenter)
{
if (axis != null && zoomFraction > 0.0001 && zoomFraction < 1000.0)
{
Scale scale = axis._scale;
/*
if ( axis.Scale.IsLog )
{
double ratio = Math.Sqrt( axis._scale._max / axis._scale._min * zoomFraction );
if ( !isZoomOnCenter )
centerVal = Math.Sqrt( axis._scale._max * axis._scale._min );
axis._scale._min = centerVal / ratio;
axis._scale._max = centerVal * ratio;
}
else
{
*/
double minLin = axis._scale._minLinearized;
double maxLin = axis._scale._maxLinearized;
double range = (maxLin - minLin) * zoomFraction / 2.0;
if (!isZoomOnCenter)
centerVal = (maxLin + minLin) / 2.0;
axis._scale._minLinearized = centerVal - range;
axis._scale._maxLinearized = centerVal + range;
// }
axis._scale._minAuto = false;
axis._scale._maxAuto = false;
}
}
#endregion
#region Pan Events
private Point HandlePanDrag(Point mousePt)
{
double x1, x2, 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();
}
}
/// <summary>
/// Handle a panning operation for the specified <see cref="Axis" />.
/// </summary>
/// <param name="axis">The <see cref="Axis" /> to be panned</param>
/// <param name="startVal">The value where the pan started. The scale range
/// will be shifted by the difference between <see paramref="startVal" /> and
/// <see paramref="endVal" />.
/// </param>
/// <param name="endVal">The value where the pan ended. The scale range
/// will be shifted by the difference between <see paramref="startVal" /> and
/// <see paramref="endVal" />.
/// </param>
protected void PanScale(Axis axis, double startVal, double endVal)
{
if (axis != null)
{
Scale scale = axis._scale;
double delta = scale.Linearize(startVal) - scale.Linearize(endVal);
scale._minLinearized += delta;
scale._maxLinearized += delta;
scale._minAuto = false;
scale._maxAuto = false;
/*
if ( axis.Type == AxisType.Log )
{
axis._scale._min *= startVal / endVal;
axis._scale._max *= startVal / endVal;
}
else
{
axis._scale._min += startVal - endVal;
axis._scale._max += startVal - endVal;
}
*/
}
}
#endregion
#region Edit Point Events
private void HandleEditDrag(Point mousePt)
{
// get the scale values that correspond to the current point
double curX, curY;
_dragPane.ReverseTransform(mousePt, _dragCurve.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
/// <summary>
/// Perform selection on curves within the drag pane, or under the mouse click.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void HandleSelectionFinish(object sender, MouseEventArgs e)
{
if (e.Button != _selectButtons)
{
Refresh();
return;
}
PointF mousePtF = BoundPointToRect(new Point(e.X, e.Y), _dragPane.Chart._rect);
PointF mousePt = BoundPointToRect(new Point(e.X, e.Y), _dragPane.Rect);
Point curPt = ((Control)sender).PointToScreen(Point.Round(mousePt));
// Only accept a drag if it covers at least 5 pixels in each direction
//Point curPt = ( (Control)sender ).PointToScreen( Point.Round( mousePt ) );
if ((Math.Abs(mousePtF.X - _dragStartPt.X) > 4) &&
(Math.Abs(mousePtF.Y - _dragStartPt.Y) > 4))
{
#region New Code to Select on Rubber Band
double x1, x2, 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));
}
}
/// <summary>
/// Use the MouseCaptureChanged as an indicator for the start and end of a scrolling operation
/// </summary>
private void ScrollBarMouseCaptureChanged(object sender, EventArgs e)
{
ScrollBar scrollBar = sender as ScrollBar;
if (scrollBar != null)
{
// If this is the start of a new scroll, then Capture will be true
if (scrollBar.Capture)
{
// save the original zoomstate
//_zoomState = new ZoomState( this.GraphPane, ZoomState.StateType.Scroll );
ZoomStateSave(this.GraphPane, ZoomState.StateType.Scroll);
}
else
{
// push the prior saved zoomstate, since the scale ranges have already been changed on
// the fly during the scrolling operation
if (_zoomState != null && _zoomState.IsChanged(this.GraphPane))
{
//this.GraphPane.ZoomStack.Push( _zoomState );
ZoomStatePush(this.GraphPane);
// Provide Callback to notify the user of pan events
if (this.ScrollDoneEvent != null)
this.ScrollDoneEvent(this, scrollBar, _zoomState,
new ZoomState(this.GraphPane, ZoomState.StateType.Scroll));
_zoomState = null;
}
}
}
}
private void HandleScroll(Axis axis, int newValue, double scrollMin, double scrollMax,
int largeChange, bool reverse)
{
if (axis != null)
{
if (scrollMin > axis._scale._min)
scrollMin = axis._scale._min;
if (scrollMax < axis._scale._max)
scrollMax = axis._scale._max;
int span = _ScrollControlSpan - largeChange;
if (span <= 0)
return;
if (reverse)
newValue = span - newValue;
Scale scale = axis._scale;
double delta = scale._maxLinearized - scale._minLinearized;
double scrollMin2 = scale.Linearize(scrollMax) - delta;
scrollMin = scale.Linearize(scrollMin);
//scrollMax = scale.Linearize( scrollMax );
double val = scrollMin + (double)newValue / (double)span *
(scrollMin2 - scrollMin);
scale._minLinearized = val;
scale._maxLinearized = val + delta;
/*
if ( axis.Scale.IsLog )
{
double ratio = axis._scale._max / axis._scale._min;
double scrollMin2 = scrollMax / ratio;
double val = scrollMin * Math.Exp( (double)newValue / (double)span *
( Math.Log( scrollMin2 ) - Math.Log( scrollMin ) ) );
axis._scale._min = val;
axis._scale._max = val * ratio;
}
else
{
double delta = axis._scale._max - axis._scale._min;
double scrollMin2 = scrollMax - delta;
double val = scrollMin + (double)newValue / (double)span *
( scrollMin2 - scrollMin );
axis._scale._min = val;
axis._scale._max = val + delta;
}
*/
this.Invalidate();
}
}
/// <summary>
/// Sets the value of the scroll range properties (see <see cref="ScrollMinX" />,
/// <see cref="ScrollMaxX" />, <see cref="YScrollRangeList" />, and
/// <see cref="Y2ScrollRangeList" /> based on the actual range of the data for
/// each corresponding <see cref="Axis" />.
/// </summary>
/// <remarks>
/// This method is called automatically by <see cref="AxisChange" /> if
/// <see cref="IsAutoScrollRange" />
/// is true. Note that this will not be called if you call AxisChange directly from the
/// <see cref="GraphPane" />. For example, zedGraphControl1.AxisChange() works properly, but
/// zedGraphControl1.GraphPane.AxisChange() does not.</remarks>
public void SetScrollRangeFromData()
{
if (this.GraphPane != null)
{
double grace = CalcScrollGrace(this.GraphPane.XAxis.Scale._rangeMin,
this.GraphPane.XAxis.Scale._rangeMax);
_xScrollRange.Min = this.GraphPane.XAxis.Scale._rangeMin - grace;
_xScrollRange.Max = this.GraphPane.XAxis.Scale._rangeMax + grace;
_xScrollRange.IsScrollable = true;
for (int i = 0; i < this.GraphPane.YAxisList.Count; i++)
{
Axis axis = this.GraphPane.YAxisList[i];
grace = CalcScrollGrace(axis.Scale._rangeMin, axis.Scale._rangeMax);
ScrollRange range = new ScrollRange(axis.Scale._rangeMin - grace,
axis.Scale._rangeMax + grace, _yScrollRangeList[i].IsScrollable);
if (i >= _yScrollRangeList.Count)
_yScrollRangeList.Add(range);
else
_yScrollRangeList[i] = range;
}
for (int i = 0; i < this.GraphPane.Y2AxisList.Count; i++)
{
Axis axis = this.GraphPane.Y2AxisList[i];
grace = CalcScrollGrace(axis.Scale._rangeMin, axis.Scale._rangeMax);
ScrollRange range = new ScrollRange(axis.Scale._rangeMin - grace,
axis.Scale._rangeMax + grace, _y2ScrollRangeList[i].IsScrollable);
if (i >= _y2ScrollRangeList.Count)
_y2ScrollRangeList.Add(range);
else
_y2ScrollRangeList[i] = range;
}
//this.GraphPane.CurveList.GetRange( out scrollMinX, out scrollMaxX,
// out scrollMinY, out scrollMaxY, out scrollMinY2, out scrollMaxY2, false, false,
// this.GraphPane );
}
}
private double CalcScrollGrace(double min, double max)
{
if (Math.Abs(max - min) < 1e-30)
{
if (Math.Abs(max) < 1e-30)
return _scrollGrace;
else
return max * _scrollGrace;
}
else
return (max - min) * _scrollGrace;
}
private void SetScroll(ScrollBar scrollBar, Axis axis, double scrollMin, double scrollMax)
{
if (scrollBar != null && axis != null)
{
scrollBar.Minimum = 0;
scrollBar.Maximum = _ScrollControlSpan - 1;
if (scrollMin > axis._scale._min)
scrollMin = axis._scale._min;
if (scrollMax < axis._scale._max)
scrollMax = axis._scale._max;
int val = 0;
Scale scale = axis._scale;
double minLinearized = scale._minLinearized;
double maxLinearized = scale._maxLinearized;
scrollMin = scale.Linearize(scrollMin);
scrollMax = scale.Linearize(scrollMax);
double scrollMin2 = scrollMax - (maxLinearized - minLinearized);
/*
if ( axis.Scale.IsLog )
scrollMin2 = scrollMax / ( axis._scale._max / axis._scale._min );
else
scrollMin2 = scrollMax - ( axis._scale._max - axis._scale._min );
*/
if (scrollMin >= scrollMin2)
{
//scrollBar.Visible = false;
scrollBar.Enabled = false;
scrollBar.Value = 0;
}
else
{
double ratio = (maxLinearized - minLinearized) / (scrollMax - scrollMin);
/*
if ( axis.Scale.IsLog )
ratio = ( Math.Log( axis._scale._max ) - Math.Log( axis._scale._min ) ) /
( Math.Log( scrollMax ) - Math.Log( scrollMin ) );
else
ratio = ( axis._scale._max - axis._scale._min ) / ( scrollMax - scrollMin );
*/
int largeChange = (int)(ratio * _ScrollControlSpan + 0.5);
if (largeChange < 1)
largeChange = 1;
scrollBar.LargeChange = largeChange;
int smallChange = largeChange / _ScrollSmallRatio;
if (smallChange < 1)
smallChange = 1;
scrollBar.SmallChange = smallChange;
int span = _ScrollControlSpan - largeChange;
val = (int)((minLinearized - scrollMin) / (scrollMin2 - scrollMin) *
span + 0.5);
/*
if ( axis.Scale.IsLog )
val = (int)( ( Math.Log( axis._scale._min ) - Math.Log( scrollMin ) ) /
( Math.Log( scrollMin2 ) - Math.Log( scrollMin ) ) * span + 0.5 );
else
val = (int)( ( axis._scale._min - scrollMin ) / ( scrollMin2 - scrollMin ) *
span + 0.5 );
*/
if (val < 0)
val = 0;
else if (val > span)
val = span;
//if ( ( axis is XAxis && axis.IsReverse ) || ( ( ! axis is XAxis ) && ! axis.IsReverse ) )
if ((axis is XAxis) == axis.Scale.IsReverse)
val = span - val;
if (val < scrollBar.Minimum)
val = scrollBar.Minimum;
if (val > scrollBar.Maximum)
val = scrollBar.Maximum;
scrollBar.Value = val;
scrollBar.Enabled = true;
//scrollBar.Visible = true;
}
}
}
#endregion
#region ContextMenu
// Revision: JCarpenter 10/06
/// <summary>
/// Public enumeration that specifies the type of
/// object present at the Context Menu's mouse location
/// </summary>
public enum ContextMenuObjectState
{
/// <summary>
/// The object is an Inactive Curve Item at the Context Menu's mouse position
/// </summary>
InactiveSelection,
/// <summary>
/// The object is an active Curve Item at the Context Menu's mouse position
/// </summary>
ActiveSelection,
/// <summary>
/// There is no selectable object present at the Context Menu's mouse position
/// </summary>
Background
}
//Revision: JCarpenter 10/06
/// <summary>
/// Find the object currently under the mouse cursor, and return its state.
/// </summary>
private ContextMenuObjectState GetObjectState()
{
ContextMenuObjectState objState = ContextMenuObjectState.Background;
// Determine object state
Point mousePt = this.PointToClient(Control.MousePosition);
int iPt;
GraphPane pane;
object nearestObj;
using (Graphics g = this.CreateGraphics())
{
if (this.MasterPane.FindNearestPaneObject(mousePt, g, out pane,
out nearestObj, out iPt))
{
CurveItem item = nearestObj as CurveItem;
if (item != null && iPt >= 0)
{
if (item.IsSelected)
objState = ContextMenuObjectState.ActiveSelection;
else
objState = ContextMenuObjectState.InactiveSelection;
}
}
}
return objState;
}
/// <summary>
/// protected method to handle the popup context menu in the <see cref="ZedGraphControl"/>.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
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 = "<22><><EFBFBD><EFBFBD>ͼƬ";
//item.Tag = "<22><><EFBFBD><EFBFBD>ͼƬ";
//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);
}
}
}
/// <summary>
/// Handler for the "Copy" context menu item. Copies the current image to a bitmap on the
/// clipboard.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void MenuClick_Copy(System.Object sender, System.EventArgs e)
{
Copy(_isShowCopyMessage);
}
/// <summary>
/// Handler for the "Copy" context menu item. Copies the current image to a bitmap on the
/// clipboard.
/// </summary>
/// <param name="isShowMessage">boolean value that determines whether or not a prompt will be
/// displayed. true to show a message of "Image Copied to ClipBoard".</param>
public void Copy(bool isShowMessage)
{
if (_masterPane != null)
{
//Clipboard.SetDataObject( _masterPane.GetImage(), true );
// Threaded copy mode to avoid crash with MTA
// Contributed by Dave Moor
Thread ct = new Thread(new ThreadStart(this.ClipboardCopyThread));
//ct.ApartmentState = ApartmentState.STA;
ct.SetApartmentState(ApartmentState.STA);
ct.Start();
ct.Join();
if (isShowMessage)
{
string str = _resourceManager.GetString("copied_to_clip");
//MessageBox.Show( "Image Copied to ClipBoard" );
MessageBox.Show(str);
}
}
}
/// <summary>
/// A threaded version of the copy method to avoid crash with MTA
/// </summary>
private void ClipboardCopyThread()
{
Clipboard.SetDataObject(_masterPane.GetImage(), true);
}
/// <summary>
/// Handler for the "Save Image As" context menu item. Copies the current image to the selected
/// file.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void MenuClick_SaveAs(System.Object sender, System.EventArgs e)
{
SaveAs();
}
/// <summary>
/// Handler for the "Save Image As" context menu item. Copies the current image to the selected
/// file in either the Emf (vector), or a variety of Bitmap formats.
/// </summary>
/// <remarks>
/// Note that <see cref="SaveAsBitmap" /> and <see cref="SaveAsEmf" /> methods are provided
/// which allow for Bitmap-only or Emf-only handling of the "Save As" context menu item.
/// </remarks>
public void SaveAs()
{
if (_masterPane != null)
{
_saveFileDialog.Filter =
"Emf Format (*.emf)|*.emf|" +
"PNG Format (*.png)|*.png|" +
"Gif Format (*.gif)|*.gif|" +
"Jpeg Format (*.jpg)|*.jpg|" +
"Tiff Format (*.tif)|*.tif|" +
"Bmp Format (*.bmp)|*.bmp";
if (_saveFileDialog.ShowDialog() == DialogResult.OK)
{
Stream myStream = _saveFileDialog.OpenFile();
if (myStream != null)
{
if (_saveFileDialog.FilterIndex == 1)
{
myStream.Close();
_masterPane.GetMetafile().Save(_saveFileDialog.FileName);
}
else
{
ImageFormat format = ImageFormat.Png;
if (_saveFileDialog.FilterIndex == 3)
format = ImageFormat.Gif;
else if (_saveFileDialog.FilterIndex == 4)
format = ImageFormat.Jpeg;
else if (_saveFileDialog.FilterIndex == 5)
format = ImageFormat.Tiff;
else if (_saveFileDialog.FilterIndex == 6)
format = ImageFormat.Bmp;
_masterPane.GetImage().Save(myStream, format);
myStream.Close();
}
}
}
}
}
/// <summary>
/// Handler for the "Save Image As" context menu item. Copies the current image to the selected
/// Bitmap file.
/// </summary>
/// <remarks>
/// Note that this handler saves as a bitmap only. The default handler is
/// <see cref="SaveAs" />, which allows for Bitmap or EMF formats
/// </remarks>
public void SaveAsBitmap()
{
if (_masterPane != null)
{
_saveFileDialog.Filter =
"PNG Format (*.png)|*.png|" +
"Gif Format (*.gif)|*.gif|" +
"Jpeg Format (*.jpg)|*.jpg|" +
"Tiff Format (*.tif)|*.tif|" +
"Bmp Format (*.bmp)|*.bmp";
if (_saveFileDialog.ShowDialog() == DialogResult.OK)
{
ImageFormat format = ImageFormat.Png;
if (_saveFileDialog.FilterIndex == 2)
format = ImageFormat.Gif;
else if (_saveFileDialog.FilterIndex == 3)
format = ImageFormat.Jpeg;
else if (_saveFileDialog.FilterIndex == 4)
format = ImageFormat.Tiff;
else if (_saveFileDialog.FilterIndex == 5)
format = ImageFormat.Bmp;
Stream myStream = _saveFileDialog.OpenFile();
if (myStream != null)
{
_masterPane.GetImage().Save(myStream, format);
myStream.Close();
}
}
}
}
/// <summary>
/// Handler for the "Save Image As" context menu item. Copies the current image to the selected
/// Emf format file.
/// </summary>
/// <remarks>
/// Note that this handler saves as an Emf format only. The default handler is
/// <see cref="SaveAs" />, which allows for Bitmap or EMF formats.
/// </remarks>
public void SaveAsEmf()
{
if (_masterPane != null)
{
_saveFileDialog.Filter = "Emf Format (*.emf)|*.emf";
if (_saveFileDialog.ShowDialog() == DialogResult.OK)
{
Stream myStream = _saveFileDialog.OpenFile();
if (myStream != null)
{
myStream.Close();
_masterPane.GetMetafile().Save(_saveFileDialog.FileName);
}
}
}
}
/// <summary>
/// Handler for the "Show Values" context menu item. Toggles the <see cref="IsShowPointValues"/>
/// property, which activates the point value tooltips.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void MenuClick_ShowValues(object sender, System.EventArgs e)
{
ToolStripMenuItem item = sender as ToolStripMenuItem;
if (item != null)
this.IsShowPointValues = !item.Checked;
}
/// <summary>
/// Handler for the "Set Scale to Default" context menu item. Sets the scale ranging to
/// full auto mode for all axes.
/// </summary>
/// <remarks>
/// This method differs from the <see cref="ZoomOutAll" /> method in that it sets the scales
/// to full auto mode. The <see cref="ZoomOutAll" /> method sets the scales to their initial
/// setting prior to any user actions (which may or may not be full auto mode).
/// </remarks>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void MenuClick_RestoreScale(object sender, EventArgs e)
{
if (_masterPane != null)
{
GraphPane pane = _masterPane.FindPane(_menuClickPt);
RestoreScale(pane);
}
}
/// <summary>
/// Handler for the "Set Scale to Default" context menu item. Sets the scale ranging to
/// full auto mode for all axes.
/// </summary>
/// <remarks>
/// This method differs from the <see cref="ZoomOutAll" /> method in that it sets the scales
/// to full auto mode. The <see cref="ZoomOutAll" /> method sets the scales to their initial
/// setting prior to any user actions (which may or may not be full auto mode).
/// </remarks>
/// <param name="primaryPane">The <see cref="GraphPane" /> object which is to have the
/// scale restored</param>
public void RestoreScale(GraphPane primaryPane)
{
if (primaryPane != null)
{
//Go ahead and save the old zoomstates, which provides an "undo"-like capability
//ZoomState oldState = primaryPane.ZoomStack.Push( primaryPane, ZoomState.StateType.Zoom );
ZoomState oldState = new ZoomState(primaryPane, ZoomState.StateType.Zoom);
using (Graphics g = this.CreateGraphics())
{
if (_isSynchronizeXAxes || _isSynchronizeYAxes)
{
foreach (GraphPane pane in _masterPane._paneList)
{
pane.ZoomStack.Push(pane, ZoomState.StateType.Zoom);
ResetAutoScale(pane, g);
}
}
else
{
primaryPane.ZoomStack.Push(primaryPane, ZoomState.StateType.Zoom);
ResetAutoScale(primaryPane, g);
}
// Provide Callback to notify the user of zoom events
if (this.ZoomEvent != null)
this.ZoomEvent(this, oldState, new ZoomState(primaryPane, ZoomState.StateType.Zoom));
//g.Dispose();
}
Refresh();
}
}
private void ResetAutoScale(GraphPane pane, Graphics g)
{
pane.XAxis.ResetAutoScale(pane, g);
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();
}
}
*/
/// <summary>
/// Handler for the "UnZoom/UnPan" context menu item. Restores the scale ranges to the values
/// before the last zoom or pan operation.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void MenuClick_ZoomOut(System.Object sender, System.EventArgs e)
{
if (_masterPane != null)
{
GraphPane pane = _masterPane.FindPane(_menuClickPt);
ZoomOut(pane);
}
}
/// <summary>
/// Handler for the "UnZoom/UnPan" context menu item. Restores the scale ranges to the values
/// before the last zoom, pan, or scroll operation.
/// </summary>
/// <remarks>
/// Triggers a <see cref="ZoomEvent" /> for any type of undo (including pan, scroll, zoom, and
/// wheelzoom). This method will affect all the
/// <see cref="GraphPane" /> objects in the <see cref="MasterPane" /> if
/// <see cref="IsSynchronizeXAxes" /> or <see cref="IsSynchronizeYAxes" /> is true.
/// </remarks>
/// <param name="primaryPane">The primary <see cref="GraphPane" /> object which is to be
/// zoomed out</param>
public void ZoomOut(GraphPane primaryPane)
{
if (primaryPane != null && !primaryPane.ZoomStack.IsEmpty)
{
ZoomState.StateType type = primaryPane.ZoomStack.Top.Type;
ZoomState oldState = new ZoomState(primaryPane, type);
ZoomState newState = null;
if (_isSynchronizeXAxes || _isSynchronizeYAxes)
{
foreach (GraphPane pane in _masterPane._paneList)
{
ZoomState state = pane.ZoomStack.Pop(pane);
if (pane == primaryPane)
newState = state;
}
}
else
newState = primaryPane.ZoomStack.Pop(primaryPane);
// Provide Callback to notify the user of zoom events
if (this.ZoomEvent != null)
this.ZoomEvent(this, oldState, newState);
Refresh();
}
}
/// <summary>
/// Handler for the "Undo All Zoom/Pan" context menu item. Restores the scale ranges to the values
/// before all zoom and pan operations
/// </summary>
/// <remarks>
/// This method differs from the <see cref="RestoreScale" /> method in that it sets the scales
/// to their initial setting prior to any user actions. The <see cref="RestoreScale" /> method
/// sets the scales to full auto mode (regardless of what the initial setting may have been).
/// </remarks>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void MenuClick_ZoomOutAll(System.Object sender, System.EventArgs e)
{
if (_masterPane != null)
{
GraphPane pane = _masterPane.FindPane(_menuClickPt);
ZoomOutAll(pane);
}
}
/// <summary>
/// Handler for the "Undo All Zoom/Pan" context menu item. Restores the scale ranges to the values
/// before all zoom and pan operations
/// </summary>
/// <remarks>
/// This method differs from the <see cref="RestoreScale" /> method in that it sets the scales
/// to their initial setting prior to any user actions. The <see cref="RestoreScale" /> method
/// sets the scales to full auto mode (regardless of what the initial setting may have been).
/// </remarks>
/// <param name="primaryPane">The <see cref="GraphPane" /> object which is to be zoomed out</param>
public void ZoomOutAll(GraphPane primaryPane)
{
if (primaryPane != null && !primaryPane.ZoomStack.IsEmpty)
{
ZoomState.StateType type = primaryPane.ZoomStack.Top.Type;
ZoomState oldState = new ZoomState(primaryPane, type);
//ZoomState newState = pane.ZoomStack.PopAll( pane );
ZoomState newState = null;
if (_isSynchronizeXAxes || _isSynchronizeYAxes)
{
foreach (GraphPane pane in _masterPane._paneList)
{
ZoomState state = pane.ZoomStack.PopAll(pane);
if (pane == primaryPane)
newState = state;
}
}
else
newState = primaryPane.ZoomStack.PopAll(primaryPane);
// Provide Callback to notify the user of zoom events
if (this.ZoomEvent != null)
this.ZoomEvent(this, oldState, newState);
Refresh();
}
}
#endregion
#region Printing
/// <summary>
/// Handler for the "Page Setup..." context menu item. Displays a
/// <see cref="PageSetupDialog" />.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void MenuClick_PageSetup(object sender, EventArgs e)
{
DoPageSetup();
}
/// <summary>
/// Handler for the "Print..." context menu item. Displays a
/// <see cref="PrintDialog" />.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void MenuClick_Print(object sender, EventArgs e)
{
DoPrint();
}
/// <summary>
/// Rendering method used by the print context menu items
/// </summary>
/// <param name="sender">The applicable <see cref="PrintDocument" />.</param>
/// <param name="e">A <see cref="PrintPageEventArgs" /> instance providing
/// page bounds, margins, and a Graphics instance for this printed output.
/// </param>
private void Graph_PrintPage(object sender, PrintPageEventArgs e)
{
PrintDocument pd = sender as PrintDocument;
MasterPane mPane = this.MasterPane;
bool[] isPenSave = new bool[mPane.PaneList.Count + 1];
bool[] isFontSave = new bool[mPane.PaneList.Count + 1];
isPenSave[0] = mPane.IsPenWidthScaled;
isFontSave[0] = mPane.IsFontsScaled;
for (int i = 0; i < mPane.PaneList.Count; i++)
{
isPenSave[i + 1] = mPane[i].IsPenWidthScaled;
isFontSave[i + 1] = mPane[i].IsFontsScaled;
if (_isPrintScaleAll)
{
mPane[i].IsPenWidthScaled = true;
mPane[i].IsFontsScaled = true;
}
}
RectangleF saveRect = mPane.Rect;
SizeF newSize = mPane.Rect.Size;
if (_isPrintFillPage && _isPrintKeepAspectRatio)
{
float xRatio = (float)e.MarginBounds.Width / (float)newSize.Width;
float yRatio = (float)e.MarginBounds.Height / (float)newSize.Height;
float ratio = Math.Min(xRatio, yRatio);
newSize.Width *= ratio;
newSize.Height *= ratio;
}
else if (_isPrintFillPage)
newSize = e.MarginBounds.Size;
mPane.ReSize(e.Graphics, new RectangleF(e.MarginBounds.Left,
e.MarginBounds.Top, newSize.Width, newSize.Height));
mPane.Draw(e.Graphics);
using (Graphics g = this.CreateGraphics())
{
mPane.ReSize(g, saveRect);
//g.Dispose();
}
mPane.IsPenWidthScaled = isPenSave[0];
mPane.IsFontsScaled = isFontSave[0];
for (int i = 0; i < mPane.PaneList.Count; i++)
{
mPane[i].IsPenWidthScaled = isPenSave[i + 1];
mPane[i].IsFontsScaled = isFontSave[i + 1];
}
}
/// <summary>
/// Gets or sets the <see cref="System.Drawing.Printing.PrintDocument" /> instance
/// that is used for all of the context menu printing functions.
/// </summary>
public PrintDocument PrintDocument
{
get
{
// Add a try/catch pair since the users of the control can't catch this one
try
{
if (_pdSave == null)
{
_pdSave = new PrintDocument();
_pdSave.PrintPage += new PrintPageEventHandler(Graph_PrintPage);
}
}
catch (Exception exception)
{
MessageBox.Show(exception.Message);
}
return _pdSave;
}
set { _pdSave = value; }
}
/// <summary>
/// Display a <see cref="PageSetupDialog" /> to the user, allowing them to modify
/// the print settings for this <see cref="ZedGraphControl" />.
/// </summary>
public void DoPageSetup()
{
PrintDocument pd = PrintDocument;
// Add a try/catch pair since the users of the control can't catch this one
try
{
if (pd != null)
{
//pd.PrintPage += new PrintPageEventHandler( GraphPrintPage );
PageSetupDialog setupDlg = new PageSetupDialog();
setupDlg.Document = pd;
if (setupDlg.ShowDialog() == DialogResult.OK)
{
pd.PrinterSettings = setupDlg.PrinterSettings;
pd.DefaultPageSettings = setupDlg.PageSettings;
// BUG in PrintDocument!!! Converts in/mm repeatedly
// http://support.microsoft.com/?id=814355
// from http://www.vbinfozine.com/tpagesetupdialog.shtml, by Palo Mraz
//if ( System.Globalization.RegionInfo.CurrentRegion.IsMetric )
//{
// setupDlg.Document.DefaultPageSettings.Margins = PrinterUnitConvert.Convert(
// setupDlg.Document.DefaultPageSettings.Margins,
// PrinterUnit.Display, PrinterUnit.TenthsOfAMillimeter );
//}
}
}
}
catch (Exception exception)
{
MessageBox.Show(exception.Message);
}
}
/// <summary>
/// Display a <see cref="PrintDialog" /> to the user, allowing them to select a
/// printer and print the <see cref="MasterPane" /> contained in this
/// <see cref="ZedGraphControl" />.
/// </summary>
public void DoPrint()
{
// Add a try/catch pair since the users of the control can't catch this one
try
{
PrintDocument pd = PrintDocument;
if (pd != null)
{
//pd.PrintPage += new PrintPageEventHandler( Graph_PrintPage );
PrintDialog pDlg = new PrintDialog();
pDlg.Document = pd;
if (pDlg.ShowDialog() == DialogResult.OK)
pd.Print();
}
}
catch (Exception exception)
{
MessageBox.Show(exception.Message);
}
}
/// <summary>
/// Display a <see cref="PrintPreviewDialog" />, allowing the user to preview and
/// subsequently print the <see cref="MasterPane" /> contained in this
/// <see cref="ZedGraphControl" />.
/// </summary>
public void DoPrintPreview()
{
// Add a try/catch pair since the users of the control can't catch this one
try
{
PrintDocument pd = PrintDocument;
if (pd != null)
{
PrintPreviewDialog ppd = new PrintPreviewDialog();
//pd.PrintPage += new PrintPageEventHandler( Graph_PrintPage );
ppd.Document = pd;
ppd.Show(this);
}
}
catch (Exception exception)
{
MessageBox.Show(exception.Message);
}
}
#endregion
#region Zoom States
/// <summary>
/// Save the current states of the GraphPanes to a separate collection. Save a single
/// (<see paramref="primaryPane" />) GraphPane if the panes are not synchronized
/// (see <see cref="IsSynchronizeXAxes" /> and <see cref="IsSynchronizeYAxes" />),
/// or save a list of states for all GraphPanes if the panes are synchronized.
/// </summary>
/// <param name="primaryPane">The primary GraphPane on which zoom/pan/scroll operations
/// are taking place</param>
/// <param name="type">The <see cref="ZoomState.StateType" /> that describes the
/// current operation</param>
/// <returns>The <see cref="ZoomState" /> that corresponds to the
/// <see paramref="primaryPane" />.
/// </returns>
private ZoomState ZoomStateSave(GraphPane primaryPane, ZoomState.StateType type)
{
ZoomStateClear();
if (_isSynchronizeXAxes || _isSynchronizeYAxes)
{
foreach (GraphPane pane in _masterPane._paneList)
{
ZoomState state = new ZoomState(pane, type);
if (pane == primaryPane)
_zoomState = state;
_zoomStateStack.Add(state);
}
}
else
_zoomState = new ZoomState(primaryPane, type);
return _zoomState;
}
/// <summary>
/// Restore the states of the GraphPanes to a previously saved condition (via
/// <see cref="ZoomStateSave" />. This is essentially an "undo" for live
/// pan and scroll actions. Restores a single
/// (<see paramref="primaryPane" />) GraphPane if the panes are not synchronized
/// (see <see cref="IsSynchronizeXAxes" /> and <see cref="IsSynchronizeYAxes" />),
/// or save a list of states for all GraphPanes if the panes are synchronized.
/// </summary>
/// <param name="primaryPane">The primary GraphPane on which zoom/pan/scroll operations
/// are taking place</param>
private void ZoomStateRestore(GraphPane primaryPane)
{
if (_isSynchronizeXAxes || _isSynchronizeYAxes)
{
for (int i = 0; i < _masterPane._paneList.Count; i++)
{
if (i < _zoomStateStack.Count)
_zoomStateStack[i].ApplyState(_masterPane._paneList[i]);
}
}
else if (_zoomState != null)
_zoomState.ApplyState(primaryPane);
ZoomStateClear();
}
/// <summary>
/// Place the previously saved states of the GraphPanes on the individual GraphPane
/// <see cref="ZedGraph.GraphPane.ZoomStack" /> collections. This provides for an
/// option to undo the state change at a later time. Save a single
/// (<see paramref="primaryPane" />) GraphPane if the panes are not synchronized
/// (see <see cref="IsSynchronizeXAxes" /> and <see cref="IsSynchronizeYAxes" />),
/// or save a list of states for all GraphPanes if the panes are synchronized.
/// </summary>
/// <param name="primaryPane">The primary GraphPane on which zoom/pan/scroll operations
/// are taking place</param>
/// <returns>The <see cref="ZoomState" /> that corresponds to the
/// <see paramref="primaryPane" />.
/// </returns>
private void ZoomStatePush(GraphPane primaryPane)
{
if (_isSynchronizeXAxes || _isSynchronizeYAxes)
{
for (int i = 0; i < _masterPane._paneList.Count; i++)
{
if (i < _zoomStateStack.Count)
_masterPane._paneList[i].ZoomStack.Add(_zoomStateStack[i]);
}
}
else if (_zoomState != null)
primaryPane.ZoomStack.Add(_zoomState);
ZoomStateClear();
}
/// <summary>
/// Clear the collection of saved states.
/// </summary>
private void ZoomStateClear()
{
_zoomStateStack.Clear();
_zoomState = null;
}
/// <summary>
/// Clear all states from the undo stack for each GraphPane.
/// </summary>
private void ZoomStatePurge()
{
foreach (GraphPane pane in _masterPane._paneList)
pane.ZoomStack.Clear();
}
#endregion
}
}