//============================================================================ //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 //============================================================================= #region Using directives using System; using System.Text; using System.Drawing; using System.Collections; using System.Runtime.Serialization; using System.Security.Permissions; #endregion namespace DrawGraph { /// /// A collection class containing a list of objects /// organized together in some form. /// /// /// John Champion /// $Revision: 3.24 $ $Date: 2007/06/02 06:56:03 $ [Serializable] public class MasterPane : PaneBase, ICloneable, ISerializable, IDeserializationCallback { #region Fields /// /// Private field that holds a collection of objects for inclusion /// in this . Use the public property /// to access this collection. /// internal PaneList _paneList; /// /// Private field that sets the amount of space between the GraphPanes. Use the public property /// to access this value; /// internal float _innerPaneGap; /// ///Private field that stores a boolean value which signifies whether all ///s in the chart use the same entries in their /// If set to true, only one set of entries will be displayed in ///this instance. If set to false, this instance will display all ///entries from all s. /// private bool _isUniformLegendEntries; /// /// private field that determines if the /// /// function will automatically set /// the of each in the /// such that the scale factors have the same value. /// private bool _isCommonScaleFactor; /// /// private field that saves the paneLayout format specified when /// was called. This value will /// default to if /// (or an overload) was never called. /// internal PaneLayout _paneLayout; /// /// Private field that stores the boolean value that determines whether /// is specifying rows or columns. /// internal bool _isColumnSpecified; /// /// private field that stores the row/column item count that was specified to the /// method. This values will be /// null if was never called. /// internal int[] _countList; /// /// private field that stores the row/column size proportional values as specified /// to the method. This /// value will be null if /// was never called. /// internal float[] _prop; /* /// /// private field to store the instance, which /// manages the persistence and handling of pane layout information. /// private PaneLayoutMgr _paneLayoutMgr; */ #endregion #region Defaults /// /// A simple struct that defines the /// default property values for the class. /// public new struct Default { /// /// The default pane layout for /// /// method calls. /// /// /// /// /// /// public static PaneLayout PaneLayout = PaneLayout.SquareColPreferred; /// /// The default value for the property. /// This is the size of the margin between adjacent /// objects, in units of points (1/72 inch). /// /// public static float InnerPaneGap = 10; /// /// The default value for the property for /// the class. /// public static bool IsShowLegend = false; /// /// The default value for the property. /// public static bool IsUniformLegendEntries = false; /// /// The default value for the property. /// public static bool IsCommonScaleFactor = false; } #endregion #region Properties /// /// Gets or sets the collection instance that holds the list of /// objects that are included in this . /// /// /// public PaneList PaneList { get { return _paneList; } set { _paneList = value; } } /* /// /// Gets the instance, which manages the pane layout /// settings, and handles the layout functions. /// /// /// /// /// /// public PaneLayoutMgr PaneLayoutMgr { get { return _paneLayoutMgr; } } */ /// /// Gets or sets the size of the margin between adjacent /// objects. /// /// This property is scaled according to , /// based on . The default value comes from /// . /// /// The value is in points (1/72nd inch). public float InnerPaneGap { get { return _innerPaneGap; } set { _innerPaneGap = value; } } /// /// Gets or set the value of the /// public bool IsUniformLegendEntries { get { return (_isUniformLegendEntries); } set { _isUniformLegendEntries = value; } } /// /// Gets or sets a value that determines if the /// method will automatically set the /// /// of each in the such that the /// scale factors have the same value. /// /// /// The scale factors, calculated by , determine /// scaled font sizes, tic lengths, etc. This function will insure that for /// multiple graphpanes, a certain specified font size will be the same for /// all the panes. /// /// /// /// /// /// public bool IsCommonScaleFactor { get { return _isCommonScaleFactor; } set { _isCommonScaleFactor = value; } } #endregion #region Constructors /// /// Default constructor for the class. Sets the to (0, 0, 500, 375). /// public MasterPane() : this( "", new RectangleF( 0, 0, 500, 375 ) ) { } /// /// Default constructor for the class. Specifies the of /// the , and the size of the . /// public MasterPane( string title, RectangleF paneRect ) : base( title, paneRect ) { _innerPaneGap = Default.InnerPaneGap; //_paneLayoutMgr = new PaneLayoutMgr(); _isUniformLegendEntries = Default.IsUniformLegendEntries ; _isCommonScaleFactor = Default.IsCommonScaleFactor; _paneList = new PaneList(); _legend.IsVisible = Default.IsShowLegend; InitLayout(); } private void InitLayout() { _paneLayout = Default.PaneLayout; _countList = null; _isColumnSpecified = false; _prop = null; } /// /// The Copy Constructor - Make a deep-copy clone of this class instance. /// /// The object from which to copy public MasterPane( MasterPane rhs ) : base( rhs ) { // copy all the value types //_paneLayoutMgr = rhs._paneLayoutMgr.Clone(); _innerPaneGap = rhs._innerPaneGap; _isUniformLegendEntries = rhs._isUniformLegendEntries; _isCommonScaleFactor = rhs._isCommonScaleFactor; // Then, fill in all the reference types with deep copies _paneList = rhs._paneList.Clone(); _paneLayout = rhs._paneLayout; _countList = rhs._countList; _isColumnSpecified = rhs._isColumnSpecified; _prop = rhs._prop; } /// /// Implement the interface in a typesafe manner by just /// calling the typed version of to make a deep copy. /// /// A deep copy of this object object ICloneable.Clone() { return this.Clone(); } /// /// Typesafe, deep-copy clone method. /// /// A new, independent copy of this class public MasterPane Clone() { return new MasterPane( this ); } #endregion #region Serialization /// /// Current schema value that defines the version of the serialized file /// // schema changed to 2 with addition of 'prop' public const int schema2 = 10; /// /// Constructor for deserializing objects /// /// A instance that defines the serialized data /// /// A instance that contains the serialized data /// protected MasterPane( SerializationInfo info, StreamingContext context ) : base( info, context ) { // The schema value is just a file version parameter. You can use it to make future versions // backwards compatible as new member variables are added to classes int sch = info.GetInt32( "schema2" ); _paneList = (PaneList) info.GetValue( "paneList", typeof(PaneList) ); //_paneLayoutMgr = (PaneLayoutMgr) info.GetValue( "paneLayoutMgr", typeof(PaneLayoutMgr) ); _innerPaneGap = info.GetSingle( "innerPaneGap" ); _isUniformLegendEntries = info.GetBoolean( "isUniformLegendEntries" ); _isCommonScaleFactor = info.GetBoolean( "isCommonScaleFactor" ); _paneLayout = (PaneLayout)info.GetValue( "paneLayout", typeof( PaneLayout ) ); _countList = (int[])info.GetValue( "countList", typeof( int[] ) ); _isColumnSpecified = info.GetBoolean( "isColumnSpecified" ); _prop = (float[])info.GetValue( "prop", typeof( float[] ) ); } /// /// Populates a instance with the data needed to serialize the target object /// /// A instance that defines the serialized data /// A instance that contains the serialized data [SecurityPermissionAttribute(SecurityAction.Demand,SerializationFormatter=true)] public override void GetObjectData( SerializationInfo info, StreamingContext context ) { base.GetObjectData( info, context ); info.AddValue( "schema2", schema2 ); info.AddValue( "paneList", _paneList ); //info.AddValue( "paneLayoutMgr", _paneLayoutMgr ); info.AddValue( "innerPaneGap", _innerPaneGap ); info.AddValue( "isUniformLegendEntries", _isUniformLegendEntries ); info.AddValue( "isCommonScaleFactor", _isCommonScaleFactor ); info.AddValue( "paneLayout", _paneLayout ); info.AddValue( "countList", _countList ); info.AddValue( "isColumnSpecified", _isColumnSpecified ); info.AddValue( "prop", _prop ); } /// /// Respond to the callback when the MasterPane objects are fully initialized. /// /// public void OnDeserialization(object sender) { Bitmap bitmap = new Bitmap( 10, 10 ); Graphics g = Graphics.FromImage( bitmap ); ReSize( g, _rect ); } #endregion #region List Methods /// /// Indexer to access the specified object from /// by its ordinal position in the list. /// /// The ordinal position (zero-based) of the /// object to be accessed. /// A object reference. public GraphPane this[ int index ] { get { return( (GraphPane) _paneList[index] ); } set { _paneList[index] = value; } } /// /// Indexer to access the specified object from /// by its string. /// /// The string title of the /// object to be accessed. /// A object reference. public GraphPane this[ string title ] { get { return _paneList[title]; } } /// /// Add a object to the collection at the end of the list. /// /// A reference to the object to /// be added /// public void Add( GraphPane pane ) { _paneList.Add( pane ); } /// /// Call for all objects in the /// list. /// /// /// This overload of AxisChange just uses the default Graphics instance for the screen. /// If you have a Graphics instance available from your Windows Form, you should use /// the overload instead. /// public void AxisChange() { using ( Graphics g = Graphics.FromHwnd( IntPtr.Zero ) ) AxisChange( g ); } /// /// Call for all objects in the /// list. /// /// /// A graphic device object to be drawn into. This is normally e.Graphics from the /// PaintEventArgs argument to the Paint() method. /// public void AxisChange( Graphics g ) { foreach ( GraphPane pane in _paneList ) pane.AxisChange( g ); } /// /// Redo the layout using the current size of the , /// and also handle resizing the /// contents by calling . /// /// This method will use the pane layout that was specified by a call to /// . If /// has not previously been called, /// it will default to . /// /// /// A graphic device object to be drawn into. This is normally e.Graphics from the /// PaintEventArgs argument to the Paint() method. /// /// /// /// /// public void ReSize( Graphics g ) { ReSize( g, _rect ); } /// /// Change the size of the , and also handle resizing the /// contents by calling . /// /// This method will use the pane layout that was specified by a call to /// . If /// has not previously been called, /// it will default to . /// /// /// A graphic device object to be drawn into. This is normally e.Graphics from the /// PaintEventArgs argument to the Paint() method. /// /// /// /// /// /// public override void ReSize( Graphics g, RectangleF rect ) { _rect = rect; DoLayout( g ); CommonScaleFactor(); } /// /// Method that forces the scale factor calculations /// (via ), /// to give a common scale factor for all objects in the /// . /// /// /// This will make it such that a given font size will result in the same output font /// size for all 's. Note that this does not make the scale /// factor for the 's the same as that of the /// . /// /// public void CommonScaleFactor() { if ( _isCommonScaleFactor ) { // Find the maximum scaleFactor of all the GraphPanes float maxFactor = 0; foreach ( GraphPane pane in PaneList ) { pane.BaseDimension = PaneBase.Default.BaseDimension; float scaleFactor = pane.CalcScaleFactor(); maxFactor = scaleFactor > maxFactor ? scaleFactor : maxFactor; } // Now, calculate the base dimension foreach ( GraphPane pane in PaneList ) { float scaleFactor = pane.CalcScaleFactor(); pane.BaseDimension *= scaleFactor / maxFactor; } } } /// /// Render all the objects in the to the /// specified graphics device. /// /// This method should be part of the Paint() update process. Calling this routine /// will redraw all /// features of all the items. No preparation is required other than /// instantiated objects that have been added to the list with the /// method. /// /// /// A graphic device object to be drawn into. This is normally e.Graphics from the /// PaintEventArgs argument to the Paint() method. /// public override void Draw( Graphics g ) { // Draw the pane border & background fill, the title, and the GraphObj objects that lie at // ZOrder.GBehindAll base.Draw( g ); if ( _rect.Width <= 1 || _rect.Height <= 1 ) return; float scaleFactor = CalcScaleFactor(); // Clip everything to the rect g.SetClip( _rect ); // For the MasterPane, All GraphItems go behind the GraphPanes, except those that // are explicity declared as ZOrder.AInFront _graphObjList.Draw( g, this, scaleFactor, ZOrder.G_BehindChartFill ); _graphObjList.Draw( g, this, scaleFactor, ZOrder.E_BehindCurves ); _graphObjList.Draw( g, this, scaleFactor, ZOrder.D_BehindAxis ); _graphObjList.Draw( g, this, scaleFactor, ZOrder.C_BehindChartBorder ); // Reset the clipping g.ResetClip(); foreach ( GraphPane pane in _paneList ) pane.Draw( g ); // Clip everything to the rect g.SetClip( _rect ); _graphObjList.Draw( g, this, scaleFactor, ZOrder.B_BehindLegend ); // Recalculate the legend rect, just in case it has not yet been done // innerRect is the area for the GraphPane's RectangleF innerRect = CalcClientRect( g, scaleFactor ); _legend.CalcRect( g, this, scaleFactor, ref innerRect ); _legend.Draw( g, this, scaleFactor ); _graphObjList.Draw( g, this, scaleFactor, ZOrder.A_InFront ); // Reset the clipping g.ResetClip(); } /// /// Find the pane and the object within that pane that lies closest to the specified /// mouse (screen) point. /// /// /// This method first finds the within the list that contains /// the specified mouse point. It then calls the /// method to determine which object, if any, was clicked. With the exception of the /// , all the parameters in this method are identical to those /// in the method. /// If the mouse point lies within the of any /// item, then that pane will be returned (otherwise it will be /// null). Further, within the selected pane, if the mouse point is within the /// bounding box of any of the items (or in the case /// of and , within /// pixels), then the object will be returned. /// You must check the type of the object to determine what object was /// selected (for example, "if ( object is Legend ) ..."). The /// parameter returns the index number of the item /// within the selected object (such as the point number within a /// object. /// /// The screen point, in pixel coordinates. /// /// A graphic device object to be drawn into. This is normally e.Graphics from the /// PaintEventArgs argument to the Paint() method. /// /// A reference to the object that was clicked. /// A reference to the nearest object to the /// specified screen point. This can be any of , /// , , /// , , or . /// Note: If the pane title is selected, then the object /// will be returned. /// /// The index number of the item within the selected object /// (where applicable). For example, for a object, /// will be the index number of the nearest data point, /// accessible via CurveItem.Points[index]. /// index will be -1 if no data points are available. /// true if a was found, false otherwise. /// public bool FindNearestPaneObject( PointF mousePt, Graphics g, out GraphPane pane, out object nearestObj, out int index ) { pane = null; nearestObj = null; index = -1; GraphObj saveGraphItem = null; int saveIndex = -1; float scaleFactor = CalcScaleFactor(); // See if the point is in a GraphObj // If so, just save the object and index so we can see if other overlying objects were // intersected as well. if ( this.GraphObjList.FindPoint( mousePt, this, g, scaleFactor, out index ) ) { saveGraphItem = this.GraphObjList[index]; saveIndex = index; // If it's an "In-Front" item, then just return it if ( saveGraphItem.ZOrder == ZOrder.A_InFront ) { nearestObj = saveGraphItem; index = saveIndex; return true; } } foreach ( GraphPane tPane in _paneList ) { if ( tPane.Rect.Contains( mousePt ) ) { pane = tPane; return tPane.FindNearestObject( mousePt, g, out nearestObj, out index ); } } // If no items were found in the GraphPanes, then return the item found on the MasterPane (if any) if ( saveGraphItem != null ) { nearestObj = saveGraphItem; index = saveIndex; return true; } return false; } /// /// Find the within the that contains the /// within its . /// /// The mouse point location where you want to search /// A object that contains the mouse point, or /// null if no was found. public GraphPane FindPane( PointF mousePt ) { foreach ( GraphPane pane in _paneList ) { if ( pane.Rect.Contains( mousePt ) ) return pane; } return null; } /// /// Find the within the that contains the /// within its . /// /// The mouse point location where you want to search /// A object that contains the mouse point, or /// null if no was found. public GraphPane FindChartRect( PointF mousePt ) { foreach ( GraphPane pane in _paneList ) { if ( pane.Chart._rect.Contains( mousePt ) ) return pane; } return null; } #endregion #region Layout Methods /// The SetLayout() methods setup the desired layout of the /// objects within a . These functions /// do not make any changes, they merely set the parameters so that future calls /// to or /// will use the desired layout.

