Click here to Skip to main content
Email Password   helpLost your password?

Background

With the advent of SP1 of WSS 3.0 and MOSS 2007, developers can now make use of ASP.NET AJAX 1.0 (formerly ATLAS) in their applications. This is a good news for the SharePoint user community since they can use partial postbacks to update the back-ends (usually SQL Server 2005) in a flash rather than waiting for the full postback of SharePoint, which can be painfully slow sometimes.

Introduction

This article describes how to build an RSS Reader Web Part which supports ASP.NET 2.0 AJAX 1.0 and makes use of the AJAX Control Toolkit to implement multiple RSS readers in Tab Panels.

Software needed

Note

Since the source code of this Web Part is too big, I am not going to post and explain the whole code. Instead, I will focus on the main areas like the implementation of the AJAX and RSS readers only. This articles assumes knowledge of developing Web Parts with custom properties. First, you need to configure your MOSS web application for AJAX. I am not going to repeat it here since this is already described in this MSDN article: http://msdn2.microsoft.com/en-us/library/bb861898.aspx.

Screenshots

This is what we are going to develop. Looks pretty cool ... isn't it?

screen21.JPG

screen31.JPG

About the Web Part

The input to our Web Part will be multiple RSS URLs (maximum 4) and the corresponding image URLs (optional) which will be displayed next to the RSS feeds.

Each RSS feed will be displayed in a separate tab, and each item as a collapsible item with description. The collapsibility is almost the same as that of out of the box Web Parts except that we will make use of + and – signs to indicate collapsibility using client script.

We will also have custom properties for Update Interval – The interval after which the Web Part will update itself without a postback.

Now, it's time to start coding the Web Part. To separate the presentation from the implementation, we will implement the RSS Reader in a different class than the Web Part.

This class contains a static method GetFeeds which accepts an RSS URL (SharePoint only) and returns a DataTable containing four columns with rows=number of items+2. The last two rows contain the timestamp, title of the list, and the URL to the list.

Here is the code for the same, I have tried to make it as simple as possible:

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;

    }
}

To start with, we will setup the custom properties for our Web Part. I have taken a string array of length four to implement the eight properties for each tab: RSS URLs and image URLs. Coming to the structure of our Web Part, we will have an UpdatePanel control. Inside the UpdatePanel, we would have four TabPanels containing a label each for the formatted output.

We will also have the UpdateProgress control to display a simple animating image while the data is updating, and a Timer control to refresh the UpdatePanel whenever the time interval elapses.

So graphically, this would be the layout of the Web Part:

Here is the code for declaring our class level variables (snipped):

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;
    }
}

To work with AJAX, we need to declare the ScriptManager object. We will make use of the OnInit() function of the Web Part to initialize the ScriptManager and initialize the TabContainer and TabPanel. The code below retrieves the already existing ScriptManager object on the page, and if none is found, a new one is created. We also have a stylesheet attached to the TabContainer to style our control.

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);
}

This is our Web Part property panel:

screen5.JPG

We will now implement the CreateChildControls() function which initializes all the controls and places the controls in the Controls collection in the correct order. The function also checks if the RSS URL is given to the Web Part, and if not, disables the tab, which renders the tab invisible.

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;
}

Let's now implement our final function, which will take the RSS URL and will return a string containing the formatted RSS output. The returned string will make use of the image URL and RSS which have been specified in the Web Part. The function will call the GetFeeds() function defined above to retrieve the feeds and format it properly so that they are ready to be rendered.

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();
}

I have tried to make the Web Part as simple as possible. Some of the points to be worth noted:

  1. This Web Part is designed only for consuming SharePoint RSS URLs only. Although, you are free to modify the code to suit your needs.
  2. The Web Part does not take user permissions into account while reading the RSS URL.

Click here to download the source code for this Web Part.

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralScriptManager
MauroMasucci
3:02 15 Apr '09  
When trying to add the file I get told that the page must have a scriptmanager on it. I'd done so yet still cant add the webpart to the page. Can you send me the updated webpart code?
QuestionProject Source Code or Binary
gandevijay
4:32 17 Feb '09  
Hi Madhur,

Wounder full job, I'm a bigger and would like to have the source code, could you please send me the complete solution or the binary file or webpart that could be installed on the site.

My email id is harshini.hande@gmail.com

Appreciate in advance.

Thanks,
Harshini.
Generalcould you send the complete solution or the binary file or webpart that could be installed on the site
Member 4045742
8:34 7 Oct '08  
Hi Madhur
Nice job, have been looking for this webpart for long time.could you please send me the complete solution or the binary file or webpart that could be installed on the site. My email id is ricky_lubana@yahoo.com
All your help would be appreciated.
GeneralRe: could you send the complete solution or the binary file or webpart that could be installed on the site
Madhur Ahuja
8:37 7 Oct '08  
Hi
Thanks. I do not have the solution right now. I will have to search it in my laptop. Will do and reply back to you.

Rgds,
Madhur

Madhur
http://madhurahuja.blogspot.com

