闲来无事,学习了下红皮书《ASP.NET组件工具包》中第1章“微调控件”,将其VB.NET代码改写为C#代码,并作了较详细的注释,实现了一个类似WinForm中NumericUpDown的微调控件。先总结一下开发中的问题和经验......
闲来无事,学习了下红皮书《ASP.NET组件工具包》中第1章“微调控件”,将其VB.NET代码改写为C#代码,并作了较详细的注释,实现了一个类似WinForm中NumericUpDown的微调控件。
效果图如右:
先总结一下开发中的问题和经验:
1.注意:将控件项目的“属性”-->“通用属性”-->“常规”中的“默认命名空间”设为空,否则总出现错误:“System.ArgumentException: 已存在具有相同键的条目。”
哪位高手知道原因的话请赐教。???
2.注意:将.js文件和.bmp文件作为嵌入资源进行编译。方法:在“解决方案资源管理器”中选中文件,右键“属性”,在属性框中“生成操作”项中选择“嵌入的资源”,随后编译该项目即可。
3.注意:.bmp文件是作为控件的自定义图标显示在工具箱中的(如果你把控件添加到工具箱中的话),该文件要求必须与控件同名(如本程序中的命名为:Xct.WebControls.Spinner.bmp),且带.bmp后缀。
4.将控件添加到工具箱中的方法:VS2003-IDE菜单“工具”-->“添加/移除工具箱项...”,在“.NET Framework组件”选项卡中点击“浏览”,选择控件对应的dll文件后点击“确定”。在工具箱的“常规”选项卡中就出现新添的控件,如图
。
5.优点:该控件在编译时将JS脚本资源嵌入到了程序集dll文件中,使得在布暑应用时不需要再拷贝js文件。
6.问题:该控件的属性“TextAlign - 文本对齐方式”不起作用,不知怎么回事。???
运行环境:Win2003Server + IIS6 + VS2003 + .NET Framework 1.1 + IE6
附:《ASP.NET组件工具包》书中的代码可以在http://support.apress.com/books.asp?bID=1861008023&s=0下载.(注:在www.wrox.com找不到其源码下载)
===============================================================================
控件程序代码(Spinner.cs):----
1using System;
2using System.Web.UI;
3using System.Web.UI.WebControls;
4using System.ComponentModel;
5using System.IO;
6
7
8//指定控件的标记前缀
9[assembly: TagPrefix("Xct.WebControls","xct")]
10namespace Xct.WebControls
11{
12 枚举类型#region 枚举类型
13 /**//// <summary>
14 /// 滚动箭头的位置,可以在文本框左侧或右侧
15 /// </summary>
16 public enum SpinnerAlign
17 {
18 Left = 0,
19 Right = 1
20 }
21
22 /**//// <summary>
23 /// 文本框中的文本对齐方式
24 /// </summary>
25 public enum ValueAlign
26 {
27 Center = 0,
28 Left = 1,
29 Right = 2
30 }
31 #endregion
32
33 /**//// <summary>
34 /// ** 微调控件V1.0 ** (夏春涛 2007-09-26) **
35 /// </summary>
36
37 [ToolboxData("<{0}:Spinner runat=\"server\" width=\"80px\" buttonsize=\"XX-Small\" />")]
38 public class Spinner : WebControl, INamingContainer
39 {
40 脚本资源变量#region 脚本资源变量
41 //Scripts变量用于存储脚本资源
42 private static string Scripts;
43 //StartupScriptFormat变量用于存储初始化微调控件的脚本资源
44 private static string StartupScriptFormat = "<script language=\"JavaScript\">t = document.getElementById(\"{0}\"); t.step = {1}; t.original = {2}; t.max = {3}; t.min = {4};</script>";
45 #endregion
46
47 静态构造函数#region 静态构造函数
48 /**//// <summary>
49 /// 静态构造函数,从程序集中提取脚本资源字符串,并将之存入Scripts变量
50 /// </summary>
51 static Spinner()
52 {
53 //获取当前代码正从中运行的 System.Reflection.Assembly
54 System.Reflection.Assembly asm = System.Reflection.Assembly.GetExecutingAssembly();
55
56 if (asm != null)
57 {
58 string resource = "SpinnerLib.js";
59 Stream stm = asm.GetManifestResourceStream(resource);//从程序集加载指定的清单资源
60 if (stm==null) return;
61
62 try
63 {
64 StreamReader reader = new StreamReader(stm);
65 Scripts = reader.ReadToEnd();
66 reader.Close();
67 }
68 finally
69 {
70 if (stm != null) stm.Close();
71 }
72
73 }
74 }
75 #endregion
76
77 属性定义#region 属性定义
78
79 Increment - 步长#region Increment - 步长
80 private int _inc = 1;
81
82 [Bindable(true),Category("Data"),DefaultValue(1),Description("步长")]
83 public int Increment
84 {
85 get
86 {
87 return _inc;
88 }
89
90 set
91 {
92 _inc = value;
93 }
94 }
95 #endregion
96
97 Value - 值#region Value - 值
98 private int _value = 0;
99
100 [Bindable(true),Category("Appearance"),DefaultValue(0),Description("值")]
101 public int Value
102 {
103 get
104 {
105 return _value;
106 }
107
108 set
109 {
110 _value = value;
111 base.ChildControlsCreated = false;
112 }
113 }
114 #endregion
115
116 Maximum - 最大值#region Maximum - 最大值
117 private int _max = 100;
118
119 [Bindable(true),Category("Data"),DefaultValue(100),Description("最大值")]
120 public int Maximum
121 {
122 get
123 {
124 return _max;
125 }
126
127 set
128 {
129 _max = value;
130 }
131 }
132 #endregion
133
134 Minimum - 最小值#region Minimum - 最小值
135 private int _min = 0;
136
137 [Bindable(true),Category("Data"),DefaultValue(0),Description("最小值")]
138 public int Minimum
139 {
140 get
141 {
142 return _min;
143 }
144
145 set
146 {
147 _min = value;
148 }
149 }
150 #endregion
151
152 ButtonAlign - 按钮位置#region ButtonAlign - 按钮位置
153 private SpinnerAlign _align = SpinnerAlign.Right;
154
155 [Bindable(true),Category("Layout"),DefaultValue(SpinnerAlign.Right),Description("按钮位置")]
156 public SpinnerAlign ButtonAlign
157 {
158 get
159 {
160 return _align;
161 }
162
163 set
164 {
165 if (!Enum.IsDefined(Type.GetType("Xct.WebControls.SpinnerAlign"),value))
166 throw new ArgumentException();
167
168 //将基类的ChildControlsCreated设为false,为此需要重写ChildControlsCreated方法,
169 //该方法用于构建组成界面的子控件的层次结构
170 base.ChildControlsCreated = false;
171 _align = value;
172 }
173 }
174 #endregion
175
176 TextAlign - 文本对齐方式#region TextAlign - 文本对齐方式
177 private ValueAlign _textalign = ValueAlign.Right;
178
179 [Bindable(true),Category("Layout"),DefaultValue(ValueAlign.Right),Description("文本对齐方式")]
180 public ValueAlign TextAlign
181 {
182 get
183 {
184 return _textalign;
185 }
186
187 set
188 {
189 if (!Enum.IsDefined(Type.GetType("Xct.WebControls.ValueAlign"),value))
190 throw new ArgumentException();
191
192 //将基类的ChildControlsCreated设为false,为此需要重写ChildControlsCreated方法,
193 //该方法用于构建组成界面的子控件的层次结构
194 base.ChildControlsCreated = false;
195 _textalign = value;
196 }
197 }
198 #endregion
199
200 ButtonSize - 按钮字体大小#region ButtonSize - 按钮字体大小
201 private FontUnit _size;
202
203 [Bindable(true),Category("Appearance"),Description("按钮字体大小")]
204 public FontUnit ButtonSize
205 {
206 get
207 {
208 return _size;
209 }
210
211 set
212 {
213 base.ChildControlsCreated = false;
214 _size = value;
215 }
216 }
217 #endregion
218
219 #endregion
220
221 重载ChildControlsCreated方法#region 重载ChildControlsCreated方法
222 //构建组成界面的子控件的层次结构,以便为回发或呈现做准备
223 //当开发复合服务器控件或模板服务器控件时,必须重写此方法
224 protected override void CreateChildControls()
225 {
226 创建表格#region 创建表格
227 Table tb = new Table();
228 TableCell tc;
229
230 tb.CellPadding = 0;
231 tb.CellSpacing = 0;
232
233 tb.Rows.Add(new TableRow());
234 tb.Rows.Add(new TableRow());
235 tb.Rows[0].Cells.Add(new TableCell());
236 tb.Rows[0].Cells.Add(new TableCell());
237 tb.Rows[1].Cells.Add(new TableCell());
238
239 base.Controls.Clear();//清除基类中的控件
240 base.Controls.Add(tb);//添加新创建的表格
241 #endregion
242
243 创建文本框#region 创建文本框
244 TextBox txt = new TextBox();
245 txt.ID = "txtValue";
246 //通过Attributes集合把客户端JS事件处理程序添加到服务器控件
247 txt.Attributes.Add("OnKeyPress","return KeyPressed(event,this)");
248 txt.Attributes.Add("OnKeyUp", "return KeyUp(this)");
249 txt.Attributes.Add("OnChange", "return SpinnerChanged(this)");
250 txt.Attributes.Add("OnPaste", "return SpinnerChanged(this)");
251 //应用样式
252 txt.ApplyStyle(this.ControlStyle);
253 //移除文本框的Border(用表格单元格Border代替)
254 txt.Style.Add("Border-Top", "none");
255 txt.Style.Add("Border-Right", "none");
256 txt.Style.Add("Border-Left", "none");
257 txt.Style.Add("Border-Bottom","none");
258 txt.Width = new Unit("100%"); //文本框的宽度
259 txt.Text = this.Value.ToString(); //文本框的值
260
261
262 //设置文本框所在的单元格的样式
263 if (this.ButtonAlign == SpinnerAlign.Left)
264 tc = tb.Rows[0].Cells[1];
265 else
266 tc = tb.Rows[0].Cells[0];
267
268 tc.Style.Add("vertical-align","middle");
269
270 tc.ApplyStyle(this.ControlStyle);
271
272 tc.Style.Add("Border-Top", "2px inset");
273 tc.Style.Add("Border-Right", "silver 1px solid");
274 tc.Style.Add("Border-Left", "2px inset");
275 tc.Style.Add("Border-Bottom","silver 1px solid");
276 tc.Controls.Add(txt);
277 tc.RowSpan = 2;//使文本框跨越Up/Down按钮两行
278 tc.Width = new Unit("100%");
279 #endregion
280
281 Up按钮#region Up按钮
282 //判断客户端浏览器是不是IE
283 bool isIE = true;
284 try
285 {
286 isIE = Context.Request.Browser.VBScript;
287 }
288 catch
289 {
290 //null
291 }
292
293 //Up按钮的单元格
294 if (this.ButtonAlign == SpinnerAlign.Left)
295 tc = tb.Rows[0].Cells[0];
296 else
297 tc = tb.Rows[0].Cells[1];
298
299 //Up按钮
300 tc.BackColor = System.Drawing.Color.Gainsboro;
301 tc.BorderStyle = (isIE==true ? BorderStyle.Outset : BorderStyle.Solid);
302 tc.BorderWidth = new Unit("1px");
303 tc.Style.Add("cursor", "pointer");
304 tc.Font.Size = _size;
305
306 tc.Attributes.Add("OnMouseUp", "this.style.backgroundColor='Gainsboro';" + (isIE==true ? "this.style.borderStyle='outset';" : ""));
307 tc.Attributes.Add("OnMouseDown", "this.style.backgroundColor='WhiteSmoke';" + (isIE==true ? "this.style.borderStyle='inset';" : ""));
308 tc.Attributes.Add("OnMouseOut", "this.style.backgroundColor='Gainsboro';" + (isIE==true ? "this.style.borderStyle='outset';" : ""));
309 tc.Attributes.Add("OnClick", "Increment('" + txt.ClientID + "');");
310 tc.Text = "▲";//字符"▲".
311 #endregion
312
313 Down按钮#region Down按钮
314 tc = tb.Rows[1].Cells[0];
315 tc.BackColor = System.Drawing.Color.Gainsboro;
316 tc.BorderStyle = (isIE==true ? BorderStyle.Outset : BorderStyle.Solid);
317 tc.BorderWidth = new Unit("1px");
318 tc.Style.Add("cursor", "pointer");
319 tc.Font.Size = _size;
320
321 tc.Attributes.Add("OnMouseUp", "this.style.backgroundColor='Gainsboro';" + (isIE==true ? "this.style.borderStyle='outset';" : ""));
322 tc.Attributes.Add("OnMouseDown", "this.style.backgroundColor='WhiteSmoke';" + (isIE==true ? "this.style.borderStyle='inset';" : ""));
323 tc.Attributes.Add("OnMouseOut", "this.style.backgroundColor='Gainsboro';" + (isIE==true ? "this.style.borderStyle='outset';" : ""));
324 tc.Attributes.Add("OnClick", "Decrement('" + txt.ClientID + "');");
325
326 tc.Text = "▼";//字符"▼"
327 #endregion
328
329 }
330
331 #endregion
332
333 重载AddAttributesToRender#region 重载AddAttributesToRender
334 //将需要呈现的 HTML 属性和样式添加到指定的 HtmlTextWriterTag 中。此方法主要由控件开发人员使用
335 protected override void AddAttributesToRender(HtmlTextWriter writer)
336 {
337 //请注意TextBox控件的宽度被指定为100%,所以如果省略下步控件的宽度会覆盖整个页面
338 writer.AddStyleAttribute(HtmlTextWriterStyle.Width,this.Width.ToString());//默认宽度80px
339 }
340 #endregion
341
342 重载OnPreRender#region 重载OnPreRender
343 //此方法通知服务器控件在保存视图状态和呈现内容之前,执行任何必要的预呈现步骤
344 protected override void OnPreRender(EventArgs e)
345 {
346 EnsureChildControls();//确定服务器控件是否包含子控件。如果不包含,则创建子控件。
347 base.OnPreRender (e);
348 }
349 #endregion
350
351 重载Render#region 重载Render
352 //将服务器控件内容发送到提供的 HtmlTextWriter 对象,此对象编写将在客户端呈现的内容
353 //在开发自定义服务器控件时,可以重写此方法以生成 ASP.NET 页的内容
354 protected override void Render(HtmlTextWriter writer)
355 {
356 EnsureChildControls();//确定服务器控件是否包含子控件。如果不包含,则创建子控件。
357 base.Render(writer);
358 }
359 #endregion
360
361 重载OnLoad,向客户端发送JS脚本资源#region 重载OnLoad,向客户端发送JS脚本资源
362 protected override void OnLoad(EventArgs e)
363 {
364 EnsureChildControls();//确定服务器控件是否包含子控件。如果不包含,则创建子控件。
365
366
367 //注册脚本库
368 if (!Page.IsClientScriptBlockRegistered(this.GetType().FullName))
369 {
370 Page.RegisterClientScriptBlock(this.GetType().FullName, Scripts);
371 }
372
373 Page.RegisterStartupScript(this.ClientID, String.Format( StartupScriptFormat,
374 this.FindControl("txtValue").ClientID,
375 this.Increment,
376 this.Value,
377 this.Maximum,
378 this.Minimum
379 )
380 );
381
382 base.OnLoad (e);
383 }
384 #endregion
385
386
387 }
388}
389
Javascript脚本源码(SpinnerLib.js, 这是原书附带的代码):----
1<script language="JavaScript">
2<!--
3 function IsChar(code)
4 {
5 return /\w/.test(String.fromCharCode(code));
6 }
7 function KeyPressed(e, src)
8 {
9 var code;
10
11 if (e.which == undefined)
12 {
13 //IE
14 code = e.keyCode;
15 }
16 else
17 {
18 //Mozilla/NS6+
19 code = e.which;
20 }
21
22 // It's not a character value, leave the function.
23 if (!IsChar(code)) return true;
24
25 return !isNaN(String.fromCharCode(code));
26 }
27 function KeyUp(src)
28 {
29 // If we have a valid value, we save it for later,
30 // else we restore the previously saved value.
31 if (src.value <= src.max && src.value >= src.min)
32 {
33 src.original = src.value;
34 return true;
35 }
36 else
37 {
38 // Exceptional case: the user is entering a negative number.
39 if (src.value == "-") return true;
40
41 src.value = src.original;
42 return false;
43 }
44 }
45 function SpinnerChanged(src)
46 {
47 // If we have a valid value, we save it for later,
48 // else we restore the previously saved value.
49 if (src.value <= src.max && src.value >= src.min)
50 {
51 src.original = src.value;
52 return true;
53 }
54 else
55 {
56 src.value = src.original;
57 return false;
58 }
59 }
60
61 function Decrement(target)
62 {
63 src = document.getElementById(target);
64 src.value = parseInt(src.value) - parseInt(src.step);
65 SpinnerChanged(src);
66 src.focus();
67 }
68
69 function Increment(target)
70 {
71 src = document.getElementById(target);
72 src.value = parseInt(src.value) + parseInt(src.step);
73 SpinnerChanged(src);
74 src.focus();
75 }
76//-->
77</script>
78
79
完整程序文件下载:----/Files/bluesky521/Spinner_OK200709271300.rar
---- 作者:夏春涛 Email:xchuntao@163.com QQ:23106676