984 lines
41 KiB
C#
984 lines
41 KiB
C#
//============================================================================
|
||
//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.Drawing;
|
||
using System.Runtime.Serialization;
|
||
using System.Security.Permissions;
|
||
|
||
namespace DrawGraph
|
||
{
|
||
/// <summary>
|
||
/// This class encapsulates the chart <see cref="Legend"/> that is displayed
|
||
/// in the <see cref="GraphPane"/>
|
||
/// </summary>
|
||
///
|
||
/// <author> John Champion </author>
|
||
/// <version> $Revision: 3.38 $ $Date: 2007/03/11 02:08:16 $ </version>
|
||
[Serializable]
|
||
public class Legend : ICloneable, ISerializable
|
||
{
|
||
#region private Fields
|
||
|
||
/// <summary> Private field to hold the bounding rectangle around the legend.
|
||
/// This bounding rectangle varies with the number of legend entries, font sizes,
|
||
/// etc., and is re-calculated by <see cref="Legend.CalcRect"/> at each redraw.
|
||
/// Use the public readonly property <see cref="Legend.Rect"/> to access this
|
||
/// rectangle.
|
||
/// </summary>
|
||
private RectangleF _rect;
|
||
/// <summary>Private field to hold the legend location setting. This field
|
||
/// contains the <see cref="LegendPos"/> enum type to specify the area of
|
||
/// the graph where the legend will be positioned. Use the public property
|
||
/// <see cref="LegendPos"/> to access this value.
|
||
/// </summary>
|
||
/// <seealso cref="Default.Position"/>
|
||
private LegendPos _position;
|
||
/// <summary>
|
||
/// Private field to enable/disable horizontal stacking of the legend entries.
|
||
/// If this value is false, then the legend entries will always be a single column.
|
||
/// Use the public property <see cref="IsHStack"/> to access this value.
|
||
/// </summary>
|
||
/// <seealso cref="Default.IsHStack"/>
|
||
private bool _isHStack;
|
||
/// <summary>
|
||
/// Private field to enable/disable drawing of the entire legend.
|
||
/// If this value is false, then the legend will not be drawn.
|
||
/// Use the public property <see cref="IsVisible"/> to access this value.
|
||
/// </summary>
|
||
private bool _isVisible;
|
||
/// <summary>
|
||
/// Private field that stores the <see cref="ZedGraph.Fill"/> data for this
|
||
/// <see cref="Legend"/>. Use the public property <see cref="Fill"/> to
|
||
/// access this value.
|
||
/// </summary>
|
||
private Fill _fill;
|
||
/// <summary>
|
||
/// Private field that stores the <see cref="ZedGraph.Border"/> data for this
|
||
/// <see cref="Legend"/>. Use the public property <see cref="Border"/> to
|
||
/// access this value.
|
||
/// </summary>
|
||
private Border _border;
|
||
/// <summary>
|
||
/// Private field to maintain the <see cref="FontSpec"/> class that
|
||
/// maintains font attributes for the entries in this legend. Use
|
||
/// the <see cref="FontSpec"/> property to access this class.
|
||
/// </summary>
|
||
private FontSpec _fontSpec;
|
||
/// <summary>
|
||
/// Private field to maintain the <see cref="Legend"/> location. This object
|
||
/// is only applicable if the <see cref="Position"/> property is set to
|
||
/// <see cref="LegendPos.Float"/>.
|
||
/// </summary>
|
||
private Location _location;
|
||
|
||
/// <summary>
|
||
/// Private temporary field to maintain the number of columns (horizontal stacking) to be used
|
||
/// for drawing the <see cref="Legend"/>. This value is only valid during a draw operation.
|
||
/// </summary>
|
||
private int _hStack;
|
||
/// <summary>
|
||
/// Private temporary field to maintain the width of each column in the
|
||
/// <see cref="Legend"/>. This value is only valid during a draw operation.
|
||
/// </summary>
|
||
private float _legendItemWidth;
|
||
/// <summary>
|
||
/// Private temporary field to maintain the height of each row in the
|
||
/// <see cref="Legend"/>. This value is only valid during a draw operation.
|
||
/// </summary>
|
||
private float _legendItemHeight;
|
||
|
||
/// <summary>
|
||
/// Private field to store the gap between the legend and the chart rectangle.
|
||
/// </summary>
|
||
private float _gap;
|
||
|
||
// CJBL
|
||
/// <summary>
|
||
/// Private field to select output order of legend entries.
|
||
/// </summary>
|
||
private bool _isReverse;
|
||
|
||
/// <summary>
|
||
/// Private temporary field to maintain the characteristic "gap" for the legend.
|
||
/// This is normal the height of the largest font in the legend.
|
||
/// This value is only valid during a draw operation.
|
||
/// </summary>
|
||
private float _tmpSize;
|
||
|
||
#endregion
|
||
|
||
#region Defaults
|
||
/// <summary>
|
||
/// A simple struct that defines the
|
||
/// default property values for the <see cref="Legend"/> class.
|
||
/// </summary>
|
||
public struct Default
|
||
{
|
||
// Default Legend properties
|
||
/// <summary>
|
||
/// The default pen width for the <see cref="Legend"/> border border.
|
||
/// (<see cref="ZedGraph.LineBase.Width"/> property). Units are in pixels.
|
||
/// </summary>
|
||
public static float BorderWidth = 1;
|
||
/// <summary>
|
||
/// The default color for the <see cref="Legend"/> border border.
|
||
/// (<see cref="ZedGraph.LineBase.Color"/> property).
|
||
/// </summary>
|
||
public static Color BorderColor = Color.Black;
|
||
/// <summary>
|
||
/// The default color for the <see cref="Legend"/> background.
|
||
/// (<see cref="ZedGraph.Fill.Color"/> property). Use of this
|
||
/// color depends on the status of the <see cref="ZedGraph.Fill.Type"/>
|
||
/// property.
|
||
/// </summary>
|
||
public static Color FillColor = Color.White;
|
||
/// <summary>
|
||
/// The default custom brush for filling in this <see cref="Legend"/>.
|
||
/// </summary>
|
||
public static Brush FillBrush = null;
|
||
/// <summary>
|
||
/// The default fill mode for the <see cref="Legend"/> background.
|
||
/// </summary>
|
||
public static FillType FillType = FillType.Brush;
|
||
/// <summary>
|
||
/// The default location for the <see cref="Legend"/> on the graph
|
||
/// (<see cref="Legend.Location"/> property). This property is
|
||
/// defined as a <see cref="LegendPos"/> enumeration.
|
||
/// </summary>
|
||
public static LegendPos Position = LegendPos.Top;
|
||
/// <summary>
|
||
/// The default border mode for the <see cref="Legend"/>.
|
||
/// (<see cref="ZedGraph.LineBase.IsVisible"/> property). true
|
||
/// to draw a border around the <see cref="Legend.Rect"/>,
|
||
/// false otherwise.
|
||
/// </summary>
|
||
public static bool IsBorderVisible = true;
|
||
/// <summary>
|
||
/// The default display mode for the <see cref="Legend"/>.
|
||
/// (<see cref="Legend.IsVisible"/> property). true
|
||
/// to show the legend,
|
||
/// false to hide it.
|
||
/// </summary>
|
||
public static bool IsVisible = true;
|
||
/// <summary>
|
||
/// The default fill mode for the <see cref="Legend"/> background
|
||
/// (<see cref="ZedGraph.Fill.Type"/> property).
|
||
/// true to fill-in the background with color,
|
||
/// false to leave the background transparent.
|
||
/// </summary>
|
||
public static bool IsFilled = true;
|
||
/// <summary>
|
||
/// The default horizontal stacking mode for the <see cref="Legend"/>
|
||
/// (<see cref="Legend.IsHStack"/> property).
|
||
/// true to allow horizontal legend item stacking, false to allow
|
||
/// only vertical legend orientation.
|
||
/// </summary>
|
||
public static bool IsHStack = true;
|
||
|
||
/// <summary>
|
||
/// The default font family for the <see cref="Legend"/> entries
|
||
/// (<see cref="ZedGraph.FontSpec.Family"/> property).
|
||
/// </summary>
|
||
public static string FontFamily = "Arial";
|
||
/// <summary>
|
||
/// The default font size for the <see cref="Legend"/> entries
|
||
/// (<see cref="ZedGraph.FontSpec.Size"/> property). Units are
|
||
/// in points (1/72 inch).
|
||
/// </summary>
|
||
public static float FontSize = 12;
|
||
/// <summary>
|
||
/// The default font color for the <see cref="Legend"/> entries
|
||
/// (<see cref="ZedGraph.FontSpec.FontColor"/> property).
|
||
/// </summary>
|
||
public static Color FontColor = Color.Black;
|
||
/// <summary>
|
||
/// The default font bold mode for the <see cref="Legend"/> entries
|
||
/// (<see cref="ZedGraph.FontSpec.IsBold"/> property). true
|
||
/// for a bold typeface, false otherwise.
|
||
/// </summary>
|
||
public static bool FontBold = false;
|
||
/// <summary>
|
||
/// The default font italic mode for the <see cref="Legend"/> entries
|
||
/// (<see cref="ZedGraph.FontSpec.IsItalic"/> property). true
|
||
/// for an italic typeface, false otherwise.
|
||
/// </summary>
|
||
public static bool FontItalic = false;
|
||
/// <summary>
|
||
/// The default font underline mode for the <see cref="Legend"/> entries
|
||
/// (<see cref="ZedGraph.FontSpec.IsUnderline"/> property). true
|
||
/// for an underlined typeface, false otherwise.
|
||
/// </summary>
|
||
public static bool FontUnderline = false;
|
||
/// <summary>
|
||
/// The default color for filling in the scale text background
|
||
/// (see <see cref="ZedGraph.Fill.Color"/> property).
|
||
/// </summary>
|
||
public static Color FontFillColor = Color.White;
|
||
/// <summary>
|
||
/// The default custom brush for filling in the scale text background
|
||
/// (see <see cref="ZedGraph.Fill.Brush"/> property).
|
||
/// </summary>
|
||
public static Brush FontFillBrush = null;
|
||
/// <summary>
|
||
/// The default fill mode for filling in the scale text background
|
||
/// (see <see cref="ZedGraph.Fill.Type"/> property).
|
||
/// </summary>
|
||
public static FillType FontFillType = FillType.None;
|
||
|
||
/// <summary>
|
||
/// The default gap size between the legend and the <see cref="Chart.Rect" />.
|
||
/// This is the default value of <see cref="Legend.Gap" />.
|
||
/// </summary>
|
||
public static float Gap = 0.5f;
|
||
|
||
/// <summary>
|
||
/// Default value for the <see cref="Legend.IsReverse" /> property.
|
||
/// </summary>
|
||
public static bool IsReverse = false;
|
||
}
|
||
#endregion
|
||
|
||
#region Properties
|
||
/// <summary>
|
||
/// Get the bounding rectangle for the <see cref="Legend"/> in screen coordinates
|
||
/// </summary>
|
||
/// <value>A screen rectangle in pixel units</value>
|
||
public RectangleF Rect
|
||
{
|
||
get { return _rect; }
|
||
}
|
||
/// <summary>
|
||
/// Access to the <see cref="ZedGraph.FontSpec"/> class used to render
|
||
/// the <see cref="Legend"/> entries
|
||
/// </summary>
|
||
/// <value>A reference to a <see cref="Legend"/> object</value>
|
||
/// <seealso cref="Default.FontColor"/>
|
||
/// <seealso cref="Default.FontBold"/>
|
||
/// <seealso cref="Default.FontItalic"/>
|
||
/// <seealso cref="Default.FontUnderline"/>
|
||
/// <seealso cref="Default.FontFamily"/>
|
||
/// <seealso cref="Default.FontSize"/>
|
||
public FontSpec FontSpec
|
||
{
|
||
get { return _fontSpec; }
|
||
set
|
||
{
|
||
if (value == null)
|
||
throw new ArgumentNullException("Uninitialized FontSpec in Legend");
|
||
_fontSpec = value;
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// Gets or sets a property that shows or hides the <see cref="Legend"/> entirely
|
||
/// </summary>
|
||
/// <value> true to show the <see cref="Legend"/>, false to hide it </value>
|
||
/// <seealso cref="Default.IsVisible"/>
|
||
public bool IsVisible
|
||
{
|
||
get { return _isVisible; }
|
||
set { _isVisible = value; }
|
||
}
|
||
/// <summary>
|
||
/// The <see cref="Border"/> class used to draw the border border around this <see cref="Legend"/>.
|
||
/// </summary>
|
||
public Border Border
|
||
{
|
||
get { return _border; }
|
||
set { _border = value; }
|
||
}
|
||
/// <summary>
|
||
/// Gets or sets the <see cref="ZedGraph.Fill"/> data for this
|
||
/// <see cref="Legend"/> background.
|
||
/// </summary>
|
||
public Fill Fill
|
||
{
|
||
get { return _fill; }
|
||
set { _fill = value; }
|
||
}
|
||
|
||
/// <summary>
|
||
/// Sets or gets a property that allows the <see cref="Legend"/> items to
|
||
/// stack horizontally in addition to the vertical stacking
|
||
/// </summary>
|
||
/// <value>true to allow horizontal stacking, false otherwise
|
||
/// </value>
|
||
/// <seealso cref="Default.IsHStack"/>
|
||
public bool IsHStack
|
||
{
|
||
get { return _isHStack; }
|
||
set { _isHStack = value; }
|
||
}
|
||
/// <summary>
|
||
/// Sets or gets the location of the <see cref="Legend"/> on the
|
||
/// <see cref="GraphPane"/> using the <see cref="LegendPos"/> enum type
|
||
/// </summary>
|
||
/// <seealso cref="Default.Position"/>
|
||
public LegendPos Position
|
||
{
|
||
get { return _position; }
|
||
set { _position = value; }
|
||
}
|
||
/// <summary>
|
||
/// Gets or sets the <see cref="Location"/> data for the <see cref="Legend"/>.
|
||
/// This property is only applicable if <see cref="Position"/> is set
|
||
/// to <see cref="LegendPos.Float"/>.
|
||
/// </summary>
|
||
public Location Location
|
||
{
|
||
get { return _location; }
|
||
set { _location = value; }
|
||
}
|
||
|
||
/// <summary>
|
||
/// Gets or sets the gap size between the legend and the <see cref="Chart.Rect" />.
|
||
/// </summary>
|
||
/// <remarks>
|
||
/// This is expressed as a fraction of the largest scaled character height for any
|
||
/// of the fonts used in the legend. Each <see cref="CurveItem" /> in the legend can
|
||
/// optionally have its own <see cref="FontSpec" /> specification.
|
||
/// </remarks>
|
||
public float Gap
|
||
{
|
||
get { return _gap; }
|
||
set { _gap = value; }
|
||
}
|
||
|
||
/// <summary>
|
||
/// Gets or sets a value that determines if the legend entries are displayed in normal order
|
||
/// (matching the order in the <see cref="CurveList" />, or in reverse order.
|
||
/// </summary>
|
||
public bool IsReverse
|
||
{
|
||
get { return _isReverse; }
|
||
set { _isReverse = value; }
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Constructors
|
||
/// <summary>
|
||
/// Default constructor that sets all <see cref="Legend"/> properties to default
|
||
/// values as defined in the <see cref="Default"/> class.
|
||
/// </summary>
|
||
public Legend()
|
||
{
|
||
_position = Default.Position;
|
||
_isHStack = Default.IsHStack;
|
||
_isVisible = Default.IsVisible;
|
||
this.Location = new Location(0, 0, CoordType.PaneFraction);
|
||
|
||
_fontSpec = new FontSpec(Default.FontFamily, Default.FontSize,
|
||
Default.FontColor, Default.FontBold,
|
||
Default.FontItalic, Default.FontUnderline,
|
||
Default.FontFillColor, Default.FontFillBrush,
|
||
Default.FontFillType);
|
||
_fontSpec.Border.IsVisible = false;
|
||
|
||
_border = new Border(Default.IsBorderVisible, Default.BorderColor, Default.BorderWidth);
|
||
_fill = new Fill(Default.FillColor, Default.FillBrush, Default.FillType);
|
||
|
||
_gap = Default.Gap;
|
||
|
||
_isReverse = Default.IsReverse;
|
||
}
|
||
|
||
/// <summary>
|
||
/// The Copy Constructor
|
||
/// </summary>
|
||
/// <param name="rhs">The XAxis object from which to copy</param>
|
||
public Legend(Legend rhs)
|
||
{
|
||
_rect = rhs.Rect;
|
||
_position = rhs.Position;
|
||
_isHStack = rhs.IsHStack;
|
||
_isVisible = rhs.IsVisible;
|
||
|
||
_location = rhs.Location;
|
||
_border = rhs.Border.Clone();
|
||
_fill = rhs.Fill.Clone();
|
||
|
||
_fontSpec = rhs.FontSpec.Clone();
|
||
|
||
_gap = rhs._gap;
|
||
|
||
_isReverse = rhs._isReverse;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Implement the <see cref="ICloneable" /> interface in a typesafe manner by just
|
||
/// calling the typed version of <see cref="Clone" />
|
||
/// </summary>
|
||
/// <returns>A deep copy of this object</returns>
|
||
object ICloneable.Clone()
|
||
{
|
||
return this.Clone();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Typesafe, deep-copy clone method.
|
||
/// </summary>
|
||
/// <returns>A new, independent copy of this class</returns>
|
||
public Legend Clone()
|
||
{
|
||
return new Legend(this);
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Serialization
|
||
/// <summary>
|
||
/// Current schema value that defines the version of the serialized file
|
||
/// </summary>
|
||
public const int schema = 11;
|
||
|
||
/// <summary>
|
||
/// Constructor for deserializing objects
|
||
/// </summary>
|
||
/// <param name="info">A <see cref="SerializationInfo"/> instance that defines the serialized data
|
||
/// </param>
|
||
/// <param name="context">A <see cref="StreamingContext"/> instance that contains the serialized data
|
||
/// </param>
|
||
protected Legend(SerializationInfo info, StreamingContext 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("schema");
|
||
|
||
_position = (LegendPos)info.GetValue("position", typeof(LegendPos));
|
||
_isHStack = info.GetBoolean("isHStack");
|
||
_isVisible = info.GetBoolean("isVisible");
|
||
_fill = (Fill)info.GetValue("fill", typeof(Fill));
|
||
_border = (Border)info.GetValue("border", typeof(Border));
|
||
_fontSpec = (FontSpec)info.GetValue("fontSpec", typeof(FontSpec));
|
||
_location = (Location)info.GetValue("location", typeof(Location));
|
||
|
||
_gap = info.GetSingle("gap");
|
||
|
||
if (schema >= 11)
|
||
_isReverse = info.GetBoolean("isReverse");
|
||
}
|
||
/// <summary>
|
||
/// Populates a <see cref="SerializationInfo"/> instance with the data needed to serialize the target object
|
||
/// </summary>
|
||
/// <param name="info">A <see cref="SerializationInfo"/> instance that defines the serialized data</param>
|
||
/// <param name="context">A <see cref="StreamingContext"/> instance that contains the serialized data</param>
|
||
[SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
|
||
public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
|
||
{
|
||
info.AddValue("schema", schema);
|
||
info.AddValue("position", _position);
|
||
info.AddValue("isHStack", _isHStack);
|
||
info.AddValue("isVisible", _isVisible);
|
||
info.AddValue("fill", _fill);
|
||
info.AddValue("border", _border);
|
||
info.AddValue("fontSpec", _fontSpec);
|
||
info.AddValue("location", _location);
|
||
|
||
info.AddValue("gap", _gap);
|
||
info.AddValue("isReverse", _isReverse);
|
||
}
|
||
#endregion
|
||
|
||
#region Rendering Methods
|
||
|
||
/// <summary>
|
||
/// Render the <see cref="Legend"/> to the specified <see cref="Graphics"/> device.
|
||
/// </summary>
|
||
/// <remarks>
|
||
/// This method is normally only called by the Draw method
|
||
/// of the parent <see cref="GraphPane"/> object.
|
||
/// </remarks>
|
||
/// <param name="g">
|
||
/// A graphic device object to be drawn into. This is normally e.Graphics from the
|
||
/// PaintEventArgs argument to the Paint() method.
|
||
/// </param>
|
||
/// <param name="pane">
|
||
/// A reference to the <see cref="PaneBase"/> object that is the parent or
|
||
/// owner of this object.
|
||
/// </param>
|
||
/// <param name="scaleFactor">
|
||
/// The scaling factor to be used for rendering objects. This is calculated and
|
||
/// passed down by the parent <see cref="GraphPane"/> object using the
|
||
/// <see cref="PaneBase.CalcScaleFactor"/> method, and is used to proportionally adjust
|
||
/// font sizes, etc. according to the actual size of the graph.
|
||
/// </param>
|
||
public void Draw(Graphics g, PaneBase pane, float scaleFactor)
|
||
{
|
||
// if the legend is not visible, do nothing
|
||
if (!_isVisible)
|
||
return;
|
||
|
||
// Fill the background with the specified color if required
|
||
_fill.Draw(g, _rect);
|
||
|
||
PaneList paneList = GetPaneList(pane);
|
||
|
||
float halfGap = _tmpSize / 2.0F;
|
||
|
||
// Check for bad data values
|
||
if (_hStack <= 0)
|
||
_hStack = 1;
|
||
if (_legendItemWidth <= 0)
|
||
_legendItemWidth = 100;
|
||
if (_legendItemHeight <= 0)
|
||
_legendItemHeight = _tmpSize;
|
||
|
||
//float gap = pane.ScaledGap( scaleFactor );
|
||
|
||
int iEntry = 0;
|
||
float x, y;
|
||
|
||
// Get a brush for the legend label text
|
||
using (SolidBrush brushB = new SolidBrush(Color.Black))
|
||
{
|
||
foreach (GraphPane tmpPane in paneList)
|
||
{
|
||
// Loop for each curve in the CurveList collection
|
||
//foreach ( CurveItem curve in tmpPane.CurveList )
|
||
int count = tmpPane.CurveList.Count;
|
||
for (int i = 0; i < count; i++)
|
||
{
|
||
CurveItem curve = tmpPane.CurveList[_isReverse ? count - i - 1 : i];
|
||
|
||
if (curve._label._text != "" && curve._label._isVisible)
|
||
{
|
||
// Calculate the x,y (TopLeft) location of the current
|
||
// curve legend label
|
||
// assuming:
|
||
// charHeight/2 for the left margin, plus legendWidth for each
|
||
// horizontal column
|
||
// legendHeight is the line spacing, with no extra margin above
|
||
|
||
x = _rect.Left + halfGap / 2.0F +
|
||
(iEntry % _hStack) * _legendItemWidth;
|
||
y = _rect.Top + (int)(iEntry / _hStack) * _legendItemHeight;
|
||
|
||
// Draw the legend label for the current curve
|
||
FontSpec tmpFont = (curve._label._fontSpec != null) ?
|
||
curve._label._fontSpec : this.FontSpec;
|
||
|
||
// This is required because, for long labels, the centering can affect the
|
||
// position in GDI+.
|
||
tmpFont.StringAlignment = StringAlignment.Near;
|
||
|
||
tmpFont.Draw(g, pane, curve._label._text,
|
||
x + 2.5F * _tmpSize, y + _legendItemHeight / 2.0F,
|
||
AlignH.Left, AlignV.Center, scaleFactor);
|
||
|
||
RectangleF rect = new RectangleF(x, y + _legendItemHeight / 4.0F,
|
||
2 * _tmpSize, _legendItemHeight / 2.0F);
|
||
curve.DrawLegendKey(g, tmpPane, rect, scaleFactor);
|
||
|
||
// maintain a curve count for positioning
|
||
iEntry++;
|
||
}
|
||
}
|
||
if (pane is MasterPane && ((MasterPane)pane).IsUniformLegendEntries)
|
||
break;
|
||
}
|
||
|
||
// Draw a border around the legend if required
|
||
if (iEntry > 0)
|
||
this.Border.Draw(g, pane, scaleFactor, _rect);
|
||
}
|
||
}
|
||
|
||
private float GetMaxHeight(PaneList paneList, Graphics g, float scaleFactor)
|
||
{
|
||
// Set up some scaled dimensions for calculating sizes and locations
|
||
float defaultCharHeight = this.FontSpec.GetHeight(scaleFactor);
|
||
float maxCharHeight = defaultCharHeight;
|
||
|
||
// Find the largest charHeight, just in case the curves have individual fonts defined
|
||
foreach (GraphPane tmpPane in paneList)
|
||
{
|
||
foreach (CurveItem curve in tmpPane.CurveList)
|
||
{
|
||
if (curve._label._text != string.Empty && curve._label._isVisible)
|
||
{
|
||
float tmpHeight = defaultCharHeight;
|
||
if (curve._label._fontSpec != null)
|
||
tmpHeight = curve._label._fontSpec.GetHeight(scaleFactor);
|
||
|
||
// Account for multiline legend entries
|
||
tmpHeight *= curve._label._text.Split('\n').Length;
|
||
|
||
if (tmpHeight > maxCharHeight)
|
||
maxCharHeight = tmpHeight;
|
||
}
|
||
}
|
||
}
|
||
|
||
return maxCharHeight;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Determine if a mouse point is within the legend, and if so, which legend
|
||
/// entry (<see cref="CurveItem"/>) is nearest.
|
||
/// </summary>
|
||
/// <param name="mousePt">The screen point, in pixel coordinates.</param>
|
||
/// <param name="pane">
|
||
/// A reference to the <see cref="PaneBase"/> object that is the parent or
|
||
/// owner of this object.
|
||
/// </param>
|
||
/// <param name="scaleFactor">
|
||
/// The scaling factor to be used for rendering objects. This is calculated and
|
||
/// passed down by the parent <see cref="GraphPane"/> object using the
|
||
/// <see cref="PaneBase.CalcScaleFactor"/> method, and is used to proportionally adjust
|
||
/// font sizes, etc. according to the actual size of the graph.
|
||
/// </param>
|
||
/// <param name="index">The index number of the <see cref="CurveItem"/> legend
|
||
/// entry that is under the mouse point. The <see cref="CurveItem"/> object is
|
||
/// accessible via <see cref="GraphPane.CurveList">CurveList[index]</see>.
|
||
/// </param>
|
||
/// <returns>true if the mouse point is within the <see cref="Legend"/> bounding
|
||
/// box, false otherwise.</returns>
|
||
/// <seealso cref="GraphPane.FindNearestObject"/>
|
||
public bool FindPoint(PointF mousePt, PaneBase pane, float scaleFactor, out int index)
|
||
{
|
||
index = -1;
|
||
|
||
if (_rect.Contains(mousePt))
|
||
{
|
||
int j = (int)((mousePt.Y - _rect.Top) / _legendItemHeight);
|
||
int i = (int)((mousePt.X - _rect.Left - _tmpSize / 2.0f) / _legendItemWidth);
|
||
if (i < 0)
|
||
i = 0;
|
||
if (i >= _hStack)
|
||
i = _hStack - 1;
|
||
|
||
int pos = i + j * _hStack;
|
||
index = 0;
|
||
|
||
PaneList paneList = GetPaneList(pane);
|
||
|
||
foreach (GraphPane tmpPane in paneList)
|
||
{
|
||
foreach (CurveItem curve in tmpPane.CurveList)
|
||
{
|
||
if (curve._label._isVisible && curve._label._text != string.Empty)
|
||
{
|
||
if (pos == 0)
|
||
return true;
|
||
pos--;
|
||
}
|
||
index++;
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
else
|
||
return false;
|
||
}
|
||
|
||
private PaneList GetPaneList(PaneBase pane)
|
||
{
|
||
// For a single GraphPane, create a PaneList to contain it
|
||
// Otherwise, just use the paneList from the MasterPane
|
||
PaneList paneList;
|
||
|
||
if (pane is GraphPane)
|
||
{
|
||
paneList = new PaneList();
|
||
paneList.Add((GraphPane)pane);
|
||
}
|
||
else
|
||
paneList = ((MasterPane)pane).PaneList;
|
||
|
||
return paneList;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Calculate the <see cref="Legend"/> rectangle (<see cref="Rect"/>),
|
||
/// taking into account the number of required legend
|
||
/// entries, and the legend drawing preferences.
|
||
/// </summary>
|
||
/// <remarks>Adjust the size of the
|
||
/// <see cref="Chart.Rect"/> for the parent <see cref="GraphPane"/> to accomodate the
|
||
/// space required by the legend.
|
||
/// </remarks>
|
||
/// <param name="g">
|
||
/// A graphic device object to be drawn into. This is normally e.Graphics from the
|
||
/// PaintEventArgs argument to the Paint() method.
|
||
/// </param>
|
||
/// <param name="pane">
|
||
/// A reference to the <see cref="PaneBase"/> object that is the parent or
|
||
/// owner of this object.
|
||
/// </param>
|
||
/// <param name="scaleFactor">
|
||
/// The scaling factor to be used for rendering objects. This is calculated and
|
||
/// passed down by the parent <see cref="GraphPane"/> object using the
|
||
/// <see cref="PaneBase.CalcScaleFactor"/> method, and is used to proportionally adjust
|
||
/// font sizes, etc. according to the actual size of the graph.
|
||
/// </param>
|
||
/// <param name="tChartRect">
|
||
/// The rectangle that contains the area bounded by the axes, in pixel units.
|
||
/// <seealso cref="Chart.Rect" />
|
||
/// </param>
|
||
public void CalcRect(Graphics g, PaneBase pane, float scaleFactor,
|
||
ref RectangleF tChartRect)
|
||
{
|
||
// Start with an empty rectangle
|
||
_rect = Rectangle.Empty;
|
||
_hStack = 1;
|
||
_legendItemWidth = 1;
|
||
_legendItemHeight = 0;
|
||
|
||
RectangleF clientRect = pane.CalcClientRect(g, scaleFactor);
|
||
|
||
// If the legend is invisible, don't do anything
|
||
if (!_isVisible)
|
||
return;
|
||
|
||
int nCurve = 0;
|
||
|
||
PaneList paneList = GetPaneList(pane);
|
||
_tmpSize = GetMaxHeight(paneList, g, scaleFactor);
|
||
|
||
float halfGap = _tmpSize / 2.0F,
|
||
maxWidth = 0,
|
||
tmpWidth,
|
||
gapPix = _gap * _tmpSize;
|
||
|
||
foreach (GraphPane tmpPane in paneList)
|
||
{
|
||
// Loop through each curve in the curve list
|
||
// Find the maximum width of the legend labels
|
||
//foreach ( CurveItem curve in tmpPane.CurveList )
|
||
//foreach ( CurveItem curve in GetIterator( tmpPane.CurveList, _isReverse ) )
|
||
int count = tmpPane.CurveList.Count;
|
||
for (int i = 0; i < count; i++)
|
||
{
|
||
CurveItem curve = tmpPane.CurveList[_isReverse ? count - i - 1 : i];
|
||
if (curve._label._text != string.Empty && curve._label._isVisible)
|
||
{
|
||
// Calculate the width of the label save the max width
|
||
FontSpec tmpFont = (curve._label._fontSpec != null) ?
|
||
curve._label._fontSpec : this.FontSpec;
|
||
|
||
tmpWidth = tmpFont.GetWidth(g, curve._label._text, scaleFactor);
|
||
|
||
if (tmpWidth > maxWidth)
|
||
maxWidth = tmpWidth;
|
||
|
||
// Save the maximum symbol height for line-type curves
|
||
if (curve is LineItem && ((LineItem)curve).Symbol.Size > _legendItemHeight)
|
||
_legendItemHeight = ((LineItem)curve).Symbol.Size;
|
||
|
||
if (curve.Label.Text.Contains("<22><><EFBFBD><EFBFBD>") || curve.Label.Text.Contains("<22><><EFBFBD><EFBFBD>") || curve.Label.Text.Contains("<22><>") || curve.Label.Text.Contains("<22><>"))
|
||
curve.Color = Color.White;
|
||
nCurve++;
|
||
}
|
||
}
|
||
|
||
if (pane is MasterPane && ((MasterPane)pane).IsUniformLegendEntries)
|
||
break;
|
||
}
|
||
|
||
float widthAvail;
|
||
|
||
// Is this legend horizontally stacked?
|
||
|
||
if (_isHStack)
|
||
{
|
||
// Determine the available space for horizontal stacking
|
||
switch (_position)
|
||
{
|
||
// Never stack if the legend is to the right or left
|
||
case LegendPos.Right:
|
||
case LegendPos.Left:
|
||
widthAvail = 0;
|
||
break;
|
||
|
||
// for the top & bottom, the axis border width is available
|
||
case LegendPos.Top:
|
||
case LegendPos.TopCenter:
|
||
case LegendPos.Bottom:
|
||
case LegendPos.BottomCenter:
|
||
widthAvail = tChartRect.Width;
|
||
break;
|
||
|
||
// for the top & bottom flush left, the panerect less margins is available
|
||
case LegendPos.TopFlushLeft:
|
||
case LegendPos.BottomFlushLeft:
|
||
widthAvail = clientRect.Width;
|
||
break;
|
||
|
||
// for inside the axis area or Float, use 1/2 of the axis border width
|
||
case LegendPos.InsideTopRight:
|
||
case LegendPos.InsideTopLeft:
|
||
case LegendPos.InsideBotRight:
|
||
case LegendPos.InsideBotLeft:
|
||
case LegendPos.Float:
|
||
widthAvail = tChartRect.Width / 2;
|
||
break;
|
||
|
||
// shouldn't ever happen
|
||
default:
|
||
widthAvail = 0;
|
||
break;
|
||
}
|
||
|
||
// width of one legend entry
|
||
_legendItemWidth = 3 * _tmpSize + maxWidth;
|
||
|
||
// Calculate the number of columns in the legend
|
||
// Normally, the legend is:
|
||
// available width / ( max width of any entry + space for line&symbol )
|
||
if (maxWidth > 0)
|
||
_hStack = (int)((widthAvail - halfGap) / _legendItemWidth);
|
||
|
||
// You can never have more columns than legend entries
|
||
if (_hStack > nCurve)
|
||
_hStack = nCurve;
|
||
|
||
// a saftey check
|
||
if (_hStack == 0)
|
||
_hStack = 1;
|
||
}
|
||
else
|
||
_legendItemWidth = 3.5F * _tmpSize + maxWidth;
|
||
|
||
// legend is:
|
||
// item: space line space text space
|
||
// width: wid 4*wid wid maxWid wid
|
||
// The symbol is centered on the line
|
||
//
|
||
// legend begins 3 * wid to the right of the plot rect
|
||
//
|
||
// The height of the legend is the actual height of the lines of text
|
||
// (nCurve * hite) plus wid on top and wid on the bottom
|
||
|
||
// total legend width
|
||
float totLegWidth = _hStack * _legendItemWidth;
|
||
|
||
// The total legend height
|
||
_legendItemHeight = _legendItemHeight * (float)scaleFactor + halfGap;
|
||
if (_tmpSize > _legendItemHeight)
|
||
_legendItemHeight = _tmpSize;
|
||
float totLegHeight = (float)Math.Ceiling((double)nCurve / (double)_hStack)
|
||
* _legendItemHeight;
|
||
|
||
RectangleF newRect = new RectangleF();
|
||
|
||
// Now calculate the legend rect based on the above determined parameters
|
||
// Also, adjust the ChartRect to reflect the space for the legend
|
||
if (nCurve > 0)
|
||
{
|
||
newRect = new RectangleF(0, 0, totLegWidth, totLegHeight);
|
||
|
||
// The switch statement assigns the left and top edges, and adjusts the ChartRect
|
||
// as required. The right and bottom edges are calculated at the bottom of the switch.
|
||
switch (_position)
|
||
{
|
||
case LegendPos.Right:
|
||
newRect.X = clientRect.Right - totLegWidth;
|
||
newRect.Y = tChartRect.Top;
|
||
|
||
tChartRect.Width -= totLegWidth + gapPix;
|
||
break;
|
||
case LegendPos.Top:
|
||
newRect.X = tChartRect.Left;
|
||
newRect.Y = clientRect.Top;
|
||
|
||
tChartRect.Y += totLegHeight + gapPix;
|
||
tChartRect.Height -= totLegHeight + gapPix;
|
||
break;
|
||
case LegendPos.TopFlushLeft:
|
||
newRect.X = clientRect.Left;
|
||
newRect.Y = clientRect.Top;
|
||
|
||
tChartRect.Y += totLegHeight + gapPix * 1.5f;
|
||
tChartRect.Height -= totLegHeight + gapPix * 1.5f;
|
||
break;
|
||
case LegendPos.TopCenter:
|
||
newRect.X = tChartRect.Left + (tChartRect.Width - totLegWidth) / 2;
|
||
newRect.Y = tChartRect.Top;
|
||
|
||
tChartRect.Y += totLegHeight + gapPix;
|
||
tChartRect.Height -= totLegHeight + gapPix;
|
||
break;
|
||
case LegendPos.Bottom:
|
||
newRect.X = tChartRect.Left;
|
||
newRect.Y = clientRect.Bottom - totLegHeight;
|
||
|
||
tChartRect.Height -= totLegHeight + gapPix;
|
||
break;
|
||
case LegendPos.BottomFlushLeft:
|
||
newRect.X = clientRect.Left;
|
||
newRect.Y = clientRect.Bottom - totLegHeight;
|
||
|
||
tChartRect.Height -= totLegHeight + gapPix;
|
||
break;
|
||
case LegendPos.BottomCenter:
|
||
newRect.X = tChartRect.Left + (tChartRect.Width - totLegWidth) / 2;
|
||
newRect.Y = clientRect.Bottom - totLegHeight;
|
||
|
||
tChartRect.Height -= totLegHeight + gapPix;
|
||
break;
|
||
case LegendPos.Left:
|
||
newRect.X = clientRect.Left;
|
||
newRect.Y = tChartRect.Top;
|
||
|
||
tChartRect.X += totLegWidth + halfGap;
|
||
tChartRect.Width -= totLegWidth + gapPix;
|
||
break;
|
||
case LegendPos.InsideTopRight:
|
||
newRect.X = tChartRect.Right - totLegWidth;
|
||
newRect.Y = tChartRect.Top;
|
||
break;
|
||
case LegendPos.InsideTopLeft:
|
||
newRect.X = tChartRect.Left;
|
||
newRect.Y = tChartRect.Top;
|
||
break;
|
||
case LegendPos.InsideBotRight:
|
||
newRect.X = tChartRect.Right - totLegWidth;
|
||
newRect.Y = tChartRect.Bottom - totLegHeight;
|
||
break;
|
||
case LegendPos.InsideBotLeft:
|
||
newRect.X = tChartRect.Left;
|
||
newRect.Y = tChartRect.Bottom - totLegHeight;
|
||
break;
|
||
case LegendPos.Float:
|
||
newRect.Location = this.Location.TransformTopLeft(pane, totLegWidth, totLegHeight);
|
||
break;
|
||
}
|
||
}
|
||
|
||
_rect = newRect;
|
||
}
|
||
|
||
// /// <summary>
|
||
// /// Private method to the render region that gives the iterator depending on the attribute
|
||
// /// </summary>
|
||
// /// <param name="c"></param>
|
||
// /// <param name="forward"></param>
|
||
// /// <returns></returns>
|
||
// private IEnumerable<CurveItem> GetIterator(CurveList c, bool forward)
|
||
// {
|
||
// return forward ? c.Forward : c.Backward;
|
||
// }
|
||
|
||
#endregion
|
||
}
|
||
}
|
||
|