• No results found

This way, you can access this PlotPanel from classes other than Form1

Add four new classes to the project, DataCollection, DataSeries, ChartStyle, and BarStyle. We have used first three classes extensively in the previous chapter. You can copy them from previous projects (such as Example3_7), and modify them to fit the current project. The BarStyle class is new and specific to this project. The following is its code listing:

using System;

using System.Drawing;

using System.Drawing.Drawing2D;

namespace Example4_1 {

public class BarStyle {

private Color fillColor = Color.Black;

private Color borderColor = Color.Black;

private float borderThickness = 1.0f;

private float barWidth = 0.8f;

private DashStyle borderPattern = DashStyle.Solid;

public float BarWidth {

get { return barWidth; } set { barWidth = value; } }

virtual public DashStyle BorderPattern {

get { return borderPattern; } set { borderPattern = value; } }

public float BorderThickness {

get { return borderThickness; } set { borderThickness = value; } }

virtual public Color FillColor {

get { return fillColor; } set { fillColor = value; } }

virtual public Color BorderColor {

get { return borderColor; } set { borderColor = value; } }

} }

This class is very simple. We define several member fields and their corresponding properties that allow you to specify the bar width, fill color, and border style.

You also need to modify the ChartStyle class. First we add a bar chart type field and its property:

private BarTypeEnum barType = BarTypeEnum.Vertical;

public BarTypeEnum BarType {

get { return barType; } set { barType = value; } }

public enum BarTypeEnum {

Vertical = 0, Horizontal = 1, VerticalStack = 2, HorizontalStack = 3, VerticalOverlay = 4, HorizontalOverlay = 5 }

Here, we define the barType as a BarTyleEnum object. From this enumeration, you can

choose different bar chart types, including vertical, horizontal, etc. We also remove the

PlotArea field and its property from the class because we use PlotPanel instead of

PlotArea in this project. We don’t define PlotPanel as a member field, so its property is

defined internally and encapsulated by users. The other fields and properties in this class are the same as before, so we don’t need to list them here. You can look at the complete source code in Example4_1 on your computer.

There are three public methods in this class. The Point2D method, which is used to transform the points from the world coordinates to the device coordinates, is the same as in project

Example1_3,. The PlotPanelStyle method defines the styles for the PlotPanel, including

gridlines and ticks (here, we put the ticks inside the PlotPanel). This method is basically similar to the previous projects in Chapter 3, but a minor modification is necessary to reflect the bar chart’s specifics. The following is the code snippet of the PlotPanelStyle method:

public void PlotPanelStyle(Graphics g) {

Pen aPen = new Pen(ChartBorderColor, 1f);

SolidBrush aBrush = new SolidBrush(ChartBackColor);

SizeF tickFontSize = g.MeasureString("A", TickFont);

// Create vertical gridlines:

float fX, fY, xm, ym;

aPen = new Pen(GridColor, 1f);

aPen.DashStyle = GridPattern;

xm = XLimMin + XTickOffset;

if (BarType == BarTypeEnum.Vertical ||

BarType == BarTypeEnum.VerticalOverlay ||

BarType == BarTypeEnum.VerticalStack) {

xm = XTickOffset + XLimMin + XTick / 2;

}

// Create vertical gridelines:

if (IsYGrid == true) {

for (fX = xm; fX < XLimMax; fX += XTick) {

g.DrawLine(aPen, Point2D(new PointF(fX, YLimMin)), Point2D(new PointF(fX, YLimMax)));

} }

// Create the x-axis tick marks:

for (fX = xm; fX < XLimMax; fX += XTick) {

PointF yAxisPoint = Point2D(new PointF(fX, YLimMin));

g.DrawLine(Pens.Black, yAxisPoint,

new PointF(yAxisPoint.X, yAxisPoint.Y - 8f));

}

// Create horizontal gridlines:

aPen = new Pen(GridColor, 1f);

aPen.DashStyle = GridPattern;

ym = YLimMin + YTickOffset;

if (BarType == BarTypeEnum.Horizontal ||

BarType == BarTypeEnum.HorizontalOverlay ||

BarType == BarTypeEnum.HorizontalStack) {

ym = YTickOffset + YLimMin + YTick / 2;

}

if (IsXGrid == true) {

for (fY = ym; fY < YLimMax; fY += YTick) {

g.DrawLine(aPen, Point2D(new PointF(XLimMin, fY)), Point2D(new PointF(XLimMax, fY)));

} }

// Create the y-axis tick marks:

for (fY = ym; fY < YLimMax; fY += YTick) {

PointF xAxisPoint = Point2D(new PointF(XLimMin, fY));

g.DrawLine(Pens.Black, xAxisPoint,

new PointF(xAxisPoint.X + 5f, xAxisPoint.Y));

}

aPen.Dispose();

aBrush.Dispose();

}

Please note how the method takes care of both vertical and horizontal bar charts. In this method, we create a SolidBrush object to fill the bar charts with different colors. If you like, you can

use other Brush objects, such as TextureBrush, HatchBrush, and

LinearGradientBrush, to fill the bar charts with images, patterns, or gradient colors.

Another public method is SetChartArea. This method is used to specify the ticks, labels, etc.

It also contains two private methods: SetPlotPanel and AddLabels. The SetPlotPanel method is used to set the size and position of the PlotPanel. The AddLabels method creates the title and labels of the axes for the bar chart. The following is the code listing of these classes:

