(一). 概述
本文主要演示一个比较简单的 WebChart 柱状实现, 可以一方面了解一个较完整的控件开发实例,
里面用到了复合样式及视图存储等内容. 另一方面了解一下WebChart的实现原理. 在Web开发中, 最终
是用浏览器呈现各种图表, 图表控件呈现过程是 根据控件提供的属性接口接收到数据参数, 用最基本的
Html元素<Table><tr><td>来呈现图表.
注: 本文参考 [Asp.net 2.0高级编程] 方案, 由于本书中配套代码说明链接打不开, 所以在文章最后面可以
下载我上传的自己做得比较完整的示例代码.
(二). 运行效果
1. 在浏览器中运行的柱状图
2. 主要属性 ( 可以通过修改这些属性参数修改其 外观样式和文本显示格式等信息)
主要属性用法介绍:
SimpleGaugeBar 控件重要属性介绍
|
|
FormatString
|
设置显示的文本格式, 如: “68 of 100”
|
Maximum
|
全值大小, 如上图中设置了 100.
|
Segments
|
每段值, 如上图中设置了 10. 这样根据Maximum的值控件就能够算出共有 100/10=10段(全值共有10个td呈现)
|
TextStyle
|
显示文本样式复合属性
|
Value
|
设置有效值, 如上图中设置了 68.
|
ForeColor
|
柱装有效值长度标志颜色(根据Value值决定其长度)
|
BackColor
|
柱装全值标志颜色
|
BorderColor
|
柱状边框颜色
|
(三). 代码
代码比较简单, 就两个文件; 主要代码, 都包含了中文注释. 在这里对代码不作多介绍.
1. 主控件文件 SimpleGaugeBar.cs 代码
2 {
3 /// <summary>
4 /// Author: [ ChengKing(ZhengJian) ]
5 /// Blog: Http://blog.csdn.net/ChengKing
6 /// 本代码 参照 Asp.net 2.0高级编程 方案
7 /// </summary>
8 [DefaultProperty("Value")]
9 [ToolboxData("<{0}:SimpleGaugeBar runat=server></{0}:SimpleGaugeBar>")]
10 [PersistChildrenAttribute(false)]
11 public class SimpleGaugeBar : CompositeControl
12 {
13 //在绘制输出画面时,标志是哪个TD为分界点(从这个分界点改变表格的颜色绘制)
14 private int _intDividerCell;
15
16 private TextItemStyle _textStyle;
17
18 public SimpleGaugeBar()
19 {
20 }
21
22 #region 属性
23 /// <summary>
24 /// 进度条值
25 /// </summary>
26 public float Value
27 {
28 get
29 {
30 object o = ViewState["Value"];
31 if (o == null)
32 return 0;
33 return float.Parse(o.ToString());
34 }
35 set
36 {
37 this.ViewState["Value"] = value;
38 if (value > Maximum)
39 {
40 this.ViewState["Value"] = Maximum;
41 }
42 }
43 }
44
45 /// <summary>
46 /// 全值
47 /// </summary>
48 public float Maximum
49 {
50 get
51 {
52 object o = this.ViewState["Maximum"];
53 if (o == null)
54 {
55 return 100;
56 }
57 return float.Parse(o.ToString());
58 }
59 set
60 {
61 this.ViewState["Maximum"] = value;
62 }
63 }
64
65 /// <summary>
66 /// 表示进度条分几段
67 /// </summary>
68 public int Segments
69 {
70 get
71 {
72 object o = this.ViewState["Segments"];
73 if (o == null)
74 {
75 return 4;
76 }
77 return int.Parse(o.ToString());
78 }
79 set
80 {
81 this.ViewState["Segments"] = value;
82 if (value < 1)
83 {
84 this.ViewState["Segments"] = 1;
85 }
86 }
87 }
88
89 /// <summary>
90 /// 文本呈现格式
91 /// </summary>
92 public string FormatString
93 {
94 get
95 {
96 object o = this.ViewState["FormatString"];
97 if (o == null)
98 {
99 return "<b>{0}</b>/<b>{1}</b>";
100 }
101 return (string)o;
102 }
103 set
104 {
105 this.ViewState["FormatString"] = value;
106 }
107 }
108
109 public bool GridLines
110 {
111 get
112 {
113 object o = this.ViewState["GridLines"];
114 if (o == null)
115 {
116 return true;
117 }
118 return (bool)o;
119 }
120 set
121 {
122 this.ViewState["GridLines"] = value;
123 }
124 }
125
126 [PersistenceMode(PersistenceMode.EncodedInnerDefaultProperty)]
127 [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
128 [NotifyParentProperty(true)]
129 public TextItemStyle TextStyle
130 {
131 get
132 {
133 if (_textStyle == null)
134 {
135 _textStyle = new TextItemStyle();
136 }
137 if (IsTrackingViewState)
138 {
139 ((IStateManager)_textStyle).TrackViewState();
140 }
141 return _textStyle;
142 }
143 }
144
145 #endregion
146
147 #region 方法
148
149 protected override void Render(HtmlTextWriter writer)
150 {
151 PrepareControlForRendering();
152 base.Render(writer);
153
154 //base.RenderContents(writer);
155 //this.RenderContents(writer);
156 //this.Render(writer);
157
158 }
159
160 protected override void CreateChildControls()
161 {
162 //base.CreateChildControls();
163 this.Controls.Clear();
164 CreateControlHierarchy();
165 ClearChildViewState();
166 }
167
168 /// <summary>
169 /// 在Web开发中,用Table/TR/TD来表示图形输出
170 /// </summary>
171 protected virtual void CreateControlHierarchy()
172 {
173 //最外层表格
174 Table outer = new Table();
175 TableRow outerRow = new TableRow();
176 outer.Rows.Add(outerRow);
177
178 TableCell rulerCell = new TableCell();
179 outerRow.Cells.Add(rulerCell);
180 BuildGaugeBar(rulerCell);
181
182 //根据条件增加文本显示单元格
183 TableCell textCell = new TableCell();
184 if (!TextStyle.DisplayTextAtBottom)
185 {
186 outerRow.Cells.Add(textCell);
187 BuildLabel(textCell);
188 }
189
190 this.Controls.Add(outer);
191
192 if (!TextStyle.RenderInsideTable && TextStyle.DisplayTextAtBottom)
193 {
194 BuildLabel(null);
195 }
196 }
197
198 /// <summary>
199 /// 用Label来呈现文本,如: { 8/10 }
200 /// </summary>
201 /// <param name="tc"></param>
202 void BuildLabel(TableCell tc)
203 {
204 float buf = GetValueToRepresent();
205 string msg = GetTextToRepresent();
206
207 Label lbl = new Label();
208 if (tc is TableCell)
209 {
210 tc.Controls.Add(lbl);
211 }
212 else
213 {
214 this.Controls.Add(lbl);
215 }
216 lbl.Text = String.Format(msg, buf, Maximum);
217 }
218
219 void BuildGaugeBar(TableCell tc)
220 {
221 Table t = new Table();
222 TableRow tr = new TableRow();
223 t.Rows.Add(tr);
224
225 BuildRuler(tr);
226
227 if (TextStyle.RenderInsideTable)
228 {
229 BuildLabelIntoTable(t);
230 }
231 tc.Controls.Add(t);
232 }
233
234 void BuildRuler(TableRow ruler)
235 {
236 float val = GetValueToRepresent();
237 float valueToRepresent = 100f * val / Maximum;
238 int numOfSegments = GetNumOfSegments();
239 int segmentWidth = 100 / numOfSegments;
240 bool finished = false;
241 for (int i = 1; i <= numOfSegments; i++)
242 {
243 if (valueToRepresent < i * segmentWidth)
244 {
245 if (finished)
246 {
247 TableCell stillToDo = new TableCell();
248 ruler.Cells.Add(stillToDo);
249 stillToDo.Width = Unit.Percentage(segmentWidth);
250 }
251 else
252 {
253 _intDividerCell = i - 1;
254 TableCell cell = new TableCell();
255 ruler.Cells.Add(cell);
256 cell.Width = Unit.Percentage(segmentWidth);
257 cell.Height = Unit.Percentage(100);
258
259 //增加子表
260 Table child = new Table();
261 child.Width = Unit.Percentage(100);
262 child.Height = Unit.Percentage(100);
263 cell.Controls.Add(child);
264 child.CellPadding = 0;
265 child.CellSpacing = 0;
266 TableRow childRow = new TableRow();
267 child.Rows.Add(childRow);
268
269 float fx = (100 * (valueToRepresent - segmentWidth * (i - 1)) / segmentWidth);
270 if (valueToRepresent > (i - 1) * segmentWidth)
271 {
272 TableCell left = new TableCell();
273 childRow.Cells.Add(left);
274 left.Width = Unit.Percentage(fx);
275 }
276 TableCell right = new TableCell();
277 childRow.Cells.Add(right);
278 right.Width = Unit.Percentage(100 - fx);
279 finished = true;
280 }
281 }
282 else
283 {
284 TableCell done = new TableCell();
285 ruler.Cells.Add(done);
286 done.Width = Unit.Percentage(segmentWidth);
287 }
288 }
289 }
290
291 /// <summary>
292 /// 创建最外Table的第二行, 显示文本
293 /// </summary>
294 /// <param name="t"></param>
295 void BuildLabelIntoTable(Table t)
296 {
297 float buf = GetValueToRepresent();
298 int numOfSegments = GetNumOfSegments();
299 string msg = GetTextToRepresent();
300 if (TextStyle.DisplayTextAtBottom)
301 {
302 TableRow label = new TableRow();
303 t.Rows.Add(label);
304 TableCell lblCell = new TableCell();
305 label.Cells.Add(lblCell);
306
307 lblCell.ColumnSpan = numOfSegments;
308 lblCell.Text = String.Format(msg, buf, Maximum);
309 }
310 }
311
312 private string GetTextToRepresent()
313 {
314 return this.FormatString;
315 }
316
317 private int GetNumOfSegments()
318 {
319 return this.Segments;
320 }
321
322 private float GetValueToRepresent()
323 {
324 return this.Value;
325 }
326
327 /// <summary>
328 /// 增加样式
329 /// </summary>
330 private void PrepareControlForRendering()
331 {
332 if (this.Controls.Count < 1)
333 {
334 return;
335 }
336 Table outer = (Table)Controls[0];
337 outer.CellPadding = 0;
338 outer.CellSpacing = 0;
339 outer.Width = Unit.Percentage(100);
340 outer.Height = Unit.Percentage(100); //this.Height;
341 outer.BorderWidth = Unit.Empty;
342
343 Table t = (Table)outer.Rows[0].Cells[0].Controls[0];
344
345 t.CopyBaseAttributes(this);
346
347 t.CellPadding = 0;
348 t.CellSpacing = 0;
349 t.Width = Unit.Percentage(100);
350 t.Height = Unit.Pixel(17);
351 t.BorderWidth = Unit.Empty;
352
353 for (int i = 0; i < Segments; i++ )
354 {
355 TableCell cell = t.Rows[0].Cells[i];
356 if (GridLines)
357 {
358 cell.BackColor = this.BorderColor;
359 cell.BorderStyle = this.BorderStyle;
360 cell.BorderWidth = this.BorderWidth;
361 }
362
363 //为刻度前面的表格设置颜色
364 if (i < _intDividerCell)
365 {
366 cell.BackColor = this.ForeColor;
367 }
368
369 //为刻度后面的表格设置颜色
370 if (i >= _intDividerCell)
371 {
372 cell.BackColor = this.BackColor;
373 }
374
375 //刻度单元格分两部分设置颜色
376 if (i == _intDividerCell)
377 {
378 Table inner = (Table)cell.Controls[0];
379 if (inner.Rows[0].Cells.Count > 1)
380 {
381 TableRow tr = inner.Rows[0];
382 tr.Cells[0].BackColor = this.ForeColor;
383 tr.Cells[1].BackColor = this.BackColor;
384 }
385 else
386 {
387 inner.Rows[0].Cells[0].BackColor = this.BackColor;
388 }
389 }
390 }
391
392 if (!TextStyle.DisplayTextAtBottom)
393 {
394 outer.Rows[0].Cells[1].ApplyStyle(TextStyle);
395 outer.Rows[0].Cells[1].Width = Unit.Percentage(15);
396 }
397 else if (TextStyle.RenderInsideTable && TextStyle.DisplayTextAtBottom)
398 {
399 TableRow row = t.Rows[1];
400 row.ApplyStyle(TextStyle);
401 }
402 else
403 {
404 Label lbl = (Label)this.Controls[1];
405 lbl.ApplyStyle(TextStyle);
406 }
407 }
408
409 #endregion
410 }
411 }
412
2. 复合样式文件 TextItemStyle.cs 代码
2 {
3 /// <summary>
4 /// Author: [ ChengKing(ZhengJian) ]
5 /// Blog: Http://blog.csdn.net/ChengKing
6 /// 本代码 参照 Asp.net 2.0高级编程 方案
7 /// </summary>
8 /// <summary>
9 /// 定义 SimpleGaugeBar 控件的内部复合属性类
10 /// </summary>
11 public class TextItemStyle : TableItemStyle, IStateManager
12 {
13 #region 类变量
14
15 private bool _renderInsideTable;
16 private bool _displayTextAtBottom;
17
18 #endregion
19
20 #region 构造函数
21
22 public TextItemStyle()
23 {
24 _displayTextAtBottom = true;
25 _renderInsideTable = false;
26 }
27
28 #endregion
29
30 #region 属性
31
32 [NotifyParentProperty(true)]
33 public bool RenderInsideTable
34 {
35 get
36 {
37 return _renderInsideTable;
38 }
39 set
40 {
41 _renderInsideTable = value;
42 }
43 }
44
45 [NotifyParentProperty(true)]
46 public bool DisplayTextAtBottom
47 {
48 get
49 {
50 return _displayTextAtBottom;
51 }
52 set
53 {
54 _displayTextAtBottom = value;
55 }
56 }
57
58 bool IStateManager.IsTrackingViewState
59 {
60 get
61 {
62 return base.IsTrackingViewState;
63 }
64 }
65
66 #endregion
67
68 #region 方法
69 //从当前点开始, 此控件具有保存视图状态功能
70 void IStateManager.TrackViewState()
71 {
72 base.TrackViewState();
73 }
74
75 object IStateManager.SaveViewState()
76 {
77 object[] state = new object[2];
78 state[0] = base.SaveViewState();
79 object[] others = new object[2];
80 others[0] = _renderInsideTable;
81 others[1] = _displayTextAtBottom;
82 state[1] = (object)others;
83
84 //状态管理会存储此返回的值; 另外此方法返回值还有个用途: 创建复合控件时取得各个子控件的视图状态时使用
85 return state;
86 }
87
88 void IStateManager.LoadViewState(object state)
89 {
90 if (state == null)
91 {
92 return;
93 }
94 object[] myState = (object[])state;
95 base.LoadViewState(myState[0]);
96
97 object[] others = (object[])myState[1];
98 _renderInsideTable = (bool)others[0];
99 _displayTextAtBottom = (bool)others[1];
100 }
101
102 #endregion
103 }
104
105 }
(四). 扩展功能
通过看上面代码可以看到, 您可能会想到那些第三方公司开发的 WebChart 控件可以显示各种各样的复杂
图形, 而且不仅仅是柱状图, 还有饼状, 折线图, 甚至是 三维图形.
以上代码仅演示了一个横向的单柱形图的做法. 但已经足够说明实现一个 Chart 的过程. 如果感兴趣, 您可
以试着扩展它, 比如: 先实现把示例代码的 横向柱状图变为 竖向的柱状图; 再自己试着做一个 组合控件, 将多
个本控件整合成一个可以同时显示多列的统计分析图; 更广一点, 再考虑做一个 饼图, 折线图等.
(五). 示例代码下载
https://files.cnblogs.com/MVP33650/SimpleGaugeBarControl.rar
(六). 控件开发其它相关文章:
http://blog.csdn.net/ChengKing/category/288694.aspx