使用c#类库绘制柱状图
生活随笔
收集整理的這篇文章主要介紹了
使用c#类库绘制柱状图
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
代碼有注釋,在通用性方面進行了處理
可以指定極值,可以自定義分段,對樣本數據分段比較靈活,
不填臨界值時平均分段,不指定極值時樣本數據中的最大值為極大值。極小值默認為0,但可以設置。
/// <summary>/// 提供獲取統計圖的相關方法/// 適合有極值和無極值,無極值時采用樣本內的極值/// 除極小值包含在近右片外,其他臨界值包含在近左片/// </summary>public class StatisticsGraph{/// <summary>/// 樣本數據/// </summary>private List<float> Samples = null;private int NumberOfSegments = 10;//段數 即得到的圖線有10個柱private double MaxSample = 0;//最大樣本數據private double MinSample = 0;//最小樣本數據private int MaxExtremum = 100;//樣本極值 ,如統計學生成績時 極值一般為100,即卷滿分private float[] Demarcation = new float[] { 10, 20, 30, 40, 50, 60, 70, 80, 90 };//限有極值時設置/// <summary>/// 根據樣本數據初始化/// </summary>/// <param name="samples"></param>public StatisticsGraph(List<float> samples){this.Init(samples);}/// <summary>/// 通過指定分片段數和樣本數據進行初始化/// </summary>/// <param name="samples"></param>/// <param name="numberOfSegments"></param>public StatisticsGraph(List<float> samples, int numberOfSegments){this.HasSpecifiedExtremum = false;this.Init(samples);this.NumberOfSegments = numberOfSegments;this.Demarcation = GetDemarcation();}/// <summary>/// 通過指定分片段數、極大值和樣本數據進行初始化/// </summary>/// <param name="samples"></param>/// <param name="numberOfSegments"></param>/// <param name="maxExtremum"></param>public StatisticsGraph(List<float> samples, int numberOfSegments, int maxExtremum){this.HasSpecifiedExtremum = true;this.MaxExtremum = maxExtremum;this.Init(samples);this.NumberOfSegments = numberOfSegments;this.Demarcation = GetDemarcation();}/// <summary>/// 通過指定極大值、樣本數據和自定義分段臨界值進行初始化/// </summary>/// <param name="samples"></param>/// <param name="demarcation"></param>/// <param name="maxExtremum"></param>public StatisticsGraph(List<float> samples, float[] demarcation, int maxExtremum){this.HasSpecifiedExtremum = true;this.Init(samples);this.MaxExtremum = maxExtremum;this.Demarcation = demarcation;this.NumberOfSegments = demarcation.Count();}public int MinExtremum = 0;//最小極值默認為0private void Init(List<float> samples){this.Samples = samples;this.MaxSample = Samples.Max();this.MinSample = Samples.Min();}/// <summary>/// 是否指定了極大值/// </summary>private bool _hasSpecifiedExtremum = true;/// <summary>/// 是否已經指定了極大值/// </summary>public bool HasSpecifiedExtremum { get { return _hasSpecifiedExtremum; } private set { _hasSpecifiedExtremum = value; } }/// <summary>/// 獲取每個分片的臨界點/// </summary>/// <returns></returns>private float[] GetDemarcation(){if (NumberOfSegments <= 1){return null;}float[] result = new float[NumberOfSegments - 1];if (HasSpecifiedExtremum == false)//未指定極大值時 樣本中最大值為統計范圍內極大值 {MaxExtremum = Convert.ToInt32(Math.Floor(MaxSample)) + 1;MinExtremum = Convert.ToInt32(Math.Floor(MinSample)) - 1;}int span = MaxExtremum - MinExtremum;//極值跨度float segSpan = span * 1f / NumberOfSegments;//每個片段的跨度for (int i = 0; i < NumberOfSegments - 1; i++){result[i] = MinExtremum + segSpan * (i + 1);}return result;}/// <summary>/// 獲取各個分片的樣本數量/// 結果《分片的極右值,該片的樣本數》/// 分片總數為臨界點數+1個/// </summary>/// <returns></returns>private Dictionary<float, int> GetSampleNumbersOfPerSegment(){Dictionary<float, int> result = new Dictionary<float, int>();List<float> segRightValue = new List<float>();//分片極右值foreach (var item in Demarcation){segRightValue.Add(item);}segRightValue.Add(MaxExtremum);for (int i = 0; i < segRightValue.Count; i++){int value = 0;foreach (float m in Samples)//計算每個片段的樣本數 {if (i == 0){if (m <= segRightValue[i]){value += 1;}}else{if (m <= segRightValue[i] && m > segRightValue[i - 1]){value += 1;}}}result.Add(segRightValue[i], value);}return result;}/// <summary>/// 獲取每個分片的左上頂點坐標/// </summary>/// <param name="UsableWidth">最大利用寬度</param>/// <param name="UsableHeight">最小利用寬度</param>/// <returns></returns>private List<PointF> GetTopLeftPointFOfSegment(PointF bottomLeft, float UsableWidth, float UsableHeight, out float widthPerSeg, out float unitHeight, out Dictionary<float, int> SampleNumbersOfPerSegment){List<PointF> result = new List<PointF>();SampleNumbersOfPerSegment = GetSampleNumbersOfPerSegment();//獲取每個片段占有的樣本數int maxSampleNumbersOfSegment = SampleNumbersOfPerSegment.Max(x => x.Value);//所有分片中 最大分片樣本數 widthPerSeg = UsableWidth * 1f / (SampleNumbersOfPerSegment.Count * 2 + 1);//每個分片的寬度 分片之間還有空白分片 unitHeight = UsableHeight * 1f / maxSampleNumbersOfSegment;//充分利用高度的情況下 單位樣本數所占高度for (int i = 0; i < SampleNumbersOfPerSegment.Count; i++){PointF pf = new PointF();pf.X = bottomLeft.X + (i * 2 + 1) * widthPerSeg;//每個片段的左邊X坐標 pf.Y = bottomLeft.Y - SampleNumbersOfPerSegment.ElementAt(i).Value * unitHeight;//每個片段上邊Y坐標 result.Add(pf);}return result;}/// <summary>/// 獲取每個分片的 條形數據(包括:左上角坐標,高度和寬度)/// </summary>/// <param name="UsableWidth"></param>/// <param name="UsableHeight"></param>/// <returns></returns>private RectangleF[] GetRectangleFs(PointF bottomLeft, float UsableWidth, float UsableHeight, out Dictionary<float, int> SampleNumbersOfPerSegment){float widthPerSeg = 0;//每個片段的寬度float unitHeight = 0;//單位樣本數據在Y軸上表示需要的高度//每個片段的左上角坐標List<PointF> pfs = GetTopLeftPointFOfSegment(bottomLeft, UsableWidth, UsableHeight, out widthPerSeg, out unitHeight, out SampleNumbersOfPerSegment);RectangleF[] RFs = new RectangleF[pfs.Count];for (int i = 0; i < pfs.Count; i++){//通過計算寬度和高度 結合左上角坐標 以準確描述每個矩形的大小和位置RFs[i] = new RectangleF(pfs[i], new SizeF(widthPerSeg, SampleNumbersOfPerSegment.ElementAt(i).Value * unitHeight));}return RFs;}/// <summary>/// 獲得10段柱狀圖/// 橫軸 分數段;縱軸 該分數段的 人數/// </summary>/// <param name="width"></param>/// <param name="heigh"></param>/// <param name="unitName"></param>/// <param name="familyName"></param>/// <returns></returns>public Bitmap GetBargraph(int width, int heigh, string XunitName, string YunitName, string familyName = "宋體"){Font font = new Font(familyName, 10);Bitmap bitmap = new Bitmap(width, heigh);Graphics gdi = Graphics.FromImage(bitmap);//用白色填充整個圖片,因為默認是黑色 gdi.Clear(Color.White);//抗鋸齒gdi.SmoothingMode = SmoothingMode.HighQuality;//高質量的文字gdi.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;//像素均偏移0.5個單位,以消除鋸齒gdi.PixelOffsetMode = PixelOffsetMode.Half;int margin = 30;//坐標軸與邊框的距離int padding = 20;//實際表示內容區域 與坐標軸右和上邊的距離 PointF bottomLeft = new PointF(margin, heigh - margin);//坐標原點 PointF topLeft = new PointF(bottomLeft.X, margin);//Y軸最上頂端坐標 PointF topLeft_bl = new PointF(topLeft.X - 6, topLeft.Y + 10);//Y軸箭頭左下端坐標 PointF topLeft_br = new PointF(topLeft.X + 6, topLeft_bl.Y);//Y軸箭頭右下端坐標 gdi.DrawLines(Pens.Black, new PointF[] { topLeft_bl, topLeft, topLeft_br });//畫坐標軸Y軸箭頭 gdi.DrawString(string.Format("({0})", YunitName), font, Brushes.Black, topLeft_bl.X - 20, topLeft.Y - 14);//在Y軸箭頭左下角寫上 Y軸表示的單位 PointF bottomRight = new PointF(width - margin, bottomLeft.Y);//X軸最右端坐標 PointF bottomRight_lt = new PointF(bottomRight.X - 10, bottomRight.Y - 6);//X軸箭頭左上端坐標 PointF bottomRight_lb = new PointF(bottomRight_lt.X, bottomRight.Y + 6);//X軸箭頭右下端坐標 gdi.DrawLines(Pens.Black, new PointF[] { bottomRight_lt, bottomRight, bottomRight_lb });//畫坐標軸X軸箭頭 gdi.DrawString(string.Format("({0})", XunitName), font, Brushes.Black, bottomRight_lt.X - 3, bottomRight_lt.Y + 13);//在X軸箭頭的下方 寫上X軸表示的單位 gdi.DrawLines(Pens.Black, new PointF[] { topLeft, bottomLeft, bottomRight });//畫坐標軸float usableHeight = bottomLeft.Y - margin - padding;//內容區高度float usableWidth = width - margin * 2 - padding;//內容區寬度 Dictionary<float, int> SampleNumbersOfPerSegment = null;//各個片段的描述數據 RectangleF[] RFs = GetRectangleFs(bottomLeft, usableWidth, usableHeight, out SampleNumbersOfPerSegment);//獲取條形圖位置數據 gdi.FillRectangles(new SolidBrush(Color.FromArgb(70, 161, 185)), RFs);//填充柱形//標上坐標軸上的數據//X軸上寫的內容string Xcontent = string.Empty;//條形頂上方寫的內容string Ycontent = string.Empty;for (int i = 0; i < SampleNumbersOfPerSegment.Count; i++){if (i == 0)Xcontent = "X<=" + SampleNumbersOfPerSegment.ElementAt(i).Key.ToString("F0");else{Xcontent = string.Format("{0}<X<={1}", SampleNumbersOfPerSegment.ElementAt(i - 1).Key.ToString("F0"), SampleNumbersOfPerSegment.ElementAt(i).Key.ToString("F0"));}gdi.DrawString(Xcontent, font, Brushes.Black, RFs[i].X - 8, bottomLeft.Y + 5); //寫上X軸上的數據 Ycontent = SampleNumbersOfPerSegment.ElementAt(i).Value.ToString();gdi.DrawString(Ycontent, font, Brushes.Black, RFs[i].X + 3, RFs[i].Y - 13); //寫上Y軸上的數據 }return bitmap;}}
調用:
一張圖片大約耗時20毫秒。
測試數據得到的柱形圖:
?
?
轉載于:https://www.cnblogs.com/langu/archive/2013/05/13/3075658.html
總結
以上是生活随笔為你收集整理的使用c#类库绘制柱状图的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Insert intervals
- 下一篇: LLBL Gen Pro 设计器使用指南