入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章。
GitHub:https://github.com/kwwwvagaa/NetWinformControl
码云:https://gitee.com/kwwwvagaa/net_winform_custom_control.git
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 点个星星吧