GeneralRe: could you send the complete solution or the binary file or webpart that could be installed on the site
Singh Ricky
9:29 7 Oct '08  
Thanks Madhur, i appreciate all your help. Please see if you could find it.
GeneralRe: could you send the complete solution or the binary file or webpart that could be installed on the site
mad-tie
6:38 29 Jan '09  
Hi! Nice done! I was needing that! Could you send me the solution also?
My email is mad-tiePPP@hotmail.com without the PPP (to avoid spam =P)

Thanx in advance.
QuestionRe: could you send the complete solution or the binary file or webpart that could be installed on the site
anoomathew
3:41 30 Jun '09  
Hi,

If you still have the complete solution, could you please send it to my mail id (dr_anumathew@yahoo.co.uk)
I am trying to develop a custom web part for feed reader.

regards,

Anu
GeneralSorry to trouble you,I have a question
liyuekd@163.com
19:52 11 May '08  
Thanks,you are a bomb
GeneralSorry to trouble you for the Webcontrol -rsspanel of the project
zeroneten
22:54 21 Apr '08  
Today I remember the code,so I read it again.I found the control - rsspanel useful to me, but the code is not complete.So should you send me a complete project code,Thanks!
my email address- workwang@gmail.com
QuestionERROR
iamsunil2005
0:45 14 Apr '08  
Error occured: Request for the permission of type 'Microsoft.SharePoint.Security.SharePointPermission, Microsoft.SharePoint.Security, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c' failed.

modified on Monday, April 21, 2008 12:14 AM

GeneralSorry for my question before. I got some progress.
zeroneten
17:21 14 Mar '08  
I have bulit your project as a class sucessfully.Thanks.

Another question is there are some images and a css file ,where should I put them ? In IIS?

Thanks!
GeneralRe: Sorry for my question before. I got some progress.
Madhur Ahuja
17:25 14 Mar '08  
Hi

Just add them directly under the solution in VS 2005. I am sorry, I should have given the full solution. This is because you need to reference some items in the Assemblyinfo.cs.. I will update the article soon.

Madhur

Madhur
http://madhurahuja.blogspot.com

GeneralRe: Sorry for my question before. I got some progress.
zeroneten
18:38 14 Mar '08  
Thanks.

I have bulit a webpart class and put your cs file in it,and then put the Dlls ,images, css file in Directory of IIS site BIN. In sharepoint I registy it and add it in my sharepoint site,but there ia a problem that when I add a rss url in "1st RSS Feed URL",Sharepoint show me the error "An unexpected error has occurred. "

Thanks for you reply.
GeneralRe: Sorry for my question before. I got some progress.
Madhur Ahuja
19:38 14 Mar '08  
Couple of things to note :

You do not need to put, images inside bin. Instead set the Compile option of all images and js files to Embedded Resource.

Make sure that you add these lines in Assemblyinfo.cs

[assembly: System.Web.UI.WebResource("RSSReaderAjax.minus.gif", "img/gif")]
[assembly: System.Web.UI.WebResource("RSSReaderAjax.plus.gif", "img/gif")]
[assembly: System.Web.UI.WebResource("RSSReaderAjax.activityanimation.gif", "img/gif")]
[assembly: System.Web.UI.WebResource("RSSReaderAjax.aero_light.gif", "img/gif")]
[assembly: System.Web.UI.WebResource("RSSReaderAjax.StyleSheet.css", "text/css")]

If this still does not solve the problem .. Follow this article to reveal the actual exception and post it here:
http://madhurahuja.blogspot.com/2008/01/reveal-unknown-error-on-sharepoint-2007.html[^]

Madhur
http://madhurahuja.blogspot.com

GeneralRe: Sorry for my question before. I got some progress.
zeroneten
23:17 14 Mar '08  
Thanks,I will have a try.
GeneralThanks for your article, but a question.
zeroneten
16:48 14 Mar '08  
I have some problem when setting your project in sharepoint,for that should I bulid your project as a class or ajax web site?

If it is a class ,I get the error "Error 1 The type or namespace name 'UpdatePanel' could not be found (are you missing a using directive or an assembly reference?) F:\Myworkplace\RSSReaderAjax\RSSReaderAjax\RSSReaderAjax.cs 30 9 RSSReaderAjax
" but I have the ajax asp.net v1 installed.

If it is a ajax web site,not a webpart?

Because I get little about webpart, look forward your answer.^.^
GeneralRe: Thanks for your article, but a question.
Madhur Ahuja
3:53 8 Apr '08  
Hi

Its an AJAX webpart not a site. You should build it as a class and deploy it as webpart.

Madhur

Madhur
http://madhurahuja.blogspot.com

GeneralThanks
Renny[RuS]
23:27 29 Feb '08  
Thanks, man! A great project!
GeneralRe: Thanks
Madhur Ahuja
0:45 1 Mar '08  
You are welcome. Let me know if you have any questions or feedback/improvements ...

Madhur
http://madhurahuja.blogspot.com


Last Updated 24 Feb 2008 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010