本文摘选自《Practical C# Charts and Graphics》,有兴趣的朋友可以到网上下载电子书。
今天主要贴出一些如何实现K线图(蜡烛图)的代码,废话不多说,直接看下面。
1.外面文件读取类(TextFileReader.cs):
View Code
using System; using System.Text; using System.Collections.Specialized; using System.Collections; using System.Windows.Forms; using System.IO; namespace Example4_9 { class TextFileReader { public string[,] ReadTextFile(string fileName) { if (File.Exists(fileName)) { string[,] sArray = ReadFile(fileName); return sArray; } else { return null; } } private string[,] ReadFile(string fileName) { try { StringCollection sc = new StringCollection(); FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.ReadWrite); StreamReader sr = new StreamReader(fs); // Read file into a string collection int noBytesRead = 0; string oneLine; while ((oneLine = sr.ReadLine()) != null) { noBytesRead += oneLine.Length; sc.Add(oneLine); } sr.Close(); string[] sArray = new string[sc.Count]; sc.CopyTo(sArray, 0); char[] cSplitter = { ' ', ',', ':', '\t' }; string[] sArray1 = sArray[0].Split(cSplitter); string[,] sArray2 = new string[sArray1.Length, sc.Count]; for (int i = 0; i < sc.Count; i++) { sArray1 = sArray[sc.Count - 1 - i].Split(cSplitter); for (int j = 0; j < sArray1.Length; j++) { sArray2[j, i] = sArray1[j]; } } return sArray2; } catch (Exception e) { MessageBox.Show(e.Message, "Error Saving File."); return null; } } } }
2.股票数据(DataSeries.cs)
View Code
using System; using System.Drawing; namespace Example4_9 { public class DataSeries { private string[,] dataString; private LineStyle lineStyle; private string seriesName = ""; public DataSeries() { lineStyle = new LineStyle(); } public LineStyle LineStyle { get { return lineStyle; } set { lineStyle = value; } } public string[,] DataString { get { return dataString; } set { dataString = value; } } public string SeriesName { get { return seriesName; } set { seriesName = value; } } } }
3.数据集合(DataCollection.cs)
View Code
using System; using System.Collections; using System.Drawing; namespace Example4_9 { public class DataCollection { private Form1 form1; private ArrayList dataSeriesList; private int dataSeriesIndex = 0; private StockChartTypeEnum stockChartType = StockChartTypeEnum.HiLoOpenClose; public DataCollection(Form1 fm1) { dataSeriesList = new ArrayList(); form1 = fm1; } public StockChartTypeEnum StockChartType { get { return stockChartType; } set { stockChartType = value; } } public enum StockChartTypeEnum { HiLo = 0, HiLoOpenClose = 1, Candle = 2 } public ArrayList DataSeriesList { get { return dataSeriesList; } set { dataSeriesList = value; } } public int DataSeriesIndex { get { return dataSeriesIndex; } set { dataSeriesIndex = value; } } public void Add(DataSeries ds) { dataSeriesList.Add(ds); if (ds.SeriesName == "") { ds.SeriesName = "DataSeries" + dataSeriesList.Count.ToString(); } } public void Insert(int dataSeriesIndex, DataSeries ds) { dataSeriesList.Insert(dataSeriesIndex, ds); if (ds.SeriesName == "") { dataSeriesIndex = dataSeriesIndex + 1; ds.SeriesName = "DataSeries" + dataSeriesIndex.ToString(); } } public void Remove(string dataSeriesName) { if (dataSeriesList != null) { for (int i = 0; i < dataSeriesList.Count; i++) { DataSeries ds = (DataSeries)dataSeriesList[i]; if (ds.SeriesName == dataSeriesName) { dataSeriesList.RemoveAt(i); } } } } public void RemoveAll() { dataSeriesList.Clear(); } public void AddStockChart(Graphics g, ChartStyle cs) { foreach (DataSeries ds in DataSeriesList) { Pen aPen = new Pen(ds.LineStyle.LineColor, ds.LineStyle.Thickness); aPen.DashStyle = ds.LineStyle.Pattern; SolidBrush aBrush = new SolidBrush(ds.LineStyle.LineColor); SolidBrush whiteBrush = new SolidBrush(Color.White); float barLength = form1.PlotPanel.Width / (5*ds.DataString.GetLength(1)); for (int i = 0; i < ds.DataString.GetLength(1); i++) { float[] stockdata = new float[4]; for (int j = 0; j < stockdata.Length; j++) { stockdata[j] = Convert.ToSingle(ds.DataString[j+1,i]); } PointF ptHigh = cs.Point2D(new PointF(i, stockdata[1])); PointF ptLow = cs.Point2D(new PointF(i, stockdata[2])); PointF ptOpen = cs.Point2D(new PointF(i, stockdata[0])); PointF ptCLose = cs.Point2D(new PointF(i, stockdata[3])); PointF ptOpen1 = new PointF(ptOpen.X - barLength, ptOpen.Y); PointF ptClose1 = new PointF(ptCLose.X + barLength, ptCLose.Y); PointF ptOpen2 = new PointF(ptOpen.X + barLength, ptOpen.Y); PointF ptClose2 = new PointF(ptCLose.X - barLength, ptCLose.Y); // Draw Hi-Lo stock chart: if (StockChartType == StockChartTypeEnum.HiLo) { g.DrawLine(aPen, ptHigh, ptLow); } // Draw Hi-Li-Open-Close chart: else if (StockChartType == StockChartTypeEnum.HiLoOpenClose) { g.DrawLine(aPen, ptHigh, ptLow); g.DrawLine(aPen,ptOpen, ptOpen1); g.DrawLine(aPen, ptCLose, ptClose1); } // Draw candle chart: else if (stockChartType == StockChartTypeEnum.Candle) { PointF[] pts = new PointF[4]; pts[0] = ptOpen1; pts[1] = ptOpen2; pts[2] = ptClose1; pts[3] = ptClose2; g.DrawLine(aPen, ptHigh, ptLow); if (stockdata[0] > stockdata[3]) { g.FillPolygon(aBrush,pts); } else if (stockdata[0] < stockdata[3]) { g.FillPolygon(whiteBrush, pts); } g.DrawPolygon(aPen, pts); } } aPen.Dispose(); aBrush.Dispose(); whiteBrush.Dispose(); } } } }
4.股票图形类别(ChartType.cs)
View Code
using System; using System.Collections.Generic; using System.Collections; using System.Drawing; using System.Drawing.Drawing2D; namespace Example4_9 { public class ChartStyle { private Form1 form1; private Rectangle chartArea; private Color chartBackColor; private Color chartBorderColor; private Color plotBackColor = Color.White; private Color plotBorderColor = Color.Black; private DashStyle gridPattern = DashStyle.Solid; private Color gridColor = Color.LightGray; private float gridLineThickness = 1.0f; private bool isXGrid = false; private bool isYGrid = false; private string xLabel = "X Axis"; private string yLabel = "Y Axis"; private string sTitle = "Title"; private Font labelFont = new Font("Arial", 10, FontStyle.Regular); private Color labelFontColor = Color.Black; private Font titleFont = new Font("Arial", 12, FontStyle.Regular); private Color titleFontColor = Color.Black; private float xLimMin = 0f; private float xLimMax = 10f; private float yLimMin = 0f; private float yLimMax = 10f; private float xTick = 1f; private float yTick = 2f; private Font tickFont; private Color tickFontColor = Color.Black; private float xtickOffset = 0f; private float ytickOffset = 0f; private BarTypeEnum barType = BarTypeEnum.Vertical; public ChartStyle(Form1 fm1) { form1 = fm1; chartArea = form1.ClientRectangle; chartBackColor = fm1.BackColor; chartBorderColor = fm1.BackColor; tickFont = form1.Font; } public BarTypeEnum BarType { get { return barType; } set { barType = value; } } public enum BarTypeEnum { Vertical = 0, Horizontal = 1, VerticalStack = 2, HorizontalStack = 3, VerticalOverlay = 4, HorizontalOverlay = 5 } public Font TickFont { get { return tickFont; } set { tickFont = value; } } public Color TickFontColor { get { return tickFontColor; } set { tickFontColor = value; } } public Color ChartBackColor { get { return chartBackColor; } set { chartBackColor = value; } } public float XTickOffset { get { return xtickOffset; } set { xtickOffset = value; } } public float YTickOffset { get { return ytickOffset; } set { ytickOffset = value; } } public Color ChartBorderColor { get { return chartBorderColor; } set { chartBorderColor = value; } } public Color PlotBackColor { get { return plotBackColor; } set { plotBackColor = value; } } public Color PlotBorderColor { get { return plotBorderColor; } set { plotBorderColor = value; } } public Rectangle ChartArea { get { return chartArea; } set { chartArea = value; } } public bool IsXGrid { get { return isXGrid; } set { isXGrid = value; } } public bool IsYGrid { get { return isYGrid; } set { isYGrid = value; } } public string Title { get { return sTitle; } set { sTitle = value; } } public string XLabel { get { return xLabel; } set { xLabel = value; } } public string YLabel { get { return yLabel; } set { yLabel = value; } } public Font LabelFont { get { return labelFont; } set { labelFont = value; } } public Color LabelFontColor { get { return labelFontColor; } set { labelFontColor = value; } } public Font TitleFont { get { return titleFont; } set { titleFont = value; } } public Color TitleFontColor { get { return titleFontColor; } set { titleFontColor = value; } } public float XLimMax { get { return xLimMax; } set { xLimMax = value; } } public float XLimMin { get { return xLimMin; } set { xLimMin = value; } } public float YLimMax { get { return yLimMax; } set { yLimMax = value; } } public float YLimMin { get { return yLimMin; } set { yLimMin = value; } } public float XTick { get { return xTick; } set { xTick = value; } } public float YTick { get { return yTick; } set { yTick = value; } } virtual public DashStyle GridPattern { get { return gridPattern; } set { gridPattern = value; } } public float GridThickness { get { return gridLineThickness; } set { gridLineThickness = value; } } virtual public Color GridColor { get { return gridColor; } set { gridColor = value; } } 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; } // 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(); } public void SetChartArea(Graphics g) { 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; xticklabel = 0; } 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(); } public PointF Point2D(PointF pt) { PointF aPoint = new PointF(); aPoint.X = (pt.X - XLimMin) * form1.PlotPanel.Width / (XLimMax - XLimMin); aPoint.Y = form1.PlotPanel.Height -(pt.Y - YLimMin) * form1.PlotPanel.Height / (YLimMax - YLimMin); return aPoint; } } }
5.改善后画线类(LineStyle.cs)
View Code
using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Drawing2D; using System.Text; namespace Example4_9 { public class LineStyle { private DashStyle linePattern = DashStyle.Solid; private Color lineColor = Color.Black; private float LineThickness = 1.0f; private PlotLinesMethodEnum pltLineMethod = PlotLinesMethodEnum.Lines; private bool isVisible = true; public LineStyle() { } public bool IsVisible { get { return isVisible; } set { isVisible = value; } } public PlotLinesMethodEnum PlotMethod { get { return pltLineMethod; } set { pltLineMethod = value; } } virtual public DashStyle Pattern { get { return linePattern; } set { linePattern = value; } } public float Thickness { get { return LineThickness; } set { LineThickness = value; } } virtual public Color LineColor { get { return lineColor; } set { lineColor = value; } } public enum PlotLinesMethodEnum { Lines = 0, Splines = 1 } } }
6.Form1中相关代码
View Code
using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Windows.Forms; namespace Example4_9 { public partial class Form1 : Form { private ChartStyle cs; private DataCollection dc; public Form1() { InitializeComponent(); this.SetStyle(ControlStyles.ResizeRedraw, true); this.BackColor = Color.White; // Subscribing to a paint eventhandler to drawingPanel: PlotPanel.Paint += new PaintEventHandler(PlotPanelPaint); cs = new ChartStyle(this); dc = new DataCollection(this); // Specify chart style parameters: cs.Title = "Chart of GE Stock"; cs.XTickOffset = 1; cs.XLimMin = -1f; cs.XLimMax = 20f; cs.YLimMin = 32f; cs.YLimMax = 36f; cs.XTick = 2f; cs.YTick = 0.5f; dc.StockChartType = DataCollection.StockChartTypeEnum.HiLoOpenClose; } private void AddData() { dc.DataSeriesList.Clear(); TextFileReader tfr = new TextFileReader(); DataSeries ds = new DataSeries(); // Add GE stock data from a text data file: ds = new DataSeries(); ds.DataString = tfr.ReadTextFile("GE.txt"); ds.LineStyle.LineColor = Color.DarkBlue; dc.Add(ds); } private void PlotPanelPaint(object sender, PaintEventArgs e) { Graphics g = e.Graphics; AddData(); cs.PlotPanelStyle(g); dc.AddStockChart(g, cs); } protected override void OnPaint(PaintEventArgs e) { Graphics g = e.Graphics; cs.ChartArea = this.ClientRectangle; cs.SetChartArea(g); } } }
改变股票图表样式,修改dc.StockChartType属性即可,如: dc.StockChartType = DataCollection.StockChartTypeEnum.HiLoOpenClose;
数据文件如下:
5-May-06 34.94 35.22 34.87 35.16 4-May-06 34.5 34.94 34.48 34.8 3-May-06 34.22 34.67 34.19 34.4 2-May-06 34.39 34.59 34.1 34.48 1-May-06 34.64 34.72 34.32 34.39 28-Apr-06 34.49 34.78 34.35 34.59 27-Apr-06 33.9 34.68 33.89 34.43 26-Apr-06 34.07 34.44 33.88 34.13 25-Apr-06 34 34.06 33.8 33.97 24-Apr-06 33.81 34 33.8 33.93 21-Apr-06 34.25 34.32 33.68 33.97 20-Apr-06 33.8 34.18 33.63 34.12 19-Apr-06 33.95 33.97 33.5 33.89 18-Apr-06 33.52 33.97 33.21 33.87 17-Apr-06 33.76 33.76 33.07 33.29 13-Apr-06 34.19 34.36 33.61 33.89 12-Apr-06 34.3 34.53 34.17 34.46 11-Apr-06 33.92 34.07 33.63 34.05 10-Apr-06 34.06 34.08 33.8 33.92 7-Apr-06 34.55 34.75 34.01 34.03
最终样式如下: