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

{11
[ParseChildren(true), PersistChildren(false)]12
public class GuestBook : Control , INamingContainer13

{14
ITemplate itemTemplate;15
[DefaultValue((string)null), PersistenceMode(PersistenceMode.InnerProperty), TemplateContainer(typeof(GuestBookItem)), Browsable(false)]16
public ITemplate ItemTemplate17

{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 AlternatingItemTemplate25

{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 PagerTemplate33

{34

get
{ return pagerTemplate; }35

set
{ pagerTemplate = value; }36
}37

38
object dataSource;39
[Browsable(false)]40
public object DataSource41

{42

get
{ return dataSource; }43

set
{ dataSource = value; }44
}45

46
int type;47
public int Type48

{49

get
{ return type; }50

set
{ type = value; }51
}52

53
int markup;54
public int Markup55

{56

get
{ return markup; }57

set
{ markup = value; }58
}59

60
int top;61
public int Top62

{63

get
{ return top; }64

set
{ top = value; }65
}66

67
int pageSize;68
public int PageSize69

{70

set
{ pageSize = value; }71

get
{ return pageSize <= 0 ? 10 : pageSize;}72
}73

74
int superUser;75
public int SuperUser76

{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 DataBind97
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
#endregion115

116

OnDataBinding#region OnDataBinding117
protected override void OnDataBinding(EventArgs e)118

{119
CreateDataItemTemplate();120
CreatePagerTemplate();121
base.OnDataBinding(e);122
}123
#endregion124

125

CreatePagerTemplate#region CreatePagerTemplate126
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
#endregion142

143

CreateDataItemTemplate#region CreateDataItemTemplate144
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
#endregion184

185

GuestBook_ItemDataBound#region GuestBook_ItemDataBound186
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
else197
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
else221

{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
else242

{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.NewLine253
+ "function openWindow(pagePath, args, width, height) {" + Environment.NewLine254
+ " var s = '';" + Environment.NewLine255
+ " s = 'width=' + width + ',height=' + height + ',';" + Environment.NewLine256
+ " var win = window.open(pagePath, 'guestbook', s + 'resizable=no,toolbar=no,scrollbars=no,status=no,alwaysLowered=yes,');" + Environment.NewLine257
+ " win.focus();" + Environment.NewLine258
+ " 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
else264

{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
#endregion278

279

GBDelPost_Command#region GBDelPost_Command280
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
#endregion292
}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 (基类) 它定义了大部分的功能。
using
System;2
using
System.Web.UI.WebControls;3
using
SunBird.Components;4

5
namespace
SunBird.GuestBookControl6

{7
public class GuestBookCreateBase : GuestBookSkinBase8

{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 AttachChildCotrols24
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
#endregion38

39

CreateButton_Click#region CreateButton_Click40
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
#endregion67

68
int type;69
public virtual int Type70

{71

get
{ return type; }72

set
{ type = value; }73
}74

75
int markup;76
public virtual int Markup77

{78

get
{ return markup; }79

set
{ markup = value; }80
}81
}82
}
2、CreatePostNeedLogin : GuestBookCreateBase 需要登录,他实际上只是override了基类的CreateButton_Click方法,为该方法加入了验证登录的操作。
using
System;2
using
System.Web.UI.WebControls;3
using
SunBird.Components;4

5
namespace
SunBird.GuestBookControl6

{7
public class CreatePostNeedLogin : GuestBookCreateBase8

{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
else46

{47
Globals.OutMessage("用户名或密码错误。");48
return;49
}50
}51

52

53
base.CreateButton_Click (sender, e); 54
}55

56
}57
}
3、CreatePostNotNeedLogin : GuestBookCreateBase 不需要登录,实际上该类没有做任何操作仅仅只是为了方便其他人使用。
using
System;2
using
System.Web.UI.WebControls;3

4
namespace
SunBird.GuestBookControl5

{6
public class CreatePostNotNeedLogin : GuestBookCreateBase7

{8
protected override void AttachChildControls()9

{10
base.AttachChildControls();11

12

13
}14
}15
}
控件的核心的类差不多就这几个,完整的代码如果有兴趣,请下载附件查看。
终于找到上传附件的地方了,下面是源代码,有兴趣的朋友可以下载看看,如果你有更好的方法请一定告诉我。
本文介绍了一种使用.NET自定义控件开发全站通用留言本的方法,通过继承Repeater控件,实现了灵活的模板加载和数据绑定,提高了网站活动留言本的复用性和效率。

被折叠的 条评论
为什么被折叠?



