(七十四)c#Winform自定义控件-金字塔图表-HZHControls
官网
前提
入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章。
GitHub:https://github.com/kwwwvagaa/NetWinformControl
码云:https://gitee.com/kwwwvagaa/net_winform_custom_control.git
如果觉得写的还行,请点个 star 支持一下吧
来都来了,点个【推荐】再走吧,谢谢
NuGet
Install-Package HZH_Controls
目录
https://www.cnblogs.com/bfyx/p/11364884.html
用处及效果
准备工作
依然使用GDI+画图,不懂的先百度了解下
开始
添加一些枚举
1 public enum FunelChartAlignment 2 { 3 /// <summary> 4 /// The left 5 /// </summary> 6 Left, 7 /// <summary> 8 /// The center 9 /// </summary> 10 Center, 11 /// <summary> 12 /// The right 13 /// </summary> 14 Right 15 } 16 17 public enum FunelChartDirection 18 { 19 /// <summary> 20 /// Up 21 /// </summary> 22 UP, 23 /// <summary> 24 /// Down 25 /// </summary> 26 Down 27 }
添加一个项实体
1 public class FunelChartItem 2 { 3 /// <summary> 4 /// Gets or sets the text. 5 /// </summary> 6 /// <value>The text.</value> 7 public string Text { get; set; } 8 /// <summary> 9 /// Gets or sets the value. 10 /// </summary> 11 /// <value>The value.</value> 12 public float Value { get; set; } 13 /// <summary> 14 /// Gets or sets the color of the value. 15 /// </summary> 16 /// <value>The color of the value.</value> 17 public System.Drawing.Color? ValueColor { get; set; } 18 /// <summary> 19 /// Gets or sets the color of the text fore. 20 /// </summary> 21 /// <value>The color of the text fore.</value> 22 public System.Drawing.Color? TextForeColor { get; set; } 23 }
添加一个类UCFunnelChart ,继承UserControl
添加一些控制属性
1 /// <summary> 2 /// The title 3 /// </summary> 4 private string title; 5 /// <summary> 6 /// Gets or sets the title. 7 /// </summary> 8 /// <value>The title.</value> 9 [Browsable(true)] 10 [Category("自定义")] 11 [Description("获取或设置标题")] 12 public string Title 13 { 14 get { return title; } 15 set 16 { 17 title = value; 18 ResetTitleSize(); 19 Invalidate(); 20 } 21 } 22 23 /// <summary> 24 /// The title font 25 /// </summary> 26 private Font titleFont = new Font("微软雅黑", 12); 27 /// <summary> 28 /// Gets or sets the title font. 29 /// </summary> 30 /// <value>The title font.</value> 31 [Browsable(true)] 32 [Category("自定义")] 33 [Description("获取或设置标题字体")] 34 public Font TitleFont 35 { 36 get { return titleFont; } 37 set 38 { 39 titleFont = value; 40 ResetTitleSize(); 41 Invalidate(); 42 } 43 } 44 45 /// <summary> 46 /// The title fore color 47 /// </summary> 48 private Color titleForeColor = Color.Black; 49 /// <summary> 50 /// Gets or sets the color of the title fore. 51 /// </summary> 52 /// <value>The color of the title fore.</value> 53 [Browsable(true)] 54 [Category("自定义")] 55 [Description("获取或设置标题文字颜色")] 56 public Color TitleForeColor 57 { 58 get { return titleForeColor; } 59 set 60 { 61 titleForeColor = value; 62 Invalidate(); 63 } 64 } 65 /// <summary> 66 /// The items 67 /// </summary> 68 private FunelChartItem[] items; 69 /// <summary> 70 /// Gets or sets the items. 71 /// </summary> 72 /// <value>The items.</value> 73 [Browsable(true)] 74 [Category("自定义")] 75 [Description("获取或设置项目")] 76 public FunelChartItem[] Items 77 { 78 get { return items; } 79 set 80 { 81 items = value; 82 Invalidate(); 83 } 84 } 85 86 /// <summary> 87 /// The direction 88 /// </summary> 89 private FunelChartDirection direction = FunelChartDirection.UP; 90 /// <summary> 91 /// Gets or sets the direction. 92 /// </summary> 93 /// <value>The direction.</value> 94 [Browsable(true)] 95 [Category("自定义")] 96 [Description("获取或设置方向")] 97 public FunelChartDirection Direction 98 { 99 get { return direction; } 100 set 101 { 102 direction = value; 103 Invalidate(); 104 } 105 } 106 107 /// <summary> 108 /// The alignment 109 /// </summary> 110 private FunelChartAlignment alignment = FunelChartAlignment.Center; 111 /// <summary> 112 /// Gets or sets the alignment. 113 /// </summary> 114 /// <value>The alignment.</value> 115 [Browsable(true)] 116 [Category("自定义")] 117 [Description("获取或设置对齐方式")] 118 public FunelChartAlignment Alignment 119 { 120 get { return alignment; } 121 set 122 { 123 alignment = value; 124 Invalidate(); 125 } 126 } 127 128 /// <summary> 129 /// The item text align 130 /// </summary> 131 private FunelChartAlignment itemTextAlign = FunelChartAlignment.Center; 132 /// <summary> 133 /// Gets or sets the item text align. 134 /// </summary> 135 /// <value>The item text align.</value> 136 [Browsable(true)] 137 [Category("自定义")] 138 [Description("获取或设置文字位置")] 139 public FunelChartAlignment ItemTextAlign 140 { 141 get { return itemTextAlign; } 142 set 143 { 144 itemTextAlign = value; 145 ResetWorkingRect(); 146 Invalidate(); 147 } 148 } 149 /// <summary> 150 /// The show value 151 /// </summary> 152 private bool showValue = false; 153 /// <summary> 154 /// Gets or sets a value indicating whether [show value]. 155 /// </summary> 156 /// <value><c>true</c> if [show value]; otherwise, <c>false</c>.</value> 157 [Browsable(true)] 158 [Category("自定义")] 159 [Description("获取或设置是否显示值")] 160 public bool ShowValue 161 { 162 get { return showValue; } 163 set 164 { 165 showValue = value; 166 Invalidate(); 167 } 168 } 169 170 171 /// <summary> 172 /// The value format 173 /// </summary> 174 private string valueFormat = "0.##"; 175 /// <summary> 176 /// Gets or sets the value format. 177 /// </summary> 178 /// <value>The value format.</value> 179 [Browsable(true)] 180 [Category("自定义")] 181 [Description("获取或设置值格式化")] 182 public string ValueFormat 183 { 184 get { return valueFormat; } 185 set 186 { 187 valueFormat = value; 188 Invalidate(); 189 } 190 } 191 192 /// <summary> 193 /// The m rect working 194 /// </summary> 195 RectangleF m_rectWorking; 196 /// <summary> 197 /// The m title size 198 /// </summary> 199 SizeF m_titleSize = SizeF.Empty; 200 /// <summary> 201 /// The int split width 202 /// </summary> 203 int intSplitWidth = 1;
构造函数初始化
1 public UCFunnelChart() 2 { 3 this.SetStyle(ControlStyles.AllPaintingInWmPaint, true); 4 this.SetStyle(ControlStyles.DoubleBuffer, true); 5 this.SetStyle(ControlStyles.ResizeRedraw, true); 6 this.SetStyle(ControlStyles.Selectable, true); 7 this.SetStyle(ControlStyles.SupportsTransparentBackColor, true); 8 this.SetStyle(ControlStyles.UserPaint, true); 9 this.FontChanged += UCFunnelChart_FontChanged; 10 Font = new Font("微软雅黑", 8); 11 12 this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; 13 this.SizeChanged += UCFunnelChart_SizeChanged; 14 Size = new System.Drawing.Size(150, 150); 15 items = new FunelChartItem[0]; 16 if (ControlHelper.IsDesignMode()) 17 { 18 items = new FunelChartItem[5]; 19 for (int i = 0; i < 5; i++) 20 { 21 items[i] = new FunelChartItem() 22 { 23 Text = "item" + i, 24 Value = 10 * (i + 1) 25 }; 26 } 27 } 28 }
当大小及状态改变时 重新计算工作区域
1 void UCFunnelChart_FontChanged(object sender, EventArgs e) 2 { 3 ResetWorkingRect(); 4 } 5 6 /// <summary> 7 /// Handles the SizeChanged event of the UCFunnelChart control. 8 /// </summary> 9 /// <param name="sender">The source of the event.</param> 10 /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param> 11 void UCFunnelChart_SizeChanged(object sender, EventArgs e) 12 { 13 ResetWorkingRect(); 14 } 15 16 /// <summary> 17 /// Resets the working rect. 18 /// </summary> 19 private void ResetWorkingRect() 20 { 21 if (itemTextAlign == FunelChartAlignment.Center) 22 { 23 m_rectWorking = new RectangleF(0, m_titleSize.Height == 0 ? 0 : (m_titleSize.Height + 10), this.Width, this.Height - (m_titleSize.Height == 0 ? 0 : (m_titleSize.Height + 10))); 24 } 25 else if (itemTextAlign == FunelChartAlignment.Left) 26 { 27 float fltMax = 0; 28 if (items != null && items.Length > 0) 29 { 30 using (Graphics g = this.CreateGraphics()) 31 { 32 fltMax = items.Max(p => g.MeasureString(p.Text, Font).Width); 33 } 34 } 35 m_rectWorking = new RectangleF(fltMax, m_titleSize.Height == 0 ? 0 : (m_titleSize.Height + 10), this.Width - fltMax, this.Height - (m_titleSize.Height == 0 ? 0 : (m_titleSize.Height + 10))); 36 } 37 else 38 { 39 float fltMax = 0; 40 if (items != null && items.Length > 0) 41 { 42 using (Graphics g = this.CreateGraphics()) 43 { 44 fltMax = items.Max(p => g.MeasureString(p.Text, Font).Width); 45 } 46 } 47 m_rectWorking = new RectangleF(0, m_titleSize.Height == 0 ? 0 : (m_titleSize.Height + 10), this.Width - fltMax, this.Height - (m_titleSize.Height == 0 ? 0 : (m_titleSize.Height + 10))); 48 } 49 } 50 51 /// <summary> 52 /// Resets the size of the title. 53 /// </summary> 54 private void ResetTitleSize() 55 { 56 if (string.IsNullOrEmpty(title)) 57 { 58 m_titleSize = SizeF.Empty; 59 } 60 else 61 { 62 using (Graphics g = this.CreateGraphics()) 63 { 64 m_titleSize = g.MeasureString(title, titleFont); 65 m_titleSize.Height += 20; 66 } 67 } 68 ResetWorkingRect(); 69 }
重绘
1 protected override void OnPaint(PaintEventArgs e) 2 { 3 base.OnPaint(e); 4 var g = e.Graphics; 5 g.SetGDIHigh(); 6 7 if (!string.IsNullOrEmpty(title)) 8 { 9 g.DrawString(title, titleFont, new SolidBrush(titleForeColor), new RectangleF(0, 0, this.Width, m_titleSize.Height), new StringFormat() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center }); 10 } 11 12 if (items == null || items.Length <= 0) 13 { 14 g.DrawString("没有数据", Font, new SolidBrush(Color.Black), this.m_rectWorking, new StringFormat() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center }); 15 return; 16 } 17 18 List<FunelChartItem> lstItems; 19 if (direction == FunelChartDirection.UP) 20 { 21 lstItems = items.OrderBy(p => p.Value).ToList(); 22 } 23 else 24 { 25 lstItems = items.OrderByDescending(p => p.Value).ToList(); 26 } 27 28 List<RectangleF> lstRects = new List<RectangleF>(); 29 List<GraphicsPath> lstPaths = new List<GraphicsPath>(); 30 float maxValue = lstItems.Max(p => p.Value); 31 float dblSplitHeight = m_rectWorking.Height / lstItems.Count; 32 for (int i = 0; i < lstItems.Count; i++) 33 { 34 FunelChartItem item = lstItems[i]; 35 if (item.ValueColor == null || item.ValueColor == Color.Empty || item.ValueColor == Color.Transparent) 36 item.ValueColor = ControlHelper.Colors[i]; 37 38 switch (alignment) 39 { 40 case FunelChartAlignment.Left: 41 lstRects.Add(new RectangleF(m_rectWorking.Left, m_rectWorking.Top + dblSplitHeight * i, item.Value / maxValue * m_rectWorking.Width, dblSplitHeight)); 42 break; 43 case FunelChartAlignment.Center: 44 lstRects.Add(new RectangleF(m_rectWorking.Left + (m_rectWorking.Width - (item.Value / maxValue * m_rectWorking.Width)) / 2, m_rectWorking.Top + dblSplitHeight * i, item.Value / maxValue * m_rectWorking.Width, dblSplitHeight)); 45 break; 46 case FunelChartAlignment.Right: 47 lstRects.Add(new RectangleF(m_rectWorking.Right - (item.Value / maxValue * m_rectWorking.Width), m_rectWorking.Top + dblSplitHeight * i, item.Value / maxValue * m_rectWorking.Width, dblSplitHeight)); 48 break; 49 } 50 } 51 52 for (int i = 0; i < lstRects.Count; i++) 53 { 54 var rect = lstRects[i]; 55 GraphicsPath path = new GraphicsPath(); 56 List<PointF> lstPoints = new List<PointF>(); 57 if (direction == FunelChartDirection.UP) 58 { 59 switch (alignment) 60 { 61 case FunelChartAlignment.Left: 62 lstPoints.Add(new PointF(rect.Left, rect.Top)); 63 if (i != 0) 64 { 65 lstPoints.Add(new PointF(lstRects[i - 1].Right, rect.Top)); 66 } 67 break; 68 case FunelChartAlignment.Center: 69 if (i == 0) 70 { 71 lstPoints.Add(new PointF(rect.Left + rect.Width / 2, rect.Top)); 72 } 73 else 74 { 75 lstPoints.Add(new PointF(lstRects[i - 1].Left, rect.Top)); 76 lstPoints.Add(new PointF(lstRects[i - 1].Right, rect.Top)); 77 } 78 break; 79 case FunelChartAlignment.Right: 80 if (i == 0) 81 { 82 lstPoints.Add(new PointF(rect.Right, rect.Top)); 83 } 84 else 85 { 86 lstPoints.Add(new PointF(rect.Right - lstRects[i - 1].Width, rect.Top)); 87 lstPoints.Add(new PointF(rect.Right, rect.Top)); 88 } 89 break; 90 } 91 lstPoints.Add(new PointF(rect.Right, rect.Bottom - intSplitWidth)); 92 lstPoints.Add(new PointF(rect.Left, rect.Bottom - intSplitWidth)); 93 } 94 else 95 { 96 lstPoints.Add(new PointF(rect.Left, rect.Top + intSplitWidth)); 97 lstPoints.Add(new PointF(rect.Right, rect.Top + intSplitWidth)); 98 switch (alignment) 99 { 100 case FunelChartAlignment.Left: 101 if (i == lstRects.Count - 1) 102 { 103 lstPoints.Add(new PointF(rect.Left, rect.Bottom)); 104 } 105 else 106 { 107 lstPoints.Add(new PointF(lstRects[i + 1].Right, rect.Bottom)); 108 lstPoints.Add(new PointF(rect.Left, rect.Bottom)); 109 } 110 break; 111 case FunelChartAlignment.Center: 112 if (i == lstRects.Count - 1) 113 { 114 lstPoints.Add(new PointF(rect.Left + rect.Width / 2, rect.Bottom)); 115 } 116 else 117 { 118 lstPoints.Add(new PointF(lstRects[i + 1].Right, rect.Bottom)); 119 lstPoints.Add(new PointF(lstRects[i + 1].Left, rect.Bottom)); 120 } 121 break; 122 case FunelChartAlignment.Right: 123 if (i == lstRects.Count - 1) 124 { 125 lstPoints.Add(new PointF(rect.Right, rect.Bottom)); 126 } 127 else 128 { 129 lstPoints.Add(new PointF(rect.Right, rect.Bottom)); 130 lstPoints.Add(new PointF(lstRects[i + 1].Left, rect.Bottom)); 131 } 132 break; 133 } 134 } 135 path.AddLines(lstPoints.ToArray()); 136 path.CloseAllFigures(); 137 // g.DrawPath(new Pen(new SolidBrush(lstItems[i].ValueColor.Value)), path); 138 g.FillPath(new SolidBrush(lstItems[i].ValueColor.Value), path); 139 140 //写字 141 if (itemTextAlign == FunelChartAlignment.Center) 142 { 143 g.DrawString(lstItems[i].Text + (ShowValue ? lstItems[i].Value.ToString("\n" + valueFormat) : ""), Font, new SolidBrush((lstItems[i].TextForeColor == null || lstItems[i].TextForeColor == Color.Empty || lstItems[i].TextForeColor == Color.Transparent) ? Color.White : lstItems[i].TextForeColor.Value), rect, new StringFormat() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center }); 144 } 145 else if (itemTextAlign == FunelChartAlignment.Left) 146 { 147 g.DrawString(lstItems[i].Text + (ShowValue ? lstItems[i].Value.ToString("\n" + valueFormat) : ""), Font, new SolidBrush((lstItems[i].TextForeColor == null || lstItems[i].TextForeColor == Color.Empty || lstItems[i].TextForeColor == Color.Transparent) ? lstItems[i].ValueColor.Value : lstItems[i].TextForeColor.Value), new RectangleF(0, rect.Top, rect.Left, rect.Height), new StringFormat() { Alignment = StringAlignment.Far, LineAlignment = StringAlignment.Center }); 148 g.DrawLine(new Pen(new SolidBrush((lstItems[i].TextForeColor == null || lstItems[i].TextForeColor == Color.Empty || lstItems[i].TextForeColor == Color.Transparent) ? lstItems[i].ValueColor.Value : lstItems[i].TextForeColor.Value)), rect.Left, rect.Top + rect.Height / 2, rect.Left + rect.Width / 2, rect.Top + rect.Height / 2); 149 } 150 else 151 { 152 g.DrawString(lstItems[i].Text + (ShowValue ? lstItems[i].Value.ToString("\n" + valueFormat) : ""), Font, new SolidBrush((lstItems[i].TextForeColor == null || lstItems[i].TextForeColor == Color.Empty || lstItems[i].TextForeColor == Color.Transparent) ? lstItems[i].ValueColor.Value : lstItems[i].TextForeColor.Value), new RectangleF(rect.Right, rect.Top, this.Width - rect.Right, rect.Height), new StringFormat() { Alignment = StringAlignment.Near, LineAlignment = StringAlignment.Center }); 153 g.DrawLine(new Pen(new SolidBrush((lstItems[i].TextForeColor == null || lstItems[i].TextForeColor == Color.Empty || lstItems[i].TextForeColor == Color.Transparent) ? lstItems[i].ValueColor.Value : lstItems[i].TextForeColor.Value)), rect.Left + rect.Width / 2, rect.Top + rect.Height / 2, rect.Right, rect.Top + rect.Height / 2); 154 } 155 } 156 }
完整代码
1 // *********************************************************************** 2 // Assembly : HZH_Controls 3 // Created : 2019-09-26 4 // 5 // *********************************************************************** 6 // <copyright file="UCFunnelChart.cs"> 7 // Copyright by Huang Zhenghui(黄正辉) All, QQ group:568015492 QQ:623128629 Email:623128629@qq.com 8 // </copyright> 9 // 10 // Blog: https://www.cnblogs.com/bfyx 11 // GitHub:https://github.com/kwwwvagaa/NetWinformControl 12 // gitee:https://gitee.com/kwwwvagaa/net_winform_custom_control.git 13 // 14 // If you use this code, please keep this note. 15 // *********************************************************************** 16 using System; 17 using System.Collections.Generic; 18 using System.Linq; 19 using System.Text; 20 using System.Windows.Forms; 21 using System.Drawing; 22 using System.Drawing.Drawing2D; 23 using System.ComponentModel; 24 25 namespace HZH_Controls.Controls 26 { 27 /// <summary> 28 /// Class UCFunnelChart. 29 /// Implements the <see cref="System.Windows.Forms.UserControl" /> 30 /// </summary> 31 /// <seealso cref="System.Windows.Forms.UserControl" /> 32 public class UCFunnelChart : UserControl 33 { 34 /// <summary> 35 /// The title 36 /// </summary> 37 private string title; 38 /// <summary> 39 /// Gets or sets the title. 40 /// </summary> 41 /// <value>The title.</value> 42 [Browsable(true)] 43 [Category("自定义")] 44 [Description("获取或设置标题")] 45 public string Title 46 { 47 get { return title; } 48 set 49 { 50 title = value; 51 ResetTitleSize(); 52 Invalidate(); 53 } 54 } 55 56 /// <summary> 57 /// The title font 58 /// </summary> 59 private Font titleFont = new Font("微软雅黑", 12); 60 /// <summary> 61 /// Gets or sets the title font. 62 /// </summary> 63 /// <value>The title font.</value> 64 [Browsable(true)] 65 [Category("自定义")] 66 [Description("获取或设置标题字体")] 67 public Font TitleFont 68 { 69 get { return titleFont; } 70 set 71 { 72 titleFont = value; 73 ResetTitleSize(); 74 Invalidate(); 75 } 76 } 77 78 /// <summary> 79 /// The title fore color 80 /// </summary> 81 private Color titleForeColor = Color.Black; 82 /// <summary> 83 /// Gets or sets the color of the title fore. 84 /// </summary> 85 /// <value>The color of the title fore.</value> 86 [Browsable(true)] 87 [Category("自定义")] 88 [Description("获取或设置标题文字颜色")] 89 public Color TitleForeColor 90 { 91 get { return titleForeColor; } 92 set 93 { 94 titleForeColor = value; 95 Invalidate(); 96 } 97 } 98 /// <summary> 99 /// The items 100 /// </summary> 101 private FunelChartItem[] items; 102 /// <summary> 103 /// Gets or sets the items. 104 /// </summary> 105 /// <value>The items.</value> 106 [Browsable(true)] 107 [Category("自定义")] 108 [Description("获取或设置项目")] 109 public FunelChartItem[] Items 110 { 111 get { return items; } 112 set 113 { 114 items = value; 115 Invalidate(); 116 } 117 } 118 119 /// <summary> 120 /// The direction 121 /// </summary> 122 private FunelChartDirection direction = FunelChartDirection.UP; 123 /// <summary> 124 /// Gets or sets the direction. 125 /// </summary> 126 /// <value>The direction.</value> 127 [Browsable(true)] 128 [Category("自定义")] 129 [Description("获取或设置方向")] 130 public FunelChartDirection Direction 131 { 132 get { return direction; } 133 set 134 { 135 direction = value; 136 Invalidate(); 137 } 138 } 139 140 /// <summary> 141 /// The alignment 142 /// </summary> 143 private FunelChartAlignment alignment = FunelChartAlignment.Center; 144 /// <summary> 145 /// Gets or sets the alignment. 146 /// </summary> 147 /// <value>The alignment.</value> 148 [Browsable(true)] 149 [Category("自定义")] 150 [Description("获取或设置对齐方式")] 151 public FunelChartAlignment Alignment 152 { 153 get { return alignment; } 154 set 155 { 156 alignment = value; 157 Invalidate(); 158 } 159 } 160 161 /// <summary> 162 /// The item text align 163 /// </summary> 164 private FunelChartAlignment itemTextAlign = FunelChartAlignment.Center; 165 /// <summary> 166 /// Gets or sets the item text align. 167 /// </summary> 168 /// <value>The item text align.</value> 169 [Browsable(true)] 170 [Category("自定义")] 171 [Description("获取或设置文字位置")] 172 public FunelChartAlignment ItemTextAlign 173 { 174 get { return itemTextAlign; } 175 set 176 { 177 itemTextAlign = value; 178 ResetWorkingRect(); 179 Invalidate(); 180 } 181 } 182 /// <summary> 183 /// The show value 184 /// </summary> 185 private bool showValue = false; 186 /// <summary> 187 /// Gets or sets a value indicating whether [show value]. 188 /// </summary> 189 /// <value><c>true</c> if [show value]; otherwise, <c>false</c>.</value> 190 [Browsable(true)] 191 [Category("自定义")] 192 [Description("获取或设置是否显示值")] 193 public bool ShowValue 194 { 195 get { return showValue; } 196 set 197 { 198 showValue = value; 199 Invalidate(); 200 } 201 } 202 203 204 /// <summary> 205 /// The value format 206 /// </summary> 207 private string valueFormat = "0.##"; 208 /// <summary> 209 /// Gets or sets the value format. 210 /// </summary> 211 /// <value>The value format.</value> 212 [Browsable(true)] 213 [Category("自定义")] 214 [Description("获取或设置值格式化")] 215 public string ValueFormat 216 { 217 get { return valueFormat; } 218 set 219 { 220 valueFormat = value; 221 Invalidate(); 222 } 223 } 224 225 /// <summary> 226 /// The m rect working 227 /// </summary> 228 RectangleF m_rectWorking; 229 /// <summary> 230 /// The m title size 231 /// </summary> 232 SizeF m_titleSize = SizeF.Empty; 233 /// <summary> 234 /// The int split width 235 /// </summary> 236 int intSplitWidth = 1; 237 238 /// <summary> 239 /// Initializes a new instance of the <see cref="UCFunnelChart"/> class. 240 /// </summary> 241 public UCFunnelChart() 242 { 243 this.SetStyle(ControlStyles.AllPaintingInWmPaint, true); 244 this.SetStyle(ControlStyles.DoubleBuffer, true); 245 this.SetStyle(ControlStyles.ResizeRedraw, true); 246 this.SetStyle(ControlStyles.Selectable, true); 247 this.SetStyle(ControlStyles.SupportsTransparentBackColor, true); 248 this.SetStyle(ControlStyles.UserPaint, true); 249 this.FontChanged += UCFunnelChart_FontChanged; 250 Font = new Font("微软雅黑", 8); 251 252 this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; 253 this.SizeChanged += UCFunnelChart_SizeChanged; 254 Size = new System.Drawing.Size(150, 150); 255 items = new FunelChartItem[0]; 256 if (ControlHelper.IsDesignMode()) 257 { 258 items = new FunelChartItem[5]; 259 for (int i = 0; i < 5; i++) 260 { 261 items[i] = new FunelChartItem() 262 { 263 Text = "item" + i, 264 Value = 10 * (i + 1) 265 }; 266 } 267 } 268 } 269 270 /// <summary> 271 /// Handles the FontChanged event of the UCFunnelChart control. 272 /// </summary> 273 /// <param name="sender">The source of the event.</param> 274 /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param> 275 void UCFunnelChart_FontChanged(object sender, EventArgs e) 276 { 277 ResetWorkingRect(); 278 } 279 280 /// <summary> 281 /// Handles the SizeChanged event of the UCFunnelChart control. 282 /// </summary> 283 /// <param name="sender">The source of the event.</param> 284 /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param> 285 void UCFunnelChart_SizeChanged(object sender, EventArgs e) 286 { 287 ResetWorkingRect(); 288 } 289 290 /// <summary> 291 /// Resets the working rect. 292 /// </summary> 293 private void ResetWorkingRect() 294 { 295 if (itemTextAlign == FunelChartAlignment.Center) 296 { 297 m_rectWorking = new RectangleF(0, m_titleSize.Height == 0 ? 0 : (m_titleSize.Height + 10), this.Width, this.Height - (m_titleSize.Height == 0 ? 0 : (m_titleSize.Height + 10))); 298 } 299 else if (itemTextAlign == FunelChartAlignment.Left) 300 { 301 float fltMax = 0; 302 if (items != null && items.Length > 0) 303 { 304 using (Graphics g = this.CreateGraphics()) 305 { 306 fltMax = items.Max(p => g.MeasureString(p.Text, Font).Width); 307 } 308 } 309 m_rectWorking = new RectangleF(fltMax, m_titleSize.Height == 0 ? 0 : (m_titleSize.Height + 10), this.Width - fltMax, this.Height - (m_titleSize.Height == 0 ? 0 : (m_titleSize.Height + 10))); 310 } 311 else 312 { 313 float fltMax = 0; 314 if (items != null && items.Length > 0) 315 { 316 using (Graphics g = this.CreateGraphics()) 317 { 318 fltMax = items.Max(p => g.MeasureString(p.Text, Font).Width); 319 } 320 } 321 m_rectWorking = new RectangleF(0, m_titleSize.Height == 0 ? 0 : (m_titleSize.Height + 10), this.Width - fltMax, this.Height - (m_titleSize.Height == 0 ? 0 : (m_titleSize.Height + 10))); 322 } 323 } 324 325 /// <summary> 326 /// Resets the size of the title. 327 /// </summary> 328 private void ResetTitleSize() 329 { 330 if (string.IsNullOrEmpty(title)) 331 { 332 m_titleSize = SizeF.Empty; 333 } 334 else 335 { 336 using (Graphics g = this.CreateGraphics()) 337 { 338 m_titleSize = g.MeasureString(title, titleFont); 339 m_titleSize.Height += 20; 340 } 341 } 342 ResetWorkingRect(); 343 } 344 345 /// <summary> 346 /// 引发 <see cref="E:System.Windows.Forms.Control.Paint" /> 事件。 347 /// </summary> 348 /// <param name="e">包含事件数据的 <see cref="T:System.Windows.Forms.PaintEventArgs" />。</param> 349 protected override void OnPaint(PaintEventArgs e) 350 { 351 base.OnPaint(e); 352 var g = e.Graphics; 353 g.SetGDIHigh(); 354 355 if (!string.IsNullOrEmpty(title)) 356 { 357 g.DrawString(title, titleFont, new SolidBrush(titleForeColor), new RectangleF(0, 0, this.Width, m_titleSize.Height), new StringFormat() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center }); 358 } 359 360 if (items == null || items.Length <= 0) 361 { 362 g.DrawString("没有数据", Font, new SolidBrush(Color.Black), this.m_rectWorking, new StringFormat() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center }); 363 return; 364 } 365 366 List<FunelChartItem> lstItems; 367 if (direction == FunelChartDirection.UP) 368 { 369 lstItems = items.OrderBy(p => p.Value).ToList(); 370 } 371 else 372 { 373 lstItems = items.OrderByDescending(p => p.Value).ToList(); 374 } 375 376 List<RectangleF> lstRects = new List<RectangleF>(); 377 List<GraphicsPath> lstPaths = new List<GraphicsPath>(); 378 float maxValue = lstItems.Max(p => p.Value); 379 float dblSplitHeight = m_rectWorking.Height / lstItems.Count; 380 for (int i = 0; i < lstItems.Count; i++) 381 { 382 FunelChartItem item = lstItems[i]; 383 if (item.ValueColor == null || item.ValueColor == Color.Empty || item.ValueColor == Color.Transparent) 384 item.ValueColor = ControlHelper.Colors[i]; 385 386 switch (alignment) 387 { 388 case FunelChartAlignment.Left: 389 lstRects.Add(new RectangleF(m_rectWorking.Left, m_rectWorking.Top + dblSplitHeight * i, item.Value / maxValue * m_rectWorking.Width, dblSplitHeight)); 390 break; 391 case FunelChartAlignment.Center: 392 lstRects.Add(new RectangleF(m_rectWorking.Left + (m_rectWorking.Width - (item.Value / maxValue * m_rectWorking.Width)) / 2, m_rectWorking.Top + dblSplitHeight * i, item.Value / maxValue * m_rectWorking.Width, dblSplitHeight)); 393 break; 394 case FunelChartAlignment.Right: 395 lstRects.Add(new RectangleF(m_rectWorking.Right - (item.Value / maxValue * m_rectWorking.Width), m_rectWorking.Top + dblSplitHeight * i, item.Value / maxValue * m_rectWorking.Width, dblSplitHeight)); 396 break; 397 } 398 } 399 400 for (int i = 0; i < lstRects.Count; i++) 401 { 402 var rect = lstRects[i]; 403 GraphicsPath path = new GraphicsPath(); 404 List<PointF> lstPoints = new List<PointF>(); 405 if (direction == FunelChartDirection.UP) 406 { 407 switch (alignment) 408 { 409 case FunelChartAlignment.Left: 410 lstPoints.Add(new PointF(rect.Left, rect.Top)); 411 if (i != 0) 412 { 413 lstPoints.Add(new PointF(lstRects[i - 1].Right, rect.Top)); 414 } 415 break; 416 case FunelChartAlignment.Center: 417 if (i == 0) 418 { 419 lstPoints.Add(new PointF(rect.Left + rect.Width / 2, rect.Top)); 420 } 421 else 422 { 423 lstPoints.Add(new PointF(lstRects[i - 1].Left, rect.Top)); 424 lstPoints.Add(new PointF(lstRects[i - 1].Right, rect.Top)); 425 } 426 break; 427 case FunelChartAlignment.Right: 428 if (i == 0) 429 { 430 lstPoints.Add(new PointF(rect.Right, rect.Top)); 431 } 432 else 433 { 434 lstPoints.Add(new PointF(rect.Right - lstRects[i - 1].Width, rect.Top)); 435 lstPoints.Add(new PointF(rect.Right, rect.Top)); 436 } 437 break; 438 } 439 lstPoints.Add(new PointF(rect.Right, rect.Bottom - intSplitWidth)); 440 lstPoints.Add(new PointF(rect.Left, rect.Bottom - intSplitWidth)); 441 } 442 else 443 { 444 lstPoints.Add(new PointF(rect.Left, rect.Top + intSplitWidth)); 445 lstPoints.Add(new PointF(rect.Right, rect.Top + intSplitWidth)); 446 switch (alignment) 447 { 448 case FunelChartAlignment.Left: 449 if (i == lstRects.Count - 1) 450 { 451 lstPoints.Add(new PointF(rect.Left, rect.Bottom)); 452 } 453 else 454 { 455 lstPoints.Add(new PointF(lstRects[i + 1].Right, rect.Bottom)); 456 lstPoints.Add(new PointF(rect.Left, rect.Bottom)); 457 } 458 break; 459 case FunelChartAlignment.Center: 460 if (i == lstRects.Count - 1) 461 { 462 lstPoints.Add(new PointF(rect.Left + rect.Width / 2, rect.Bottom)); 463 } 464 else 465 { 466 lstPoints.Add(new PointF(lstRects[i + 1].Right, rect.Bottom)); 467 lstPoints.Add(new PointF(lstRects[i + 1].Left, rect.Bottom)); 468 } 469 break; 470 case FunelChartAlignment.Right: 471 if (i == lstRects.Count - 1) 472 { 473 lstPoints.Add(new PointF(rect.Right, rect.Bottom)); 474 } 475 else 476 { 477 lstPoints.Add(new PointF(rect.Right, rect.Bottom)); 478 lstPoints.Add(new PointF(lstRects[i + 1].Left, rect.Bottom)); 479 } 480 break; 481 } 482 } 483 path.AddLines(lstPoints.ToArray()); 484 path.CloseAllFigures(); 485 // g.DrawPath(new Pen(new SolidBrush(lstItems[i].ValueColor.Value)), path); 486 g.FillPath(new SolidBrush(lstItems[i].ValueColor.Value), path); 487 488 //写字 489 if (itemTextAlign == FunelChartAlignment.Center) 490 { 491 g.DrawString(lstItems[i].Text + (ShowValue ? lstItems[i].Value.ToString("\n" + valueFormat) : ""), Font, new SolidBrush((lstItems[i].TextForeColor == null || lstItems[i].TextForeColor == Color.Empty || lstItems[i].TextForeColor == Color.Transparent) ? Color.White : lstItems[i].TextForeColor.Value), rect, new StringFormat() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center }); 492 } 493 else if (itemTextAlign == FunelChartAlignment.Left) 494 { 495 g.DrawString(lstItems[i].Text + (ShowValue ? lstItems[i].Value.ToString("\n" + valueFormat) : ""), Font, new SolidBrush((lstItems[i].TextForeColor == null || lstItems[i].TextForeColor == Color.Empty || lstItems[i].TextForeColor == Color.Transparent) ? lstItems[i].ValueColor.Value : lstItems[i].TextForeColor.Value), new RectangleF(0, rect.Top, rect.Left, rect.Height), new StringFormat() { Alignment = StringAlignment.Far, LineAlignment = StringAlignment.Center }); 496 g.DrawLine(new Pen(new SolidBrush((lstItems[i].TextForeColor == null || lstItems[i].TextForeColor == Color.Empty || lstItems[i].TextForeColor == Color.Transparent) ? lstItems[i].ValueColor.Value : lstItems[i].TextForeColor.Value)), rect.Left, rect.Top + rect.Height / 2, rect.Left + rect.Width / 2, rect.Top + rect.Height / 2); 497 } 498 else 499 { 500 g.DrawString(lstItems[i].Text + (ShowValue ? lstItems[i].Value.ToString("\n" + valueFormat) : ""), Font, new SolidBrush((lstItems[i].TextForeColor == null || lstItems[i].TextForeColor == Color.Empty || lstItems[i].TextForeColor == Color.Transparent) ? lstItems[i].ValueColor.Value : lstItems[i].TextForeColor.Value), new RectangleF(rect.Right, rect.Top, this.Width - rect.Right, rect.Height), new StringFormat() { Alignment = StringAlignment.Near, LineAlignment = StringAlignment.Center }); 501 g.DrawLine(new Pen(new SolidBrush((lstItems[i].TextForeColor == null || lstItems[i].TextForeColor == Color.Empty || lstItems[i].TextForeColor == Color.Transparent) ? lstItems[i].ValueColor.Value : lstItems[i].TextForeColor.Value)), rect.Left + rect.Width / 2, rect.Top + rect.Height / 2, rect.Right, rect.Top + rect.Height / 2); 502 } 503 } 504 } 505 } 506 }
最后的话
如果你喜欢的话,请到 https://gitee.com/kwwwvagaa/net_winform_custom_control 点个星星吧
作者:冰封一夏
出处:http://www.cnblogs.com/bfyx/
HZHControls官网:http://www.hzhcontrols.cn
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,
且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
GitHub:https://github.com/kwwwvagaa/NetWinformControl
码云:https://gitee.com/kwwwvagaa/net_winform_custom_control.git