不知道大家有没有用过igoogle这个google的个人门户网站,我弄的这个东西就是模仿igoogle,主要的难点是新闻模块的动态生成和模块的拖放功能和位置保存。基本原理是用userControl去实现新闻的容器--新闻模块,模块的内容由RSS提供,ASP.Net AJAX实现模块的拖放和服务器的数据存储,js负责提供页面的动态信息交给服务器处理。
一、userControl编写
userControl负责存放新闻,我们为了可以重复使用给他填上了属性。这样根据不同的属性就可以让
userControl提供不同的新闻。
模块的信息来源是RSS然后通过程序分析XML中的信息放在userControl中。
后台代码如下:

1 using System;
2 using System.Data;
3 using System.Configuration;
4 using System.Collections;
5 using System.Web;
6 using System.Web.Security;
7 using System.Web.UI;
8 using System.Web.UI.WebControls;
9 using System.Web.UI.WebControls.WebParts;
10 using System.Web.UI.HtmlControls;
11 using UI_Servers;
12 using DB_Servers;
13 using Castle.ActiveRecord;
14 using Castle.ActiveRecord.Framework;
15
16
17 //public delegate void ClickEventHandler(object sender, EventArgs e);
18 public partial class WebUserControl : System.Web.UI.UserControl
19 {
20 private string Cid;//用户控件ID
21 private string Rssid;//获得数据库中Rss链接的编号
22 private RSSChannel channel;//Rss新闻信息
23 private int count;
24 public string CID
25 {
26 get { return Cid; }
27 set { Cid = value; }
28 }
29 public string RSSID
30 {
31 get { return Rssid; }
32 set { Rssid = value; }
33 }
34
35 public int Count
36 {
37 get { return count; }
38 set { count = value; }
39 }
40 protected void Page_Load(object sender, EventArgs e)
41 {
42 string url;
43 Rss RSS = Rss.Find(Convert.ToInt32(Rssid));//通过Rssid获得Rss实体类(使用了Castle AR技术,数据库层这里不多做介绍了)
44 channel = new RSSChannel(RSS.RssURL);//通过RSS的URL建立新闻列表实体。
45 channel.ReadChannel();//为新闻实体添加数据(从RSS的XML分析而来)
46 }
47 /// <summary>
48 /// 填充页面RSS新闻
49 /// </summary>
50 /// <returns></returns>
51 public string GetContent()
52 {
53 string ret = string.Empty;
54 RSSItem[] items = new RSSItem[channel.Items.Count];
55 int i = 0;
56 for (; i < count && i < channel.Items.Count; i++)
57 {
58 items[i] = channel.Items[i];
59 ret += "<li><a href=/"" + items[i].Link + "/" alt=/"" + items[i].Description + "/">" +
60 items[i].Title +
61 "</a></li>";
62 }
63 return ret;
64
65 }
66 /// <summary>
67 /// RSS新闻模块标题填充
68 /// </summary>
69 /// <returns></returns>
70 public string Title()
71 {
72 return "<a href=/"" + channel.Link + "/" name=/"check/">" + channel.Title + "</a>";
73 }
74 /// <summary>
75 /// 生成拖动控件ID
76 /// 为以后ASP.Net AJAX的拖放注册准备
77 /// </summary>
78 /// <returns></returns>
79 public string DragID()
80 {
81 return Cid + "Drag";
82 }
83
84
85
前台代码如下:

1 <%@ Control Language="C#" AutoEventWireup="true" CodeFile="WebUserControl.cs" Inherits="WebUserControl" %>
2 <div id="<%=CID %>" class="items">
3 <div id="List_top" class="itemTop">
4 <div id="<%=DragID() %>" class="itemTitle">
5 <%=Title() %>
6 </div>
7 <%--<asp:Button runat="server" ID="close_but" Text="X" CssClass="closing" OnClientClick="elemClose(this);"/>--%>
8 <input id="close_but" value="X" class="closing" onclick="elemClose(this);" type="button"/>
9 </div>
10 <!-- 内容区域 OnClick="AButton_Click"-->
11 <div class="itemContent">
12 <ul>
13 <%=GetContent() %>
14 </ul>
15 </div>
16 <div style="height:10px;"></div>
17 </div>
18
二、ASP.Net AJAX拖放编写
这里用到的是ASP.Net AJAX的拖放实现是依靠 ASP.Net AJAX CTP实现的,普通的ASP.Net AJAX 1.0是没有这个功能的。
在aspx页面上面加以下代码加载拖放的功能。

1 <asp:ScriptManager ID="sm" runat="server" EnablePageMethods="true">
2 <Scripts>
3 <asp:ScriptReference Assembly="Microsoft.Web.Preview" Name="PreviewScript.js" />
4 <asp:ScriptReference Assembly="Microsoft.Web.Preview" Name="PreviewDragDrop.js" />
5 </Scripts>
6 </asp:ScriptManager
现在拖放功能加载了,但是要告诉程序那个控件是可以拖放的,那个地方可以是投放控件的容器。ASP.Net AJAX CTP实现拖放需要用XML来注册控件的信息。下面是容器和容器注册的代码:
2 <!-- 左 (容器) -->
3 < div id ="leftArea" class ="list1" runat ="server" >
4 </ div >
5 <!-- 中 (容器) --> (容器)
6 < div id ="middleArea" class ="list2" runat ="server" >
7 </ div >
8 <!-- 右 (容器) --> (容器)
9 < div id ="rightArea" class ="list3" runat ="server" >
10 </ div >
11 <!-- template elements -->
12 < div style ="display: none;" >
13 <!-- 内有元素时候显示 -->
14 < div id ="dropCueTpl" class ="dropCue" >
15 </ div >
16 <!-- 内没有元素时候显示 -->
17 < div id ="emptyTpl" class ="emptyList" >
18 Drop content here.
19 </ div >
20 </ div >
21 </ div >
22 <!-- 下面是注册代码(XML) -->
23 < script type ="text/xml-script" >
24 < page >
25 < components >
26
27 <!-- 左 -->
28 < control id = " leftArea " > (id为容器控件ID)
29 < behaviors >
30 < dragDropList id = " leftDragDropBehavior " dragDataType = " HTML "
31 acceptedDataTypes = " 'HTML' " dragMode = " Move " direction = " Vertical " >
32 < dropCueTemplate > (内有元素时候显示)
33 < template layoutElement = " dropCueTpl " / >
34 < / dropCueTemplate>
35 < emptyTemplate > (内没有元素时候显示)
36 < template layoutElement = " emptyTpl " / >
37 < / emptyTemplate>
38 < / dragDropList>
39 < / behaviors>
40 < / control>
41 <!-- 中 -->
42 < control id = " middleArea " >
43 < behaviors >
44 < dragDropList id = " middleDragDropBehavior " dragDataType = " HTML "
45 acceptedDataTypes = " 'HTML' " dragMode = " Move " direction = " Vertical " >
46 < dropCueTemplate >
47 < template layoutElement = " dropCueTpl " / >
48 < / dropCueTemplate>
49 < emptyTemplate >
50 < template layoutElement = " emptyTpl " / >
51 < / emptyTemplate>
52 < / dragDropList>
53 < / behaviors>
54 < / control>
55
56 <!-- 右 -->
57 < control id = " rightArea " >
58 < behaviors >
59 < dragDropList id = " rightDragDropBehavior " dragDataType = " HTML "
60 acceptedDataTypes = " 'HTML' " dragMode = " Move " direction = " Vertical " >
61 < dropCueTemplate >
62 < template layoutElement = " dropCueTpl " / >
63 < / dropCueTemplate>
64 < emptyTemplate >
65 < template layoutElement = " emptyTpl " / >
66 < / emptyTemplate>
67 < / dragDropList>
68 < / behaviors>
69 < / control>
70
71 <!-- 可拖放控件的注册代码下面会提到 -->
72 <%= getreg() %>
73
74 < / components>
75 < / page>
76 </ script >
下面介绍一下拖放时候的可以用到的两个触发事件:
- Sys.Preview.UI.DragDropManager.add_dragStart(function(){})在拖拽开始的时候触发
- Sys.Preview.UI.DragDropManager.add_dragStop(function(){})在拖拽结束的时候触发
括号里面是执行的函数。
三、新闻模块的动态生成
新闻模块既然已经被做成userControl,那么只要把userControl当做一个类实例化它,再通过页面初始化时候加载它就可以实现了。但是这个涉及到的问题就是如何在程序中将userControl当做类实例化,如何使其具有拖拽功能,又放在那里。
首先我们来解决userControl的实例化问题,有些朋友可能发现直接写出userControl的名字程序不认识,就算是放在一个解决方案中using这个方案也不行,其实这个是要在.aspx页面下面注册的,注册或就可以在.cs下面访问了,这个和加载其他第三方控件是一样的。需要在aspx的文件开头就注册好程序中才能使用,当然在Web.config文件中也可以注册。一下是两种注册方法:
这样解决了这个问题就可以动态实例化一个userControl了

1 WebUserControl b = (WebUserControl)LoadControl("WebUserControl.ascx");
2 b.CID = item.Trim();
3 b.RSSID = item_data[1].Trim(); ;
4 b.Count = Convert.ToInt32(item_data[2
设置了这些属性以后在程序调用实例化userControl,其初始化时候新闻信息就会添加在这个userControl中了。
下面是如何实现拖放功能,想让其有拖放功能在前面已经做好了其拖放容器好容器的注册,现在只需要将其加入注册就可以了,因为控件是动态生成的没有办法直接写在aspx代码里面我们用<%=函数名%>实现向aspx中添加注册代码的方式实现注册。把userControl的CID和DragID以上面的可拖放控件的注册方式生成一段注册字符串,再提供给函数就可以了,由于比较简单这里就不增加代码了。
四、JS对userControl的定位和位置存储。
这个是我编这个网站时候遇到的最大的问题,我在这里想了一个方法实现这个问题是一段字符串来记录的,因为每次用户的拖拽时间都要去改变本用户的页面设置,也就是说每次数据库都要有改动,都要和服务器有信息交换,为了不花销过多的服务器资源把大部分的计算工作留给了预览器即JS处理,然后将页面状况描述为一个二维数组,再由有服务器将数组变成字符串存储在个人的设置中。
字符串格式
JS会在每次拖放结束的时候执行,搜索所有的新闻模块,然后根据其位置经过计算处理整理出字符串。
JS处理代码如下:
2 function pageLoad(sender, args) {
3 // 加载拖放结束时间的触发函数
4 Sys.Preview.UI.DragDropManager.add_dragStop(DragStopped);
5 // Sys.Preview.UI.DragDropManager.add_dragStart(function(){window.alert("Asdf");});
6 }
7 // 拖放结束时间的触发函数
8 function DragStopped(sender, args) {
9 // 模块位置排序
10 var elems = elemSort();
11 // userid为当前用户ID因为AJAX在程序中无法访问Session的值所以放在一个input中用AJAX回传
12 var userid = $get( " userIdHidden " );
13 // 调用服务器事件保存模块位置
14 PageMethods.elemOffset(elems,userid.value);
15 }
16 // function DragStart(sender, args)
17 // {
18 // window.alert("DragStart Goes");
19 // }
20 // 模块类
21 function elem(ID,X,Y)
22 {
23 this .ID = ID;
24 this .X = X; // 模块位子X
25 this .Y = Y; // 模块位子Y
26 }
27 // 获得模块
28 // findElemsName为可拖放模块的Name熟悉,这里的拖放模块Name值都是一致的。
29 function elemGet(findElemsName)
30 {
31
32 // 获得可拖放模块集合
33 var ele = document.getElementsByName(findElemsName);
34 var elems = new Array;
35 if (ele.length)
36 {
37 var temp_ele;
38 for (elecount = 0 ;elecount < ele.length;elecount ++ )
39 {
40 var tempOffset = getoffset(ele[elecount]);
41 temp_ele = elemFilter(ele[elecount].parentNode.parentNode.parentNode.id);
42 var temp = new elem(temp_ele,tempOffset.x,tempOffset.y);
43 elems.push(temp);
44 }
45 }
46 return elems;
47 }
48 // 获得拖放模块ID
49 function elemFilter(elemid)
50 {
51 // window.alert("elemFilter load");
52 var temp = new Array();
53 temp = elemid.split( " X " );
54 i = 0 ;
55 var ret = "" ;
56 // window.alert(elemid);
57 while (temp.length && i < 3 )
58 {
59 if (temp[i])
60 {
61 if (i != 0 )
62 ret += " X " ;
63 ret += temp[i];
64 i ++ ;
65 }
66 }
67 return ret;
68 }
69 // 元素位子
70 function elemOffset(x,y)
71 {
72 this .x = x;
73 this .y = y;
74
75 }
76 // 获得元素位子
77 function getoffset(e)
78 {
79 var x = e.offsetLeft;
80 var y = e.offsetTop;
81 while (e = e.offsetParent)
82 {
83 x += e.offsetLeft;
84 y += e.offsetTop;
85 }
86 var rec = new elemOffset(x,y);
87 return rec
88 }
89 //
90 function elemSort()
91 {
92 var elems = new Array();
93 elems = elemGet( " check " );
94 elems = sortY(elems);
95 elems = sortX(elems);
96 // 三个数组分辨对应相应列
97 var elemSorted = new Array( new Array(), new Array(), new Array());
98 // 获得列的位置
99 var leftArea = getoffset(document.getElementById( " leftArea " ));
100 var middleArea = getoffset(document.getElementById( " middleArea " ));
101 var rightArea = getoffset(document.getElementById( " rightArea " ));
102 // 和列位置比较看当前循环模块属于那个列,并降模块ID压入相应数组
103 for (elemscount = 0 ;elemscount < elems.length;elemscount ++ )
104 {
105
106
107 if (elems[elemscount].X >= leftArea.x && elems[elemscount].X < middleArea.x)
108 {
109 elemSorted[ 0 ].push(elems[elemscount].ID);
110
111
112 }
113 else if (elems[elemscount].X >= middleArea.x && elems[elemscount].X < rightArea.x)
114 {
115 elemSorted[ 1 ].push(elems[elemscount].ID);
116 }
117 else
118 {
119 elemSorted[ 2 ].push(elems[elemscount].ID);
120 }
121 }
122 return elemSorted;
123 }
124
125 // 拖放模块当前列排序
126 function sortY(elems)
127 {
128 var temp = new elem();
129 if (elems.length)
130 {
131 for (sorti = 0 ;i < elems.length - 1 ;sorti ++ )
132 {
133 for (sortj = elems.length - 1 ;sortj > sorti + 1 ;sortj -- )
134 {
135 if (elems[sortj].Y < elems[sortj - 1 ].Y)
136 {
137 temp = elems[sortj];
138 elems[sortj] = elems[sortj - 1 ];
139 elems[sortj - 1 ] = temp;
140 }
141 }
142 }
143 }
144 return elems;
145 }
146
147 // 拖放模块当前行排序
148 function sortX(elems)
149 {
150 var temp = new elem();
151 if (elems.length)
152 {
153 for (Sorti = 0 ;Sorti < elems.length - 1 ;Sorti ++ )
154 {
155 for (Sortj = elems.length - 1 ;Sortj > Sorti + 1 ;Sortj -- )
156 {
157 if (elems[Sortj].X < elems[Sortj - 1 ].X)
158 {
159 temp = elems[Sortj];
160 elems[Sortj] = elems[Sortj - 1 ];
161 elems[Sortj - 1 ] = temp;
162 }
163 }
164 }
165 }
166 return elems;
167 }
168 /* 以下是控件关闭按钮客户端事件 */
169 function elemClose(sender)
170 {
171 var closeElemId = sender.parentNode.parentNode.id;
172
173 var closeElem = document.getElementById(closeElemId);
174 // 删除节点
175 closeElem.parentNode.removeChild(closeElem);
176 // 模块排序
177 var elemCloseSort = elemSort();
178 var userid = $get( " userIdHidden " );
179 // 保存位置
180 PageMethods.elemOffset(elemCloseSort,userid.value);
181 }
最后是首页的源码可以更好的理解程序但是代码不能运行哦首页代码