使用.NET一年多了,前不久开始迷上了.NET的控件开发,发觉这真是一个表现创造力的工作:)。
前不久应公司要求写一个在公司网站全站通用的留言本,因为公司旗下网站(www.163888.net)内容比较庞杂,而且时常会出现各种活动,留言本的使用频率非常高,而原先的方法是每次活动都从新开发一个留言本,实在是非常不科学的方式,而实际上每个留言本的逻辑处理基本上是一致的,因此才有了开发一个全站通用的留言本的想法。
任务到手之后,思考实现时,第一个想到了动态调用.ascx文件的方式来写一个复合控件,但是经过再三考虑之后,发觉这个方法存在不少问题。
虽然它达到了同一逻辑结构,灵活换肤的需求,但是正如我先前所说留言本在本站使用频率很高,这样每次使用都会产生1-2个.ascx文件,感觉上有点累赘。
经过再三考虑,无意中想到了Repeater这个控件,它使用ItemTemplate和AlternatingItemTemplate两个属性灵活包含模板而不产生.ascx文件,这不正是我所需要的吗?我何不写一个控件内部绑定数据的类似Repeater的控件?
有了这个想法之后,立刻使用Reflector打开了System.Web.dll找到了Repeater控件,参考着完成了下面的GuestBook控件。
1
using
System;
2
using
System.Web.UI;
3
using
System.Web.UI.WebControls;
4
using
System.ComponentModel;
5
using
System.Collections;
6
using
SunBird.Controls;
7
using
SunBird.Components;
8

9
namespace
SunBird.GuestBookControl
10

