(一). 概述
本文主要演示一个比较简单的 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 代码
1
namespace
KingControls
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 代码
1
namespace
KingControls
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 的过程. 如果感兴趣, 您可
以试着扩展它, 比如: 先实现把示例代码的 横向柱状图变为 竖向的柱状图; 再自己试着做一个 组合控件, 将多
个本控件整合成一个可以同时显示多列的统计分析图; 更广一点, 再考虑做一个 饼图, 折线图等.
(五). 示例代码下载
http://www.cnblogs.com/Files/MVP33650/SimpleGaugeBarControl.rar
(六). 控件开发其它相关文章:
http://blog.csdn.net/ChengKing/category/288694.aspx