天天看点

Stock Charts With GDI+

本文摘选自《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      

最终样式如下:

Stock Charts With GDI+

转载于:https://www.cnblogs.com/jiewong/archive/2012/12/06/stock.html

c#