{
11
[ParseChildren(true), PersistChildren(false)]
12
public class GuestBook : Control , INamingContainer
13
{
14
ITemplate itemTemplate;
15
[DefaultValue((string)null), PersistenceMode(PersistenceMode.InnerProperty), TemplateContainer(typeof(GuestBookItem)), Browsable(false)]
16
public ITemplate ItemTemplate
17
{
18
get
{ return itemTemplate; }
19
set
{ itemTemplate = value; }
20
}
21
22
ITemplate alternatingItemTemplate;
23
[DefaultValue((string)null), PersistenceMode(PersistenceMode.InnerProperty), TemplateContainer(typeof(GuestBookItem)), Browsable(false)]
24
public ITemplate AlternatingItemTemplate
25
{
26
get
{ return alternatingItemTemplate; }
27
set
{ alternatingItemTemplate = value; }
28
}
29
30
ITemplate pagerTemplate;
31
[DefaultValue((string)null), PersistenceMode(PersistenceMode.InnerProperty), TemplateContainer(typeof(GuestBookItem)), Browsable(false)]
32
public ITemplate PagerTemplate
33
{
34
get
{ return pagerTemplate; }
35
set
{ pagerTemplate = value; }
36
}
37
38
object dataSource;
39
[Browsable(false)]
40
public object DataSource
41
{
42
get
{ return dataSource; }
43
set
{ dataSource = value; }
44
}
45
46
int type;
47
public int Type
48
{
49
get
{ return type; }
50
set
{ type = value; }
51
}
52
53
int markup;
54
public int Markup
55
{
56
get
{ return markup; }
57
set
{ markup = value; }
58
}
59
60
int top;
61
public int Top
62
{
63
get
{ return top; }
64
set
{ top = value; }
65
}
66
67
int pageSize;
68
public int PageSize
69
{
70
set
{ pageSize = value; }
71
get
{ return pageSize <= 0 ? 10 : pageSize;}
72
}
73
74
int superUser;
75
public int SuperUser
76
{
77
get
78
{
79
if (superUser <= 0)
80
return -1;
81
return superUser;
82
}
83
set
{ superUser = value; }
84
}
85
86
87
protected override void OnLoad(EventArgs e)
88
{
89
base.OnLoad (e);
90
if (!Page.IsPostBack)
91
{
92
DataBind();
93
}
94
}
95
96
DataBind#region DataBind
97
int totalRecords;
98
public override void DataBind()
99
{
100
if (type == 0)
{
101
throw new ArgumentException("为指定读取类型(Type)。");
102
}
103
if (markup == 0)
{
104
throw new ArgumentException("未指定读取标识(Markup)。");
105
}
106
107
int pageIndex = Globals.SafeInt(UContext.Current.QueryString["PageIndex"], 1);
108
109
ArrayList posts = GuestBookDataProvider.Instance().GetPosts(type, markup, top, pageIndex, PageSize, out totalRecords);
110
DataSource = posts;
111
112
base.DataBind ();
113
}
114
#endregion
115
116
OnDataBinding#region OnDataBinding
117
protected override void OnDataBinding(EventArgs e)
118
{
119
CreateDataItemTemplate();
120
CreatePagerTemplate();
121
base.OnDataBinding(e);
122
}
123
#endregion
124
125
CreatePagerTemplate#region CreatePagerTemplate
126
private void CreatePagerTemplate()
127
{
128
if (pagerTemplate != null && dataSource != null && top == 0)
129
{
130
GuestBookItem item = new GuestBookItem(-1, GuestBookItemType.Pager);
131
pagerTemplate.InstantiateIn(item);
132
Pager p = new Pager();
133
p.PageSize = PageSize;
134
p.TotalRecords = totalRecords;
135
136
item.Controls.Add(p);
137
138
Controls.Add(item);
139
}
140
}
141
#endregion
142
143
CreateDataItemTemplate#region CreateDataItemTemplate
144
private void CreateDataItemTemplate()
145
{
146
if (itemTemplate != null)
147
{
148
if (dataSource != null)
149
{
150
int itemCount = 1;
151
152
foreach(object data in (ArrayList)dataSource)
153
{
154
ITemplate template = itemTemplate;
155
GuestBookItemType itemType = GuestBookItemType.Item;
156
if(alternatingItemTemplate != null)
157
{
158
if (itemCount % 2 > 0)
159
{
160
template = alternatingItemTemplate;
161
itemType = GuestBookItemType.AlternatingItem;
162
}
163
}
164
GuestBookItem item = new GuestBookItem(itemCount, itemType);
165
166
item.DataItem = data;
167
168
template.InstantiateIn(item);
169
170
GuestBook_ItemDataBound(item);
171
172
Controls.Add(item);
173
174
itemCount ++;
175
}
176
177
ViewState["ItemCount"] = itemCount;
178
}
179
180
ChildControlsCreated = true;
181
}
182
}
183
#endregion
184
185
GuestBook_ItemDataBound#region GuestBook_ItemDataBound
186
private void GuestBook_ItemDataBound(GuestBookItem item)
187
{
188
GBPost post = item.DataItem as GBPost;
189
if (post != null)
190
{
191
Image GBUserHead = item.FindControl("GBUserHead") as Image;
192
if (GBUserHead != null)
193
{
194
if (!Globals.IsNullorEmpty(post.UserHead))
195
GBUserHead.ImageUrl = post.UserHead;
196
else
197
GBUserHead.Visible = false;
198
}
199
200
HyperLink GBUser = item.FindControl("GBUser") as HyperLink;
201
if (GBUser != null)
202
{
203
GBUser.Text = post.UserName;
204
GBUser.NavigateUrl = "http://" + post.UserID + ".163888.net/";
205
GBUser.Target = "_blank";
206
}
207
208
Literal GBBody = item.FindControl("GBBody") as Literal;
209
if (GBBody != null)
210
{
211
GBBody.Text = post.Body;
212
Literal GBReplayBody = item.FindControl("GBReplayBody") as Literal;
213
if (GBReplayBody != null)
214
{
215
if (!Globals.IsNullorEmpty(post.ReplyBody))
216
{
217
GBReplayBody.Visible = true;
218
GBReplayBody.Text = "<font color=\"#808080\">回复</font>:" + post.ReplyBody;
219
}
220
else
221
{
222
GBReplayBody.Visible = false;
223
}
224
225
}
226
}
227
228
User user = UContext.Current.User;
229
230
LinkButton GBDelPost = item.FindControl("GBDelPost") as LinkButton;
231
if (GBDelPost != null)
232
{
233
if (user.UserID == SuperUser)
234
{
235
GBDelPost.Visible = true;
236
GBDelPost.CausesValidation = false;
237
GBDelPost.Command += new CommandEventHandler(GBDelPost_Command);
238
GBDelPost.CommandName = "DelPost";
239
GBDelPost.CommandArgument = post.ID.ToString();
240
}
241
else
242
{
243
GBDelPost.Visible = false;
244
}
245
}
246
247
HyperLink GBReplayPost = item.FindControl("GBReplayPost") as HyperLink;
248
if (GBReplayPost != null)
249
{
250
if (user.UserID == SuperUser)
251
{
252
string script = "<script language=\"javascript\">" + Environment.NewLine
253
+ "function openWindow(pagePath, args, width, height) {" + Environment.NewLine
254
+ " var s = '';" + Environment.NewLine
255
+ " s = 'width=' + width + ',height=' + height + ',';" + Environment.NewLine
256
+ " var win = window.open(pagePath, 'guestbook', s + 'resizable=no,toolbar=no,scrollbars=no,status=no,alwaysLowered=yes,');" + Environment.NewLine
257
+ " win.focus();" + Environment.NewLine
258
+ " win.moveTo( (screen.width - width)/2, (screen.height - height)/2 );}</script>" + Environment.NewLine;
259
GBReplayPost.Visible = true;
260
Page.RegisterClientScriptBlock("guestBook_openWindow", script);
261
GBReplayPost.NavigateUrl = "javascript:openWindow('/GuestBook/GuestBook_Replay.aspx?rid=" + post.ID + "', '', 350, 150);";
262
}
263
else
264
{
265
GBReplayPost.Visible = false;
266
}
267
}
268
269
Literal GBDate = item.FindControl("GBDate") as Literal;
270
if (GBDate != null)
271
{
272
GBDate.Text = post.DateCreated.ToShortDateString();
273
}
274
275
}
276
}
277
#endregion
278
279
GBDelPost_Command#region GBDelPost_Command
280
private void GBDelPost_Command(object sender, CommandEventArgs e)
281
{
282
string cmd = e.CommandName;
283
int postID = int.Parse(e.CommandArgument as string);
284
switch(cmd)
285
{
286
case "DelPost":
287
GuestBookDataProvider.Instance().DeletePost(postID);
288
break;
289
}
290
}
291
#endregion
292
}
293
}
如果使用这样一个控件,在页面注册之后只需要使用它的ItemTemplate和AlternatingItemTemplate属性就可以加载模板了,比原先设想的动态加载.ascx文件的方式要好得多,如:
<%
@ Register TagPrefix="GB" Namespace="SunBird.GuestBookControl" Assembly="SunBird.GuestBook"
%>