public void SetChartArea(Graphics g) {

// Define PlotPanel:

SetPlotPanel(g);

// Draw chart area:

Pen aPen = new Pen(ChartBorderColor, 1f);

SolidBrush aBrush = new SolidBrush(ChartBackColor);

SizeF tickFontSize = g.MeasureString("A", TickFont);

g.FillRectangle(aBrush, ChartArea);

g.DrawRectangle(aPen, ChartArea);

// Create the x-axis tick labels:

aBrush = new SolidBrush(TickFontColor);

float xm = XLimMin + XTickOffset;

float xticklabel = 0f;

if (BarType == BarTypeEnum.Vertical ||

BarType == BarTypeEnum.VerticalOverlay ||

BarType == BarTypeEnum.VerticalStack) {

xm = XTickOffset + XLimMin + XTick / 2;

xticklabel = XTick / 2;

}

for (float fX = xm; fX <= XLimMax; fX += XTick) {

PointF yAxisPoint = Point2D(new PointF(fX, YLimMin));

StringFormat sFormat = new StringFormat();

sFormat.Alignment = StringAlignment.Center;

g.DrawString((fX + xticklabel).ToString(), TickFont, aBrush,

new PointF(form1.PlotPanel.Left + yAxisPoint.X, form1.PlotPanel.Top + yAxisPoint.Y + 4f), sFormat);

}

// Create the y-axis tick labels:

float ym = YLimMin + YTickOffset;

float yticklabel = 0f;

if (BarType == BarTypeEnum.Horizontal ||

BarType == BarTypeEnum.HorizontalOverlay ||

BarType == BarTypeEnum.HorizontalStack) {

ym = YTickOffset + YLimMin + YTick / 2;

yticklabel = YTick / 2;

}

for (float fY = ym; fY <= YLimMax; fY += YTick) {

PointF xAxisPoint = Point2D(new PointF(XLimMin, fY));

StringFormat sFormat = new StringFormat();

sFormat.Alignment = StringAlignment.Far;

g.DrawString((fY + yticklabel).ToString(), TickFont, aBrush,

new PointF(form1.PlotPanel.Left + xAxisPoint.X - 3f, form1.PlotPanel.Top + xAxisPoint.Y

- tickFontSize.Height / 2), sFormat);

}

AddLabels(g);

}

private void SetPlotPanel(Graphics g) {

// Set form1.PlotPanel:

float xOffset = ChartArea.Width / 30.0f;

float yOffset = ChartArea.Height / 30.0f;

SizeF labelFontSize = g.MeasureString("A", LabelFont);

SizeF titleFontSize = g.MeasureString("A", TitleFont);

if (Title.ToUpper() == "NO TITLE") {

titleFontSize.Width = 8f;

titleFontSize.Height = 8f;

}

float xSpacing = xOffset / 3.0f;

float ySpacing = yOffset / 3.0f;

SizeF tickFontSize = g.MeasureString("A", TickFont);

float tickSpacing = 2f;

SizeF yTickSize = g.MeasureString(

YLimMin.ToString(), TickFont);

for (float yTick = YLimMin + YTickOffset;

yTick <= YLimMax; yTick += YTick) {

SizeF tempSize = g.MeasureString(

yTick.ToString(), TickFont);

if (yTickSize.Width < tempSize.Width) {

yTickSize = tempSize;

} }

float leftMargin = xOffset + labelFontSize.Width + xSpacing + yTickSize.Width + tickSpacing;

float rightMargin = xOffset;

float topMargin = yOffset + titleFontSize.Height + ySpacing;

float bottomMargin = yOffset + labelFontSize.Height + ySpacing + tickSpacing + tickFontSize.Height;

// Define the plot panel size:

int[] panelsize = new int[4];

form1.PlotPanel.Left = ChartArea.X + (int)leftMargin;

form1.PlotPanel.Top = ChartArea.Y + (int)topMargin;

form1.PlotPanel.Width = ChartArea.Width –

(int)leftMargin - 2 * (int)rightMargin;

form1.PlotPanel.Height = ChartArea.Height – (int)topMargin - (int)bottomMargin;

form1.PlotPanel.BackColor = plotBackColor;

}

private void AddLabels(Graphics g) {

float xOffset = ChartArea.Width / 30.0f;

float yOffset = ChartArea.Height / 30.0f;

SizeF labelFontSize = g.MeasureString("A", LabelFont);

SizeF titleFontSize = g.MeasureString("A", TitleFont);

// Add horizontal axis label:

SolidBrush aBrush = new SolidBrush(LabelFontColor);

SizeF stringSize = g.MeasureString(XLabel, LabelFont);

g.DrawString(XLabel, LabelFont, aBrush, new Point(form1.PlotPanel.Left + form1.PlotPanel.Width / 2 -

(int)stringSize.Width / 2, ChartArea.Bottom - (int)yOffset - (int)labelFontSize.Height));

// Add y-axis label:

StringFormat sFormat = new StringFormat();

sFormat.Alignment = StringAlignment.Center;

stringSize = g.MeasureString(YLabel, LabelFont);

// Save the state of the current Graphics object GraphicsState gState = g.Save();

g.TranslateTransform(ChartArea.X + xOffset, ChartArea.Y + yOffset + titleFontSize.Height

+ yOffset / 3 + form1.PlotPanel.Height / 2);

g.RotateTransform(-90);

g.DrawString(YLabel, LabelFont, aBrush, 0, 0, sFormat);

// Restore it:

g.Restore(gState);

// Add title:

aBrush = new SolidBrush(TitleFontColor);

stringSize = g.MeasureString(Title, TitleFont);

if (Title.ToUpper() != "NO TITLE") {

g.DrawString(Title, TitleFont, aBrush, new Point(form1.PlotPanel.Left + form1.PlotPanel.Width / 2 - (int)stringSize.Width / 2, ChartArea.Top + (int)yOffset));

}

aBrush.Dispose();

}