65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIcon

2.60/5 (6投票s)

2008年2月24日

CPOL

4分钟阅读

viewsIcon

104001

downloadIcon

403

创建带标签支持和使用 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

屏幕截图

这就是我们要开发的。看起来很酷……不是吗?

screen21.JPG

screen31.JPG

关于 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 并初始化 TabContainerTabPanel。下面的代码检索页面上已存在的 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 部件属性面板

screen5.JPG

现在,我们将实现 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 部件尽可能简单。一些值得注意的点

  1. 此 Web 部件仅设计用于消耗 SharePoint RSS URL。但是,您可以自由修改代码以满足您的需求。
  2. Web 部件在读取 RSS URL 时不考虑用户权限。

单击此处 下载此 Web 部件的源代码。

© . All rights reserved.