<
GB:GuestBook
id
="guestBook"
runat
="server"
Type
="0"
Markup
="0"
>

<
ItemTemplate
>
模板代码
</
ItemTemplate
>
<
AlternatingItemTemplate
>
奇数行模板代码
</
AlternatingItemTemplate
>

<
PagerTemplate
/>
<!--
分页
-->
</
GB:GuestBook
>
这样就完成了该控件的定义,PagerTemplate属性是分页显示。
下面是留言本信息的发布,发不分两种:1、需要登录,2、不需要登录。
于是分别写了三个控件类:
1、GuestBookCreateBase (基类) 它定义了大部分的功能。
1
using
System;
2
using
System.Web.UI.WebControls;
3
using
SunBird.Components;
4

5
namespace
SunBird.GuestBookControl
6

{
7
public class GuestBookCreateBase : GuestBookSkinBase
8
{
9
public override void DataBind()
10
{
11
base.DataBind();
12
}
13
14
protected override void OnLoad(EventArgs e)
15
{
16
base.OnLoad(e);
17
if (!Page.IsPostBack)
18
{
19
DataBind();
20
}
21
}
22
23
AttachChildCotrols#region AttachChildCotrols
24
protected TextBox Content;
25
protected Button CreateButton;
26
protected ImageButton CreateImageButton;
27
protected override void AttachChildControls()
28
{
29
Content = (TextBox) FindControl("Content");
30
CreateButton = FindControl("CreateButton") as Button;
31
if (CreateButton == null)
32
{
33
throw new Exception("缺少必要的请求控件(Button)。");
34
}
35
CreateButton.Click += new EventHandler(CreateButton_Click);
36
}
37
#endregion
38
39
CreateButton_Click#region CreateButton_Click
40
protected virtual void CreateButton_Click(object sender, EventArgs e)
41
{
42
if (Globals.IsNullorEmpty(Content.Text))
43
{
44
Globals.OutMessage("请输入评论内容。");
45
return;
46
}
47
48
User user = UContext.Current.User;
49
50
GBPost post = new GBPost();
51
post.UserID = user.UserID;
52
post.UserName = user.UserName;
53
post.Type = Type;
54
post.Markup = Markup;
55
post.Body = HtmlScrubber.Clean(Content.Text, false, true);
56
post.DateCreated = DateTime.Now;
57
58
GuestBookDataProvider.Instance().CreatePost(post);
59
60
Uri uri = UContext.Current.CurrentUri;
61
if (uri != null)
62
{
63
Page.Response.Redirect(uri.ToString());
64
}
65
}
66
#endregion
67
68
int type;
69
public virtual int Type
70
{
71
get
{ return type; }
72
set
{ type = value; }
73
}
74
75
int markup;
76
public virtual int Markup
77
{
78
get
{ return markup; }
79
set
{ markup = value; }
80
}
81
}
82
}
2、CreatePostNeedLogin : GuestBookCreateBase 需要登录,他实际上只是override了基类的CreateButton_Click方法,为该方法加入了验证登录的操作。
1
using
System;
2
using
System.Web.UI.WebControls;
3
using
SunBird.Components;
4

