支持标签和使用 AJAX 进行异步周期性数据刷新的 RSS 阅读器 Web 部件






2.60/5 (6投票s)
创建带标签支持和使用 AJAX 的异步周期数据刷新 RSS 阅读器 Web 部件。
背景
随着 WSS 3.0 和 MOSS 2007 的 SP1 的发布,开发人员现在可以在他们的应用程序中使用 ASP.NET AJAX 1.0(以前称为 ATLAS)。这对 SharePoint 用户社区来说是个好消息,因为他们可以使用部分回发来闪电般地更新后端(通常是 SQL Server 2005),而无需等待 SharePoint 的完整回发,后者有时可能会非常缓慢。
引言
本文介绍了如何构建一个支持 ASP.NET 2.0 AJAX 1.0 的 RSS 阅读器 Web 部件,并利用 AJAX 控件工具包在 Tab Panels 中实现多个 RSS 阅读器。
所需软件
- MOSS 2007 SP1
- Visual Studio 2005
- ASP.NET 2.0
- AJAX 1.0
- AJAX 控件工具包
注意
由于此 Web 部件的源代码太大,我不会发布和解释全部代码。相反,我将重点介绍 AJAX 和 RSS 阅读器实现的几个主要方面。本文假定您了解使用自定义属性开发 Web 部件的知识。首先,您需要为您的 MOSS Web 应用程序配置 AJAX。我不会在这里重复,因为这已在 MSDN 文章中进行了描述:http://msdn2.microsoft.com/en-us/library/bb861898.aspx。
屏幕截图
这就是我们要开发的。看起来很酷……不是吗?
关于 Web 部件
我们将要输入的 Web 部件是多个 RSS URL(最多 4 个)以及相应的图片 URL(可选),这些 URL 将显示在 RSS Feed 旁边。
每个 RSS Feed 将显示在一个单独的选项卡中,每个项目将作为一个可折叠的项目显示,并带有描述。可折叠性几乎与开箱即用的 Web 部件相同,除了我们将使用“+”和“-”符号来指示可折叠性,并使用客户端脚本。
我们还将拥有用于更新间隔的自定义属性 – Web 部件在没有回发的情况下自行更新的间隔。
现在,是时候开始编写 Web 部件的代码了。为了将表示与实现分开,我们将实现 RSS 阅读器,使其与 Web 部件位于不同的类中。
此类包含一个静态方法 GetFeeds
,该方法接受一个 RSS URL(仅限 SharePoint)并返回一个 DataTable
,其中包含四列,行数等于项目数 + 2。最后两行包含时间戳、列表标题和列表的 URL。
这是代码,我已尽力使其尽可能简单。
class RSSRead
{
internal static System.Data.DataSet GetFeeds(string url)
{
System.Data.DataSet ds = new System.Data.DataSet();
System.Data.DataTable dtFeeds = new System.Data.DataTable();
dtFeeds.Columns.Add("Title");
dtFeeds.Columns.Add("Url");
dtFeeds.Columns.Add("PublishDate");
dtFeeds.Columns.Add("Content");
System.Data.DataTable info = new System.Data.DataTable();
info.Columns.Add("Title");
XmlDocument doc = null;
try
{
doc = new XmlDocument();
WebClient wc = new WebClient();
wc.UseDefaultCredentials = true;
string xml = wc.DownloadString(url);
xml = xml.Substring(3);
doc.LoadXml(xml);
XmlNode nRoot = doc.DocumentElement;
XmlNodeList nNodes = nRoot.SelectNodes("channel/item");
System.Data.DataRow dr2 = info.NewRow();
dr2["Title"] = nRoot.SelectSingleNode("channel/title").InnerText; ;
info.Rows.Add(dr2);
dr2 = info.NewRow();
dr2["Title"] = nRoot.SelectSingleNode("channel/link").InnerText;
info.Rows.Add(dr2);
foreach (XmlNode node in nNodes)
{
System.Data.DataRow dr = dtFeeds.NewRow();
dr["Title"] = node.SelectSingleNode("title").InnerText;
dr["Url"] = node.SelectSingleNode("link").InnerText;
dr["PublishDate"] = node.SelectSingleNode("pubDate").InnerText;
dr["PublishDate"] = Convert.ToDateTime(
dr["PublishDate"].ToString()).ToUniversalTime().ToString();
dr["Content"] = node.SelectSingleNode("description").InnerText;
dtFeeds.Rows.Add(dr);
}
}
catch (Exception ee)
{
System.Data.DataRow dr2 = info.NewRow();
dr2["Title"] = ee.Message;
info.Rows.Add(dr2);
ds.Tables.Add(info);
return ds;
}
System.Data.DataRow dr1 = info.NewRow();
dr1["Title"] = DateTime.Now.ToLongTimeString();
info.Rows.Add(dr1);
ds.Tables.Add(dtFeeds);
ds.Tables.Add(info);
return ds;
}
}
首先,我们将设置 Web 部件的自定义属性。我采用了长度为四的字符串数组来实现每个选项卡的八个属性:RSS URL 和图片 URL。至于 Web 部件的结构,我们将拥有一个 UpdatePanel
控件。在 UpdatePanel
内部,我们将有四个 TabPanel
,每个 TabPanel
包含一个用于格式化输出的标签。
我们还将拥有 UpdateProgress
控件,用于在数据更新时显示简单的动画图像,以及一个 Timer
控件,用于在时间间隔过去时刷新 UpdatePanel
。
所以,从图形上看,这就是 Web 部件的布局。
- Web 部件控件
UpdatePanel
控件TabContainer
控件TabPanel
控件Label
控件UpdateProgress
控件- Timer 控件
这是声明我们的类级别变量的代码(已省略)
private string []_rssurl=new string[4];
private string[] _rssimgurl = new string[4];
TabPanel []tabs=new TabPanel[4];
TabContainer tc;
Label[] rsstext = new Label[4];
UpdatePanel rsspanel;
UpdateProgress rssprogress;
Timer ajaxtimer;
int _updateinterval=120;
int _imgpanelwidth = 0;
[DefaultValue("")]
[WebPartStorage(Storage.Shared)]
[FriendlyNameAttribute("1st RSS Feed URL")]
[Description("Put 1st the RSS Feed URL")]
[Browsable(true)]
[XmlElement(ElementName = "RSSUrl")]
public string RSSUrl
{
get
{
return _rssurl[0];
}
set
{
Uri url=new Uri(value,UriKind.Absolute);
if (url.GetLeftPart(UriPartial.Path).Contains(rssstr))
{
_rssurl[0] = value;
}
}
}
[DefaultValue("")]
[WebPartStorage(Storage.Shared)]
[FriendlyNameAttribute("1st Tab Image URL")]
[Description("Put 1st Tab Imag URL")]
[Browsable(true)]
[XmlElement(ElementName = "RSSimgUrl")]
public string RSSImgUrl
{
get
{
return _rssimgurl[0];
}
set
{
_rssimgurl[0] = value;
}
}
为了使用 AJAX,我们需要声明 ScriptManager
对象。我们将利用 Web 部件的 OnInit()
函数来初始化 ScriptManager
并初始化 TabContainer
和 TabPanel
。下面的代码检索页面上已存在的 ScriptManager
对象,如果未找到,则创建一个新的。我们还将一个样式表附加到 TabContainer
以样式化我们的控件。
protected override void OnInit(EventArgs e)
{
// Let's find if the ScriptManager exists and add it if not
ScriptManager scriptManager = ScriptManager.GetCurrent(Page);
if (scriptManager == null)
{
scriptManager = new ScriptManager();
scriptManager.EnablePartialRendering = true;
scriptManager.ID = "sm";
if (Page.Form != null)
{
// Insert script manager after the web part manager
for (int controlIndex = 0; controlIndex <
Page.Form.Controls.Count; controlIndex++)
{
if (Page.Form.Controls[controlIndex].GetType() ==
WebPartManager.GetType())
{
Page.Form.Controls.AddAt(controlIndex + 1, scriptManager);
}
}
}
}
tc = new TabContainer();
tc.ID = "tc";
tc.BorderWidth = Unit.Pixel(0);
for (int i = 0; i < 4; ++i)
{
tabs[i] = new TabPanel();
tabs[i].HeaderText = "Tab "+i.ToString();
tabs[i].ID = "tabs" + i.ToString();
tabs[i].BorderWidth = Unit.Pixel(0);
tc.Tabs.Add(tabs[i]);
}
rssprogress = new UpdateProgress();
rssprogress.ID = "rssprogress";
rssprogress.ProgressTemplate =
new MyTemplate(this.Page.ClientScript.GetWebResourceUrl(this.GetType(),
"RSSReaderAjax.activityanimation.gif"));
this.Controls.Add(rssprogress);
this.ChromeType = System.Web.UI.WebControls.WebParts.PartChromeType.None;
base.OnInit(e);
}
这是我们的 Web 部件属性面板
现在,我们将实现 CreateChildControls()
函数,该函数初始化所有控件并将控件按正确的顺序放置在 Controls
集合中。如果未为 Web 部件提供 RSS URL,该函数还会检查并禁用选项卡,从而使选项卡不可见。
protected override void CreateChildControls()
{
base.CreateChildControls();
#region Ajax_start
EnsurePanelFix();
rsspanel = new UpdatePanel();
rsspanel.ID = "rsspanel";
rsspanel.UpdateMode = UpdatePanelUpdateMode.Conditional;
rsspanel.ChildrenAsTriggers = true;
ajaxtimer = new Timer();
ajaxtimer.Enabled = true;
ajaxtimer.ID = "ajaxtimer";
ajaxtimer.Interval = UpdateInterval*1000;
#endregion
string content = "<script language="'javascript'"> " +
togglescript + "</script>";
System.Web.UI.ScriptManager.RegisterClientScriptBlock(Page,
this.GetType(), "madhur", content, false);
plusimage = this.Page.ClientScript.GetWebResourceUrl(this.GetType(),
"RSSReaderAjax.plus.gif");
minusimage = this.Page.ClientScript.GetWebResourceUrl(this.GetType(),
"RSSReaderAjax.minus.gif");
string css = this.Page.ClientScript.GetWebResourceUrl(this.GetType(),
"RSSReaderAjax.StyleSheet.css");
string link = "<link rel='stylesheet' type='text/css' href='" +
css + "'/>";
Page.Header.Controls.Add(new LiteralControl(link));
rsspanel.ContentTemplateContainer.Controls.Add(tc);
rsspanel.ContentTemplateContainer.Controls.Add(ajaxtimer);
for (int i = 0; i < 4; ++i)
{
if (GetRSSUrl(i) == null)
{
tc.Tabs[i].Enabled = false;
tabs[i].Controls.Add(new LiteralControl("Please specify " +
"the URL of the RSS feed in webpart properties.<br>"));
}
else
{
rsstext[i] = new Label();
rsstext[i].ID = "rsstext" + i.ToString();
rsstext[i].Text = RSSBind(GetRSSUrl(i),i);
tabs[i].Controls.Add(rsstext[i]);
}
}
this.Controls.Add(rsspanel);
AjaxControlToolkit.UpdatePanelAnimationExtender anim =
new UpdatePanelAnimationExtender();
anim.ID = "anim";
anim.TargetControlID = rsspanel.ID;
}
现在,让我们实现最后一个函数,它将接受 RSS URL 并返回一个包含格式化 RSS 输出的字符串。返回的字符串将利用 Web 部件中指定的图片 URL 和 RSS。该函数将调用上面定义的 GetFeeds()
函数来检索 Feed 并对其进行适当格式化,以便可以呈现它们。
public string RSSBind(string url,int index)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
System.Data.DataSet ds = RSSRead.GetFeeds(url);
if (ds.Tables.Count == 1)
{
sb.Append("Error occured: " + ds.Tables[0].Rows[0][0].ToString());
return sb.ToString();
}
System.Data.DataTable dtFeeds = ds.Tables[0];
System.Data.DataTable info = ds.Tables[1];
int count = 1;
string divnonestyle = "style=display:none";
string divid = string.Empty;
string parentdivid = string.Empty;
string funccall = string.Empty;
sb.Append("<table>");
sb.Append("<tr>");
string s = GetRSSImage(index);
if (!string.IsNullOrEmpty(s))
{
sb.Append("<td width='"+ImgPanelWidth+
"' valign='top'>");
sb.Append("<img src='" + GetRSSImage(index) +
"' valign='top'/>");
sb.Append("</td>");
}
sb.Append("<td>");
foreach (System.Data.DataRowView drv in dtFeeds.DefaultView)
{
divid = this.ID+index.ToString()+ count.ToString();
parentdivid = "ctl_" + divid;
funccall = "javascript:ToggleItemDescription('" + divid +
"','" + plusimage + "','" +
minusimage + "')";
sb.Append("<table>");
sb.Append("<tr>");
sb.Append("<td>");
sb.Append("<img onclick=" + funccall +
" valign=bottom border=0 style='cursor:hand;'id=\"" +
parentdivid + "\" src=\"" + plusimage + "\"/>");
sb.Append("</td>");
sb.Append("<td>");
sb.Append("<a href=\"" + funccall+"\">" +
drv["Title"].ToString() + "</a>");
sb.Append("</td>");
sb.Append("</tr>");
sb.Append("</table>");
sb.Append("<div id=\"" + divid +
"\"" + divnonestyle + ">");
drv["Content"] =
drv["Content"].ToString().Replace("<b>Body:</b>", string.Empty);
drv["Content"] = drv["Content"].ToString().Replace('Â', ' ');
sb.Append(drv["Content"].ToString());
sb.Append("Published on: " + drv["PublishDate"].ToString());
sb.Append("</div>");
count++;
}
sb.Append("<a href=\"" + info.Rows[1][0].ToString() +
"\"><br>Click here to View all items</a><br>");
sb.Append("</td>");
sb.Append("</tr>");
sb.Append("</table>");
int j=info.Rows[0][0].ToString().IndexOf(':');
if (j != -1)
tc.Tabs[index].HeaderText = info.Rows[0][0].ToString().Substring(j + 2);
else
tc.Tabs[index].HeaderText = info.Rows[0][0].ToString();
return sb.ToString();
}
我已尽力使 Web 部件尽可能简单。一些值得注意的点
- 此 Web 部件仅设计用于消耗 SharePoint RSS URL。但是,您可以自由修改代码以满足您的需求。
- Web 部件在读取 RSS URL 时不考虑用户权限。
单击此处 下载此 Web 部件的源代码。