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