5
namespace
SunBird.GuestBookControl
6

{
7
public class CreatePostNeedLogin : GuestBookCreateBase
8
{
9
protected TextBox UserName;
10
protected TextBox UserPassword;
11
protected override void AttachChildControls()
12
{
13
base.AttachChildControls();
14
15
UserName = (TextBox)FindControl("UserName");
16
UserPassword = (TextBox)FindControl("UserPassword");
17
}
18
19
protected override void CreateButton_Click(object sender, EventArgs e)
20
{
21
User user = UContext.Current.User;
22
if (user.IsAnonymous)
23
{
24
if (Globals.IsNullorEmpty(UserName.Text))
25
{
26
Globals.OutMessage("请输入用户名。");
27
return;
28
}
29
if (Globals.IsNullorEmpty(UserPassword.Text))
30
{
31
Globals.OutMessage("请输入密码。");
32
return;
33
}
34
35
int userId;
36
string name = UserName.Text;
37
string password = NewBase.String.GetMd5(UserPassword.Text);
38
bool result = GuestBookDataProvider.Instance().ValidateUser(name, password, out userId);
39
if (result)
40
{
41
user.UserID = userId;
42
user.UserName = name;
43
Users.SaveUserCookie(user);
44
}
45
else
46
{
47
Globals.OutMessage("用户名或密码错误。");
48
return;
49
}
50
}
51
52
53
base.CreateButton_Click (sender, e);
54
}
55
56
}
57
}
3、CreatePostNotNeedLogin : GuestBookCreateBase 不需要登录,实际上该类没有做任何操作仅仅只是为了方便其他人使用。
1
using
System;
2
using
System.Web.UI.WebControls;
3

4
namespace
SunBird.GuestBookControl
5

{
6
public class CreatePostNotNeedLogin : GuestBookCreateBase
7
{
8
protected override void AttachChildControls()
9
{
10
base.AttachChildControls();
11
12
13
}
14
}
15
}
控件的核心的类差不多就这几个,完整的代码如果有兴趣,请下载附件查看。
终于找到上传附件的地方了,下面是源代码,有兴趣的朋友可以下载看看,如果你有更好的方法请一定告诉我。
GuestBookControl源代码