923 lines
No EOL
30 KiB
C#
923 lines
No EOL
30 KiB
C#
using System;
|
|
using System.Drawing;
|
|
using System.Collections;
|
|
using System.Drawing.Imaging;
|
|
using System.Drawing.Drawing2D;
|
|
|
|
namespace Server.Engines.Reports
|
|
{
|
|
// Modified from MS sample
|
|
|
|
//*********************************************************************
|
|
//
|
|
// BarGraph Class
|
|
//
|
|
// This class uses GDI+ to render Bar Chart.
|
|
//
|
|
//*********************************************************************
|
|
|
|
public class BarRegion
|
|
{
|
|
public int m_RangeFrom, m_RangeTo;
|
|
public string m_Name;
|
|
|
|
public BarRegion( int rangeFrom, int rangeTo, string name )
|
|
{
|
|
m_RangeFrom = rangeFrom;
|
|
m_RangeTo = rangeTo;
|
|
m_Name = name;
|
|
}
|
|
}
|
|
|
|
public class BarGraphRenderer : ChartRenderer
|
|
{
|
|
private const float _graphLegendSpacer = 15F;
|
|
private const float _labelFontSize = 7f;
|
|
private const int _legendFontSize = 9;
|
|
private const float _legendRectangleSize = 10F;
|
|
private const float _spacer = 5F;
|
|
|
|
public BarRegion[] _regions;
|
|
|
|
private BarGraphRenderMode _renderMode;
|
|
|
|
// Overall related members
|
|
private Color _backColor;
|
|
private string _fontFamily;
|
|
private string _longestTickValue = string.Empty; // Used to calculate max value width
|
|
private float _maxTickValueWidth; // Used to calculate left offset of bar graph
|
|
private float _totalHeight;
|
|
private float _totalWidth;
|
|
|
|
// Graph related members
|
|
private float _barWidth;
|
|
private float _bottomBuffer; // Space from bottom to x axis
|
|
private bool _displayBarData;
|
|
private Color _fontColor;
|
|
private float _graphHeight;
|
|
private float _graphWidth;
|
|
private float _maxValue = 0.0f; // = final tick value * tick count
|
|
private float _scaleFactor; // = _maxValue / _graphHeight
|
|
private float _spaceBtwBars; // For now same as _barWidth
|
|
private float _topBuffer; // Space from top to the top of y axis
|
|
private float _xOrigin; // x position where graph starts drawing
|
|
private float _yOrigin; // y position where graph starts drawing
|
|
private string _yLabel;
|
|
private int _yTickCount;
|
|
private float _yTickValue; // Value for each tick = _maxValue/_yTickCount
|
|
|
|
// Legend related members
|
|
private bool _displayLegend;
|
|
private float _legendWidth;
|
|
private string _longestLabel = string.Empty; // Used to calculate legend width
|
|
private float _maxLabelWidth = 0.0f;
|
|
|
|
public string FontFamily
|
|
{
|
|
get{ return _fontFamily; }
|
|
set{ _fontFamily = value; }
|
|
}
|
|
|
|
public BarGraphRenderMode RenderMode
|
|
{
|
|
get{ return _renderMode; }
|
|
set{ _renderMode = value; }
|
|
}
|
|
|
|
public Color BackgroundColor
|
|
{
|
|
set{ _backColor = value; }
|
|
}
|
|
|
|
public int BottomBuffer
|
|
{
|
|
set { _bottomBuffer = Convert.ToSingle(value); }
|
|
}
|
|
|
|
public Color FontColor
|
|
{
|
|
set{ _fontColor = value; }
|
|
}
|
|
|
|
public int Height
|
|
{
|
|
get{ return Convert.ToInt32(_totalHeight); }
|
|
set{ _totalHeight = Convert.ToSingle(value); }
|
|
}
|
|
|
|
public int Width
|
|
{
|
|
get{ return Convert.ToInt32(_totalWidth); }
|
|
set{ _totalWidth = Convert.ToSingle(value); }
|
|
}
|
|
|
|
public bool ShowLegend
|
|
{
|
|
get{ return _displayLegend; }
|
|
set{ _displayLegend = value; }
|
|
}
|
|
|
|
public bool ShowData
|
|
{
|
|
get{ return _displayBarData; }
|
|
set{ _displayBarData = value; }
|
|
}
|
|
public int TopBuffer
|
|
{
|
|
set { _topBuffer = Convert.ToSingle(value); }
|
|
}
|
|
|
|
public string VerticalLabel
|
|
{
|
|
get{ return _yLabel; }
|
|
set{ _yLabel = value; }
|
|
}
|
|
|
|
public int VerticalTickCount
|
|
{
|
|
get{ return _yTickCount; }
|
|
set{ _yTickCount = value; }
|
|
}
|
|
|
|
private string _xTitle, _yTitle;
|
|
|
|
public void SetTitles( string xTitle, string yTitle )
|
|
{
|
|
_xTitle = xTitle;
|
|
_yTitle = yTitle;
|
|
}
|
|
|
|
public BarGraphRenderer()
|
|
{
|
|
AssignDefaultSettings();
|
|
}
|
|
|
|
public BarGraphRenderer(Color bgColor)
|
|
{
|
|
AssignDefaultSettings();
|
|
BackgroundColor = bgColor;
|
|
}
|
|
|
|
//*********************************************************************
|
|
//
|
|
// This method collects all data points and calculate all the necessary dimensions
|
|
// to draw the bar graph. It is the method called before invoking the Draw() method.
|
|
// labels is the x values.
|
|
// values is the y values.
|
|
//
|
|
//*********************************************************************
|
|
|
|
public void CollectDataPoints(string[] labels, string[] values)
|
|
{
|
|
if (labels.Length == values.Length)
|
|
{
|
|
for(int i=0; i<labels.Length; i++)
|
|
{
|
|
float temp = Convert.ToSingle(values[i]);
|
|
string shortLbl = MakeShortLabel(labels[i]);
|
|
|
|
// For now put 0.0 for start position and sweep size
|
|
DataPoints.Add(new DataItem(shortLbl, labels[i], temp, 0.0f, 0.0f, GetColor(i)));
|
|
|
|
// Find max value from data; this is only temporary _maxValue
|
|
if (_maxValue < temp) _maxValue = temp;
|
|
|
|
// Find the longest description
|
|
if (_displayLegend)
|
|
{
|
|
string currentLbl = labels[i] + " (" + shortLbl + ")";
|
|
float currentWidth = CalculateImgFontWidth(currentLbl, _legendFontSize, FontFamily);
|
|
if(_maxLabelWidth < currentWidth)
|
|
{
|
|
_longestLabel = currentLbl;
|
|
_maxLabelWidth = currentWidth;
|
|
}
|
|
}
|
|
}
|
|
|
|
CalculateTickAndMax();
|
|
CalculateGraphDimension();
|
|
CalculateBarWidth(DataPoints.Count, _graphWidth);
|
|
CalculateSweepValues();
|
|
}
|
|
else
|
|
throw new Exception("X data count is different from Y data count");
|
|
}
|
|
|
|
//*********************************************************************
|
|
//
|
|
// Same as above; called when user doesn't care about the x values
|
|
//
|
|
//*********************************************************************
|
|
|
|
public void CollectDataPoints(string[] values)
|
|
{
|
|
string[] labels = values;
|
|
CollectDataPoints(labels, values);
|
|
}
|
|
|
|
public void DrawRegions( Graphics gfx )
|
|
{
|
|
if ( _regions == null )
|
|
return;
|
|
|
|
using ( StringFormat textFormat = new StringFormat() )
|
|
{
|
|
textFormat.Alignment = StringAlignment.Center;
|
|
textFormat.LineAlignment = StringAlignment.Center;
|
|
|
|
using ( Font font = new Font( _fontFamily, _labelFontSize ) )
|
|
{
|
|
using ( Brush textBrush = new SolidBrush( _fontColor ) )
|
|
{
|
|
using ( Pen solidPen = new Pen( _fontColor ) )
|
|
{
|
|
using ( Pen lightPen = new Pen( Color.FromArgb( 128, _fontColor ) ) )
|
|
{
|
|
float labelWidth = _barWidth + _spaceBtwBars;
|
|
|
|
for ( int i = 0; i < _regions.Length; ++i )
|
|
{
|
|
BarRegion reg = _regions[i];
|
|
|
|
RectangleF rc = new RectangleF( _xOrigin + (reg.m_RangeFrom * labelWidth), _yOrigin, (reg.m_RangeTo - reg.m_RangeFrom + 1) * labelWidth, _graphHeight );
|
|
|
|
if ( rc.X+rc.Width > _xOrigin+_graphWidth )
|
|
rc.Width = _xOrigin+_graphWidth-rc.X;
|
|
|
|
using ( SolidBrush brsh = new SolidBrush( Color.FromArgb( 48, GetColor( i ) ) ) )
|
|
gfx.FillRectangle( brsh, rc );
|
|
|
|
rc.Offset( (rc.Width - 200.0f) * 0.5f, -16.0f );
|
|
rc.Width = 200.0f;
|
|
rc.Height = 20.0f;
|
|
|
|
gfx.DrawString( reg.m_Name, font, textBrush, rc, textFormat );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//*********************************************************************
|
|
//
|
|
// This method returns a bar graph bitmap to the calling function. It is called after
|
|
// all dimensions and data points are calculated.
|
|
//
|
|
//*********************************************************************
|
|
|
|
public override Bitmap Draw()
|
|
{
|
|
int height = Convert.ToInt32(_totalHeight);
|
|
int width = Convert.ToInt32(_totalWidth);
|
|
|
|
Bitmap bmp = new Bitmap(width, height);
|
|
|
|
using(Graphics graph = Graphics.FromImage(bmp))
|
|
{
|
|
graph.CompositingQuality = CompositingQuality.HighQuality;
|
|
graph.SmoothingMode = SmoothingMode.AntiAlias;
|
|
|
|
using ( SolidBrush brsh = new SolidBrush( _backColor ) )
|
|
graph.FillRectangle(brsh, -1, -1, bmp.Width+1, bmp.Height+1);
|
|
|
|
DrawRegions(graph);
|
|
DrawVerticalLabelArea(graph);
|
|
DrawXLabelBack(graph);
|
|
DrawBars(graph);
|
|
DrawXLabelArea(graph);
|
|
|
|
if (_displayLegend)
|
|
DrawLegend(graph);
|
|
}
|
|
|
|
return bmp;
|
|
}
|
|
|
|
//*********************************************************************
|
|
//
|
|
// This method draws all the bars for the graph.
|
|
//
|
|
//*********************************************************************
|
|
|
|
public int _interval;
|
|
|
|
private void DrawBars(Graphics graph)
|
|
{
|
|
SolidBrush brsFont = null;
|
|
Font valFont = null;
|
|
StringFormat sfFormat = null;
|
|
|
|
try
|
|
{
|
|
brsFont = new SolidBrush(_fontColor);
|
|
valFont = new Font(_fontFamily, _labelFontSize);
|
|
sfFormat = new StringFormat();
|
|
sfFormat.Alignment = StringAlignment.Center;
|
|
int i = 0;
|
|
|
|
PointF[] linePoints = null;
|
|
|
|
if ( _renderMode == BarGraphRenderMode.Lines )
|
|
linePoints = new PointF[DataPoints.Count];
|
|
|
|
int pointIndex = 0;
|
|
|
|
// Draw bars and the value above each bar
|
|
using ( Pen pen = new Pen(_fontColor,0.15f) )
|
|
{
|
|
using ( SolidBrush whiteBrsh = new SolidBrush( Color.FromArgb( 128, Color.White ) ) )
|
|
{
|
|
foreach(DataItem item in DataPoints)
|
|
{
|
|
using(SolidBrush barBrush = new SolidBrush(item.ItemColor))
|
|
{
|
|
float itemY = _yOrigin + _graphHeight - item.SweepSize;
|
|
|
|
if ( _renderMode == BarGraphRenderMode.Lines )
|
|
{
|
|
linePoints[pointIndex++] = new PointF( _xOrigin + item.StartPos + (_barWidth / 2), itemY );
|
|
}
|
|
else if ( _renderMode == BarGraphRenderMode.Bars )
|
|
{
|
|
float ox = _xOrigin + item.StartPos;
|
|
float oy = itemY;
|
|
float ow = _barWidth;
|
|
float oh = item.SweepSize;
|
|
float of = 9.5f;
|
|
|
|
PointF[] pts = new PointF[]
|
|
{
|
|
new PointF( ox, oy ),
|
|
new PointF( ox + ow, oy ),
|
|
new PointF( ox + of, oy + of ),
|
|
new PointF( ox + of + ow, oy + of ),
|
|
new PointF( ox, oy + oh ),
|
|
new PointF( ox + of, oy + of + oh ),
|
|
new PointF( ox + of + ow, oy + of + oh )
|
|
};
|
|
|
|
graph.FillPolygon( barBrush, new PointF[]{ pts[2], pts[3], pts[6], pts[5] } );
|
|
|
|
using ( SolidBrush ltBrsh = new SolidBrush( System.Windows.Forms.ControlPaint.Light(item.ItemColor,0.1f) ) )
|
|
graph.FillPolygon( ltBrsh, new PointF[]{ pts[0], pts[2], pts[5], pts[4] } );
|
|
|
|
using ( SolidBrush drkBrush = new SolidBrush( System.Windows.Forms.ControlPaint.Dark(item.ItemColor,0.05f) ) )
|
|
graph.FillPolygon( drkBrush, new PointF[]{ pts[0], pts[1], pts[3], pts[2] } );
|
|
|
|
graph.DrawLine( pen, pts[0], pts[1] );
|
|
graph.DrawLine( pen, pts[0], pts[2] );
|
|
graph.DrawLine( pen, pts[1], pts[3] );
|
|
graph.DrawLine( pen, pts[2], pts[3] );
|
|
graph.DrawLine( pen, pts[2], pts[5] );
|
|
graph.DrawLine( pen, pts[0], pts[4] );
|
|
graph.DrawLine( pen, pts[4], pts[5] );
|
|
graph.DrawLine( pen, pts[5], pts[6] );
|
|
graph.DrawLine( pen, pts[3], pts[6] );
|
|
|
|
// Draw data value
|
|
if (_displayBarData&&(i%_interval)==0)
|
|
{
|
|
float sectionWidth = ( _barWidth + _spaceBtwBars );
|
|
float startX = _xOrigin + (i * sectionWidth) + (sectionWidth / 2); // This draws the value on center of the bar
|
|
float startY = itemY - 2f - valFont.Height; // Positioned on top of each bar by 2 pixels
|
|
RectangleF recVal = new RectangleF(startX-((sectionWidth * _interval) / 2), startY, sectionWidth * _interval, valFont.Height);
|
|
SizeF sz = graph.MeasureString(item.Value.ToString("#,###.##"),valFont,recVal.Size,sfFormat);
|
|
//using ( SolidBrush brsh = new SolidBrush( Color.FromArgb( 180, 255, 255, 255 ) ) )
|
|
// graph.FillRectangle( brsh, new RectangleF(recVal.X+((recVal.Width-sz.Width)/2),recVal.Y+((recVal.Height-sz.Height)/2),sz.Width+4,sz.Height) );
|
|
|
|
//graph.DrawString(item.Value.ToString("#,###.##"), valFont, brsFont, recVal, sfFormat);
|
|
|
|
for ( int box = -1; box <= 1; ++box )
|
|
{
|
|
for ( int boy = -1; boy <= 1; ++boy )
|
|
{
|
|
if ( box == 0 && boy == 0 )
|
|
continue;
|
|
|
|
RectangleF rco = new RectangleF( recVal.X+box, recVal.Y+boy, recVal.Width, recVal.Height );
|
|
graph.DrawString(item.Value.ToString("#,###.##"), valFont, whiteBrsh, rco, sfFormat);
|
|
}
|
|
}
|
|
|
|
graph.DrawString(item.Value.ToString("#,###.##"), valFont, brsFont, recVal, sfFormat);
|
|
}
|
|
}
|
|
|
|
i++;
|
|
}
|
|
}
|
|
|
|
if ( _renderMode == BarGraphRenderMode.Lines )
|
|
{
|
|
if ( linePoints.Length >= 2 )
|
|
{
|
|
using ( Pen linePen = new Pen( Color.FromArgb(220,Color.Red), 2.5f ) )
|
|
graph.DrawCurve( linePen, linePoints, 0.5f );
|
|
}
|
|
|
|
using ( Pen linePen = new Pen( Color.FromArgb(40,_fontColor), 0.8f ) )
|
|
{
|
|
for ( int j = 0; j < linePoints.Length; ++j )
|
|
{
|
|
graph.DrawLine( linePen, linePoints[j], new PointF( linePoints[j].X, _yOrigin + _graphHeight ) );
|
|
|
|
DataItem item = DataPoints[j];
|
|
float itemY = _yOrigin + _graphHeight - item.SweepSize;
|
|
|
|
// Draw data value
|
|
if (_displayBarData&&(j%_interval)==0)
|
|
{
|
|
graph.FillEllipse( brsFont, new RectangleF( linePoints[j].X-2.0f, linePoints[j].Y-2.0f, 4.0f, 4.0f ) );
|
|
|
|
float sectionWidth = ( _barWidth + _spaceBtwBars );
|
|
float startX = _xOrigin + (j * sectionWidth) + (sectionWidth/2); // This draws the value on center of the bar
|
|
float startY = itemY - 2f - valFont.Height; // Positioned on top of each bar by 2 pixels
|
|
RectangleF recVal = new RectangleF(startX-((sectionWidth * _interval) / 2), startY, sectionWidth * _interval, valFont.Height);
|
|
SizeF sz = graph.MeasureString(item.Value.ToString("#,###.##"),valFont,recVal.Size,sfFormat);
|
|
//using ( SolidBrush brsh = new SolidBrush( Color.FromArgb( 48, 255, 255, 255 ) ) )
|
|
// graph.FillRectangle( brsh, new RectangleF(recVal.X+((recVal.Width-sz.Width)/2),recVal.Y+((recVal.Height-sz.Height)/2),sz.Width+4,sz.Height) );
|
|
|
|
for ( int box = -1; box <= 1; ++box )
|
|
{
|
|
for ( int boy = -1; boy <= 1; ++boy )
|
|
{
|
|
if ( box == 0 && boy == 0 )
|
|
continue;
|
|
|
|
RectangleF rco = new RectangleF( recVal.X+box, recVal.Y+boy, recVal.Width, recVal.Height );
|
|
graph.DrawString(item.Value.ToString("#,###.##"), valFont, whiteBrsh, rco, sfFormat);
|
|
}
|
|
}
|
|
|
|
graph.DrawString(item.Value.ToString("#,###.##"), valFont, brsFont, recVal, sfFormat);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
if (brsFont != null) brsFont.Dispose();
|
|
if (valFont != null) valFont.Dispose();
|
|
if (sfFormat != null) sfFormat.Dispose();
|
|
}
|
|
}
|
|
|
|
//*********************************************************************
|
|
//
|
|
// This method draws the y label, tick marks, tick values, and the y axis.
|
|
//
|
|
//*********************************************************************
|
|
|
|
private void DrawVerticalLabelArea(Graphics graph)
|
|
{
|
|
Font lblFont = null;
|
|
SolidBrush brs = null;
|
|
StringFormat lblFormat = null;
|
|
Pen pen = null;
|
|
StringFormat sfVLabel = null;
|
|
|
|
float fo = (_yTitle==null?0.0f:20.0f);
|
|
|
|
try
|
|
{
|
|
brs = new SolidBrush(_fontColor);
|
|
lblFormat = new StringFormat();
|
|
pen = new Pen(_fontColor);
|
|
|
|
if ( _yTitle != null )
|
|
{
|
|
sfVLabel = new StringFormat();
|
|
sfVLabel.Alignment = StringAlignment.Center;
|
|
sfVLabel.LineAlignment = StringAlignment.Center;
|
|
sfVLabel.FormatFlags=StringFormatFlags.DirectionVertical;
|
|
|
|
lblFont = new Font(_fontFamily, _labelFontSize+4.0f);
|
|
graph.DrawString(_yTitle,lblFont,brs,new RectangleF( 0.0f, _yOrigin, 20.0f, _graphHeight ), sfVLabel );
|
|
lblFont.Dispose();
|
|
}
|
|
|
|
sfVLabel = new StringFormat();
|
|
lblFormat.Alignment = StringAlignment.Far;
|
|
lblFormat.FormatFlags|=StringFormatFlags.NoClip;
|
|
|
|
// Draw vertical label at the top of y-axis and place it in the middle top of y-axis
|
|
lblFont = new Font(_fontFamily, _labelFontSize+2.0f,FontStyle.Bold);
|
|
RectangleF recVLabel = new RectangleF(0, _yOrigin-2*_spacer-lblFont.Height, _xOrigin*2, lblFont.Height);
|
|
sfVLabel.Alignment = StringAlignment.Center;
|
|
sfVLabel.FormatFlags|=StringFormatFlags.NoClip;
|
|
//graph.DrawRectangle(Pens.Black,Rectangle.Truncate(recVLabel));
|
|
graph.DrawString(_yLabel, lblFont, brs, recVLabel, sfVLabel);
|
|
lblFont.Dispose();
|
|
|
|
lblFont = new Font(_fontFamily, _labelFontSize);
|
|
// Draw all tick values and tick marks
|
|
using ( Pen smallPen = new Pen(Color.FromArgb(96,_fontColor),0.8f) )
|
|
{
|
|
for (int i=0; i<_yTickCount; i++)
|
|
{
|
|
float currentY = _topBuffer + (i * _yTickValue/_scaleFactor); // Position for tick mark
|
|
float labelY = currentY-lblFont.Height/2; // Place label in the middle of tick
|
|
RectangleF lblRec = new RectangleF(_spacer+fo-6, labelY, _maxTickValueWidth, lblFont.Height);
|
|
|
|
float currentTick = _maxValue - i*_yTickValue; // Calculate tick value from top to bottom
|
|
graph.DrawString(currentTick.ToString("#,###.##"), lblFont, brs, lblRec, lblFormat); // Draw tick value
|
|
graph.DrawLine(pen, _xOrigin, currentY, _xOrigin - 4.0f, currentY); // Draw tick mark
|
|
|
|
graph.DrawLine(smallPen, _xOrigin, currentY, _xOrigin + _graphWidth, currentY );
|
|
}
|
|
}
|
|
|
|
// Draw y axis
|
|
graph.DrawLine(pen, _xOrigin, _yOrigin, _xOrigin, _yOrigin + _graphHeight);
|
|
}
|
|
finally
|
|
{
|
|
if (lblFont != null) lblFont.Dispose();
|
|
if (brs != null) brs.Dispose();
|
|
if (lblFormat != null) lblFormat.Dispose();
|
|
if (pen != null) pen.Dispose();
|
|
if (sfVLabel != null) sfVLabel.Dispose();
|
|
}
|
|
}
|
|
|
|
//*********************************************************************
|
|
//
|
|
// This method draws x axis and all x labels
|
|
//
|
|
//*********************************************************************
|
|
|
|
private void DrawXLabelBack(Graphics graph)
|
|
{
|
|
Font lblFont = null;
|
|
SolidBrush brs = null;
|
|
StringFormat lblFormat = null;
|
|
Pen pen = null;
|
|
|
|
try
|
|
{
|
|
lblFont = new Font(_fontFamily, _labelFontSize);
|
|
brs = new SolidBrush(_fontColor);
|
|
lblFormat = new StringFormat();
|
|
pen = new Pen(_fontColor);
|
|
|
|
lblFormat.Alignment = StringAlignment.Center;
|
|
|
|
// Draw x axis
|
|
graph.DrawLine(pen, _xOrigin, _yOrigin + _graphHeight, _xOrigin + _graphWidth, _yOrigin + _graphHeight );
|
|
}
|
|
finally
|
|
{
|
|
if (lblFont != null) lblFont.Dispose();
|
|
if (brs != null) brs.Dispose();
|
|
if (lblFormat != null) lblFormat.Dispose();
|
|
if (pen != null) pen.Dispose();
|
|
}
|
|
}
|
|
|
|
private void DrawXLabelArea(Graphics graph)
|
|
{
|
|
Font lblFont = null;
|
|
SolidBrush brs = null;
|
|
StringFormat lblFormat = null;
|
|
Pen pen = null;
|
|
|
|
try
|
|
{
|
|
brs = new SolidBrush(_fontColor);
|
|
pen = new Pen(_fontColor);
|
|
|
|
if ( _xTitle != null )
|
|
{
|
|
lblFormat = new StringFormat();
|
|
lblFormat.Alignment = StringAlignment.Center;
|
|
lblFormat.LineAlignment = StringAlignment.Center;
|
|
// sfVLabel.FormatFlags=StringFormatFlags.DirectionVertical;
|
|
|
|
lblFont = new Font(_fontFamily, _labelFontSize+2.0f, FontStyle.Bold);
|
|
graph.DrawString(_xTitle,lblFont,brs,new RectangleF( _xOrigin, _yOrigin+_graphHeight+14.0f+(_renderMode==BarGraphRenderMode.Bars?10.0f:0.0f)+((DataPoints.Count/_interval)>24?16.0f:0.0f), _graphWidth, 20.0f ), lblFormat );
|
|
}
|
|
|
|
lblFont = new Font(_fontFamily, _labelFontSize);
|
|
lblFormat = new StringFormat();
|
|
lblFormat.Alignment = StringAlignment.Center;
|
|
lblFormat.FormatFlags |= StringFormatFlags.NoClip;
|
|
lblFormat.Trimming = StringTrimming.None;
|
|
//lblFormat.FormatFlags |= StringFormatFlags.NoWrap;
|
|
|
|
float of = 0.0f;
|
|
|
|
if ( _renderMode == BarGraphRenderMode.Bars )
|
|
{
|
|
of = 10.0f;
|
|
|
|
// Draw x axis
|
|
graph.DrawLine( pen, _xOrigin + of, _yOrigin + _graphHeight + of, _xOrigin + _graphWidth + of, _yOrigin + _graphHeight + of );
|
|
|
|
graph.DrawLine( pen, _xOrigin, _yOrigin + _graphHeight, _xOrigin + of, _yOrigin + _graphHeight + of );
|
|
graph.DrawLine( pen, _xOrigin + _graphWidth, _yOrigin + _graphHeight, _xOrigin + of + _graphWidth, _yOrigin + _graphHeight + of );
|
|
}
|
|
|
|
float currentX;
|
|
float currentY = _yOrigin + _graphHeight + 2.0f; // All x labels are drawn 2 pixels below x-axis
|
|
float labelWidth = _barWidth + _spaceBtwBars; // Fits exactly below the bar
|
|
int i = 0;
|
|
|
|
// Draw x labels
|
|
foreach(DataItem item in DataPoints)
|
|
{
|
|
if((i%_interval)==0)
|
|
{
|
|
currentX = _xOrigin + (i * labelWidth)+of+(labelWidth/2);
|
|
RectangleF recLbl = new RectangleF(currentX-((labelWidth*_interval)/2), currentY+of, labelWidth*_interval, lblFont.Height*2);
|
|
string lblString = _displayLegend ? item.Label : item.Description; // Decide what to show: short or long
|
|
|
|
graph.DrawString(lblString, lblFont, brs, recLbl, lblFormat);
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
if (lblFont != null) lblFont.Dispose();
|
|
if (brs != null) brs.Dispose();
|
|
if (lblFormat != null) lblFormat.Dispose();
|
|
if (pen != null) pen.Dispose();
|
|
}
|
|
}
|
|
|
|
//*********************************************************************
|
|
//
|
|
// This method determines where to place the legend box.
|
|
// It draws the legend border, legend description, and legend color code.
|
|
//
|
|
//*********************************************************************
|
|
|
|
private void DrawLegend(Graphics graph)
|
|
{
|
|
Font lblFont = null;
|
|
SolidBrush brs = null;
|
|
StringFormat lblFormat = null;
|
|
Pen pen = null;
|
|
|
|
try
|
|
{
|
|
lblFont = new Font(_fontFamily, _legendFontSize);
|
|
brs = new SolidBrush(_fontColor);
|
|
lblFormat = new StringFormat();
|
|
pen = new Pen(_fontColor);
|
|
lblFormat.Alignment = StringAlignment.Near;
|
|
|
|
// Calculate Legend drawing start point
|
|
float startX = _xOrigin + _graphWidth + _graphLegendSpacer;
|
|
float startY = _yOrigin;
|
|
|
|
float xColorCode = startX + _spacer;
|
|
float xLegendText = xColorCode + _legendRectangleSize + _spacer;
|
|
float legendHeight = 0.0f;
|
|
for(int i=0; i<DataPoints.Count; i++)
|
|
{
|
|
DataItem point = DataPoints[i];
|
|
string text = point.Description + " (" + point.Label + ")";
|
|
float currentY = startY + _spacer + (i * (lblFont.Height + _spacer));
|
|
legendHeight += lblFont.Height + _spacer;
|
|
|
|
// Draw legend description
|
|
graph.DrawString(text, lblFont, brs, xLegendText, currentY, lblFormat);
|
|
|
|
// Draw color code
|
|
using ( SolidBrush brsh = new SolidBrush(DataPoints[i].ItemColor) )
|
|
graph.FillRectangle(brsh, xColorCode, currentY + 3f, _legendRectangleSize, _legendRectangleSize);
|
|
}
|
|
|
|
// Draw legend border
|
|
graph.DrawRectangle(pen, startX, startY, _legendWidth, legendHeight + _spacer);
|
|
}
|
|
finally
|
|
{
|
|
if (lblFont != null) lblFont.Dispose();
|
|
if (brs != null) brs.Dispose();
|
|
if (lblFormat != null) lblFormat.Dispose();
|
|
if (pen != null) pen.Dispose();
|
|
}
|
|
}
|
|
|
|
//*********************************************************************
|
|
//
|
|
// This method calculates all measurement aspects of the bar graph from the given data points
|
|
//
|
|
//*********************************************************************
|
|
|
|
private void CalculateGraphDimension()
|
|
{
|
|
FindLongestTickValue();
|
|
|
|
// Need to add another character for spacing; this is not used for drawing, just for calculation
|
|
_longestTickValue += "0";
|
|
//_maxTickValueWidth = CalculateImgFontWidth(_longestTickValue, _labelFontSize, FontFamily);
|
|
_maxTickValueWidth = 0.0f;
|
|
|
|
|
|
float currentTick;
|
|
string tickString;
|
|
for (int i=0; i<_yTickCount; i++)
|
|
{
|
|
currentTick = _maxValue - i*_yTickValue;
|
|
tickString = currentTick.ToString("#,###.##");
|
|
|
|
float measured = CalculateImgFontWidth( tickString, _labelFontSize, FontFamily );
|
|
|
|
if ( measured > _maxTickValueWidth )
|
|
_maxTickValueWidth = measured;
|
|
}
|
|
|
|
float leftOffset = _spacer + _maxTickValueWidth + (_yTitle == null ? 0.0f : 20.0f);
|
|
float rtOffset = 0.0f;
|
|
|
|
if (_displayLegend)
|
|
{
|
|
_legendWidth = _spacer + _legendRectangleSize + _spacer + _maxLabelWidth + _spacer;
|
|
rtOffset = _graphLegendSpacer + _legendWidth + _spacer;
|
|
}
|
|
else
|
|
rtOffset = _spacer; // Make graph in the middle
|
|
|
|
if ( _renderMode == BarGraphRenderMode.Bars )
|
|
rtOffset += 10.0f;
|
|
|
|
rtOffset+=10.0f;
|
|
|
|
_graphHeight = _totalHeight - _topBuffer - _bottomBuffer - (_xTitle == null ? 0.0f : 20.0f); // Buffer spaces are used to print labels
|
|
_graphWidth = _totalWidth - leftOffset - rtOffset;
|
|
_xOrigin = leftOffset;
|
|
_yOrigin = _topBuffer;
|
|
|
|
// Once the correct _maxValue is determined, then calculate _scaleFactor
|
|
_scaleFactor = _maxValue / _graphHeight;
|
|
}
|
|
|
|
//*********************************************************************
|
|
//
|
|
// This method determines the longest tick value from the given data points.
|
|
// The result is needed to calculate the correct graph dimension.
|
|
//
|
|
//*********************************************************************
|
|
|
|
private void FindLongestTickValue()
|
|
{
|
|
float currentTick;
|
|
string tickString;
|
|
for (int i=0; i<_yTickCount; i++)
|
|
{
|
|
currentTick = _maxValue - i*_yTickValue;
|
|
tickString = currentTick.ToString("#,###.##");
|
|
if (_longestTickValue.Length < tickString.Length)
|
|
_longestTickValue = tickString;
|
|
}
|
|
}
|
|
|
|
//*********************************************************************
|
|
//
|
|
// This method calculates the image width in pixel for a given text
|
|
//
|
|
//*********************************************************************
|
|
|
|
private float CalculateImgFontWidth(string text, float size, string family)
|
|
{
|
|
Bitmap bmp = null;
|
|
Graphics graph = null;
|
|
Font font = null;
|
|
|
|
try
|
|
{
|
|
font = new Font(family, size);
|
|
|
|
// Calculate the size of the string.
|
|
bmp = new Bitmap(1,1,PixelFormat.Format32bppArgb);
|
|
graph = Graphics.FromImage(bmp);
|
|
SizeF oSize = graph.MeasureString(text, font);
|
|
oSize.Width=4+(float)Math.Ceiling(oSize.Width);
|
|
|
|
return oSize.Width;
|
|
}
|
|
finally
|
|
{
|
|
if (graph != null) graph.Dispose();
|
|
if (bmp != null) bmp.Dispose();
|
|
if (font != null) font.Dispose();
|
|
}
|
|
}
|
|
|
|
//*********************************************************************
|
|
//
|
|
// This method creates abbreviation from long description; used for making legend
|
|
//
|
|
//*********************************************************************
|
|
|
|
private string MakeShortLabel(string text)
|
|
{
|
|
string label = text;
|
|
if (text.Length > 2)
|
|
{
|
|
int midPostition = Convert.ToInt32(Math.Floor(text.Length/2.0));
|
|
label = text.Substring(0,1) + text.Substring(midPostition, 1) + text.Substring(text.Length-1,1);
|
|
}
|
|
return label;
|
|
}
|
|
|
|
//*********************************************************************
|
|
//
|
|
// This method calculates the max value and each tick mark value for the bar graph.
|
|
//
|
|
//*********************************************************************
|
|
|
|
private void CalculateTickAndMax()
|
|
{
|
|
float tempMax = 0.0f;
|
|
|
|
// Give graph some head room first about 10% of current max
|
|
_maxValue *= 1.1f;
|
|
|
|
if (_maxValue != 0.0f)
|
|
{
|
|
// Find a rounded value nearest to the current max value
|
|
// Calculate this max first to give enough space to draw value on each bar
|
|
double exp = Convert.ToDouble(Math.Floor(Math.Log10(_maxValue)));
|
|
tempMax = Convert.ToSingle(Math.Ceiling(_maxValue / Math.Pow(10, exp)) * Math.Pow(10, exp));
|
|
}
|
|
else
|
|
tempMax = 1.0f;
|
|
|
|
// Once max value is calculated, tick value can be determined; tick value should be a whole number
|
|
_yTickValue = tempMax / _yTickCount;
|
|
double expTick = Convert.ToDouble(Math.Floor(Math.Log10(_yTickValue)));
|
|
_yTickValue = Convert.ToSingle(Math.Ceiling(_yTickValue / Math.Pow(10, expTick)) * Math.Pow(10, expTick));
|
|
|
|
// Re-calculate the max value with the new tick value
|
|
_maxValue = _yTickValue * _yTickCount;
|
|
}
|
|
|
|
//*********************************************************************
|
|
//
|
|
// This method calculates the height for each bar in the graph
|
|
//
|
|
//*********************************************************************
|
|
|
|
private void CalculateSweepValues()
|
|
{
|
|
// Called when all values and scale factor are known
|
|
// All values calculated here are relative from (_xOrigin, _yOrigin)
|
|
int i = 0;
|
|
foreach(DataItem item in DataPoints)
|
|
{
|
|
// This implementation does not support negative value
|
|
if (item.Value >= 0) item.SweepSize = item.Value/_scaleFactor;
|
|
|
|
// (_spaceBtwBars/2) makes half white space for the first bar
|
|
item.StartPos = (_spaceBtwBars/2) + i * (_barWidth+_spaceBtwBars);
|
|
i++;
|
|
}
|
|
}
|
|
|
|
//*********************************************************************
|
|
//
|
|
// This method calculates the width for each bar in the graph
|
|
//
|
|
//*********************************************************************
|
|
|
|
private void CalculateBarWidth(int dataCount, float barGraphWidth)
|
|
{
|
|
// White space between each bar is the same as bar width itself
|
|
_barWidth = barGraphWidth / (dataCount * 2); // Each bar has 1 white space
|
|
//_barWidth =/* (float)Math.Floor(*/_barWidth/*)*/;
|
|
_spaceBtwBars = _barWidth;
|
|
}
|
|
|
|
//*********************************************************************
|
|
//
|
|
// This method assigns default value to the bar graph properties and is only
|
|
// called from BarGraph constructors
|
|
//
|
|
//*********************************************************************
|
|
|
|
private void AssignDefaultSettings()
|
|
{
|
|
// default values
|
|
_totalWidth = 680f;
|
|
_totalHeight = 450f;
|
|
_fontFamily = "Verdana";
|
|
_backColor = Color.White;
|
|
_fontColor = Color.Black;
|
|
_topBuffer = 30f;
|
|
_bottomBuffer = 30f;
|
|
_yTickCount = 2;
|
|
_displayLegend = false;
|
|
_displayBarData = false;
|
|
}
|
|
}
|
|
} |