/// The layout options include a set of "canned" layouts provided by the /// enumeration, options to just set a specific /// number of rows and columns of panes (and all pane sizes are the same), and more /// customized options of specifying the number or rows in each column or the number of /// columns in each row, along with proportional values that determine the size of each /// individual column or row. ///
/// /// Automatically set all of the 's in /// the list to a pre-defined layout configuration from a /// enumeration. /// /// This method uses a enumeration to describe the type of layout /// to be used. Overloads are available that provide other layout options /// A enumeration that describes how /// the panes should be laid out within the . /// /// A graphic device object to be drawn into. This is normally created with a call to /// the CreateGraphics() method of the Control or Form. /// /// /// /// public void SetLayout( Graphics g, PaneLayout paneLayout ) { InitLayout(); _paneLayout = paneLayout; DoLayout( g ); } /// /// Automatically set all of the 's in /// the list to a reasonable configuration. /// /// This method explicitly specifies the number of rows and columns to use /// in the layout, and all objects will have the same size. /// Overloads are available that provide other layout options /// /// A graphic device object to be drawn into. This is normally created with a call to /// the CreateGraphics() method of the Control or Form. /// /// The number of rows of objects /// to include in the layout /// The number of columns of objects /// to include in the layout /// /// /// public void SetLayout( Graphics g, int rows, int columns ) { InitLayout(); if ( rows < 1 ) rows = 1; if ( columns < 1 ) columns = 1; int[] countList = new int[rows]; for ( int i = 0; i < rows; i++ ) countList[i] = columns; SetLayout( g, true, countList, null ); } /// /// Automatically set all of the 's in /// the list to the specified configuration. /// /// This method specifies the number of rows in each column, or the number of /// columns in each row, allowing for irregular layouts. Overloads are available that /// provide other layout options. /// /// /// A graphic device object to be drawn into. This is normally created with a call to /// the CreateGraphics() method of the Control or Form. /// /// Specifies whether the number of columns in each row, or /// the number of rows in each column will be specified. A value of true indicates the /// number of columns in each row are specified in . /// An integer array specifying either the number of columns in /// each row or the number of rows in each column, depending on the value of /// . /// /// /// public void SetLayout( Graphics g, bool isColumnSpecified, int[] countList ) { SetLayout( g, isColumnSpecified, countList, null ); } /// /// Automatically set all of the 's in /// the list to the specified configuration. /// /// This method specifies the number of panes in each row or column, allowing for /// irregular layouts. /// This method specifies the number of rows in each column, or the number of /// columns in each row, allowing for irregular layouts. Additionally, a /// parameter is provided that allows varying column or /// row sizes. Overloads for SetLayout() are available that provide other layout options. /// /// /// A graphic device object to be drawn into. This is normally created with a call to /// the CreateGraphics() method of the Control or Form. /// /// Specifies whether the number of columns in each row, or /// the number of rows in each column will be specified. A value of true indicates the /// number of columns in each row are specified in . /// An integer array specifying either the number of columns in /// each row or the number of rows in each column, depending on the value of /// . /// An array of float values specifying proportional sizes for each /// row or column. Note that these proportions apply to the non-specified dimension -- that is, /// if is true, then these proportions apply to the row /// heights, and if is false, then these proportions apply /// to the column widths. The values in this array are arbitrary floats -- the dimension of /// any given row or column is that particular proportional value divided by the sum of all /// the values. For example, let be true, and /// is an array with values of { 1.0, 2.0, 3.0 }. The sum of /// those values is 6.0. Therefore, the first row is 1/6th of the available height, the /// second row is 2/6th's of the available height, and the third row is 3/6th's of the /// available height. /// /// /// /// public void SetLayout( Graphics g, bool isColumnSpecified, int[] countList, float[] proportion ) { InitLayout(); // use defaults if the parameters are invalid if ( countList != null && countList.Length > 0 ) { _prop = new float[countList.Length]; // Sum up the total proportional factors float sumProp = 0.0f; for ( int i = 0; i < countList.Length; i++ ) { _prop[i] = ( proportion == null || proportion.Length <= i || proportion[i] < 1e-10 ) ? 1.0f : proportion[i]; sumProp += _prop[i]; } // Make prop sum to 1.0 for ( int i = 0; i < countList.Length; i++ ) _prop[i] /= sumProp; _isColumnSpecified = isColumnSpecified; _countList = countList; DoLayout( g ); } } /// /// Modify the sizes of each /// such that they fit within the /// in a pre-configured layout. /// /// The method (and overloads) is /// used for setting the layout configuration. /// /// /// /// public void DoLayout( Graphics g ) { if ( _countList != null ) DoLayout( g, _isColumnSpecified, _countList, _prop ); else { int count = _paneList.Count; if ( count == 0 ) return; int rows, cols, root = (int)( Math.Sqrt( (double)count ) + 0.9999999 ); //float[] widthList = new float[5]; switch ( _paneLayout ) { case PaneLayout.ForceSquare: rows = root; cols = root; DoLayout( g, rows, cols ); break; case PaneLayout.SingleColumn: rows = count; cols = 1; DoLayout( g, rows, cols ); break; case PaneLayout.SingleRow: rows = 1; cols = count; DoLayout( g, rows, cols ); break; default: case PaneLayout.SquareColPreferred: rows = root; cols = root; if ( count <= root * ( root - 1 ) ) rows--; DoLayout( g, rows, cols ); break; case PaneLayout.SquareRowPreferred: rows = root; cols = root; if ( count <= root * ( root - 1 ) ) cols--; DoLayout( g, rows, cols ); break; case PaneLayout.ExplicitCol12: DoLayout( g, true, new int[2] { 1, 2 }, null ); break; case PaneLayout.ExplicitCol21: DoLayout( g, true, new int[2] { 2, 1 }, null ); break; case PaneLayout.ExplicitCol23: DoLayout( g, true, new int[2] { 2, 3 }, null ); break; case PaneLayout.ExplicitCol32: DoLayout( g, true, new int[2] { 3, 2 }, null ); break; case PaneLayout.ExplicitRow12: DoLayout( g, false, new int[2] { 1, 2 }, null ); break; case PaneLayout.ExplicitRow21: DoLayout( g, false, new int[2] { 2, 1 }, null ); break; case PaneLayout.ExplicitRow23: DoLayout( g, false, new int[2] { 2, 3 }, null ); break; case PaneLayout.ExplicitRow32: DoLayout( g, false, new int[2] { 3, 2 }, null ); break; } } } /// /// Internal method that applies a previously set layout with a specific /// row and column count. This method is only called by /// . /// internal void DoLayout( Graphics g, int rows, int columns ) { if ( rows < 1 ) rows = 1; if ( columns < 1 ) columns = 1; int[] countList = new int[rows]; for ( int i = 0; i < rows; i++ ) countList[i] = columns; DoLayout( g, true, countList, null ); } /// /// Internal method that applies a previously set layout with a rows per column or /// columns per row configuration. This method is only called by /// . /// internal void DoLayout( Graphics g, bool isColumnSpecified, int[] countList, float[] proportion ) { // calculate scaleFactor on "normal" pane size (BaseDimension) float scaleFactor = CalcScaleFactor(); // innerRect is the area for the GraphPane's RectangleF innerRect = CalcClientRect( g, scaleFactor ); _legend.CalcRect( g, this, scaleFactor, ref innerRect ); // scaled InnerGap is the area between the GraphPane.Rect's float scaledInnerGap = (float)( _innerPaneGap * scaleFactor ); int iPane = 0; if ( isColumnSpecified ) { int rows = countList.Length; float y = 0.0f; for ( int rowNum = 0; rowNum < rows; rowNum++ ) { float propFactor = _prop == null ? 1.0f / rows : _prop[rowNum]; float height = ( innerRect.Height - (float)( rows - 1 ) * scaledInnerGap ) * propFactor; int columns = countList[rowNum]; if ( columns <= 0 ) columns = 1; float width = ( innerRect.Width - (float)( columns - 1 ) * scaledInnerGap ) / (float)columns; for ( int colNum = 0; colNum < columns; colNum++ ) { if ( iPane >= _paneList.Count ) return; this[iPane].Rect = new RectangleF( innerRect.X + colNum * ( width + scaledInnerGap ), innerRect.Y + y, width, height ); iPane++; } y += height + scaledInnerGap; } } else { int columns = countList.Length; float x = 0.0f; for ( int colNum = 0; colNum < columns; colNum++ ) { float propFactor = _prop == null ? 1.0f / columns : _prop[colNum]; float width = ( innerRect.Width - (float)( columns - 1 ) * scaledInnerGap ) * propFactor; int rows = countList[colNum]; if ( rows <= 0 ) rows = 1; float height = ( innerRect.Height - (float)( rows - 1 ) * scaledInnerGap ) / (float)rows; for ( int rowNum = 0; rowNum < rows; rowNum++ ) { if ( iPane >= _paneList.Count ) return; this[iPane].Rect = new RectangleF( innerRect.X + x, innerRect.Y + rowNum * ( height + scaledInnerGap ), width, height ); iPane++; } x += width + scaledInnerGap; } } } /* /// /// Automatically set all of the 's in /// the list to a reasonable configuration. /// /// This method explicitly specifies the number of rows and columns to use in the layout. /// A more automatic overload, using a enumeration, is available. /// /// A graphic device object to be drawn into. This is normally e.Graphics from the /// PaintEventArgs argument to the Paint() method. /// /// The number of rows of objects /// to include in the layout /// The number of columns of objects /// to include in the layout public void DoPaneLayout( Graphics g, int rows, int columns ) { // save the layout settings for future reference _countList = null; _rows = rows; _columns = columns; // calculate scaleFactor on "normal" pane size (BaseDimension) float scaleFactor = this.CalcScaleFactor(); // innerRect is the area for the GraphPane's RectangleF innerRect = CalcClientRect( g, scaleFactor ); _legend.CalcRect( g, this, scaleFactor, ref innerRect ); // scaled InnerGap is the area between the GraphPane.Rect's float scaledInnerGap = (float)( _innerPaneGap * scaleFactor ); float width = ( innerRect.Width - (float)( columns - 1 ) * scaledInnerGap ) / (float)columns; float height = ( innerRect.Height - (float)( rows - 1 ) * scaledInnerGap ) / (float)rows; int i = 0; foreach ( GraphPane pane in _paneList ) { float rowNum = (float)( i / columns ); float colNum = (float)( i % columns ); pane.Rect = new RectangleF( innerRect.X + colNum * ( width + scaledInnerGap ), innerRect.Y + rowNum * ( height + scaledInnerGap ), width, height ); i++; } } */ #endregion } }