AJAX News Rotator in Sitefinity Part 1

I've recently been developing a control to display recent news in a rotating fashion. Originally, I was using the RadRotator control from Telerik. This is a powerful control that takes a lot of the guesswork out of displaying and rotating news. However, I decided to build my own for two reasons.

First, I didn't like the way the rotator control loads ALL of the news items before running. The RadRotator works by preloading all of the elements, placing each item into its own div element, then cycling through each, toggling visibility of each one at a time. This solution works for a few elements, but if you're cyclying through a half dozen or more items, each with an image, it can be an expensive procedure, especially since the control has to finish loading in order for the page to run. This leaves users viewing half a screen while the content loads.

The other reason I decided to build my own was just out of sheer curiousity and a desire to learn, especially since this is a perfect candidate to get my feet wet in the Ajax world that I've been dying to dive into.

Anyway, enough back-story, let's get to the code! The first thing I had to do was retrieve the news items that will be displayed. I'll be storing the Guids for each news item in the ViewState for quick, on-demand retrieval. The Guids go in a Generic List, which will be accessed by a CurrentItem index, which checks to make sure that this value is always within the valid range of elements.

protected List newsIDs  
{  
    get {  
        if (ViewState["NewsIDs"] == null) ViewState["NewsIDs"] = new List();  
        return (List)ViewState["NewsIDs"];  
    }  
    set { ViewState["NewsIDs"] = value; }  
}  
 
protected int CurrentItem  
{  
    get {  
        if (ViewState["CurrentItem"] == null) ViewState["CurrentItem"] = 0;  
        return (int)ViewState["CurrentItem"];  
    }  
    set {  
        if (value > newsIDs.Count - 1)  
            ViewState["CurrentItem"] = 0;  
        else if (value < 0)  
            ViewState["CurrentItem"] = newsIDs.Count - 1;  
        else ViewState["CurrentItem"] = value;  
    }  
} 

Since I'm using Sitefinity (my content management system of choice!), I'll need to use the NewsManager to retrieve these items. Because of the nature of the News API, it is necessary for me to retrieve the full list of news items on Page_Load. Ideally, I would only be grabbing the Guid for each item, but since the only way to retrieve them is to retrieve the full items, I'll have to do that. Fortunately, this is only in the code behind, and isn't rendering to the page so the overhead should be minimal, especially since it's only done on the initial Page_Load

protected void Page_Load(object sender, EventArgs e)  
{  
    if (this.manager == null) this.manager = new NewsManager("News");  
      
    if (!IsPostBack)  
    {  
        BindData();  
    }  
}  
 
private void BindData()  
{  
    IMetaSearchInfo[] filters = new IMetaSearchInfo[3];  
    filters[0] = new MetaSearchInfo(MetaValueTypes.DateTime, "Expiration_Date", DateTime.Now, SearchCondition.GreaterOrEqual);  
    filters[1] = new MetaSearchInfo(MetaValueTypes.ShortText, "Category", "Top Story");  
    filters[2] = new MetaSearchInfo(MetaValueTypes.DateTime, "Publication_Date", DateTime.Now, SearchCondition.LessOrEqual);  
 
    IList newsList;  
    newsList = manager.Content.GetContent("Publication_Date DESC", filters);  
 
    newsNavRepeater.DataSource = newsList;  
    newsNavRepeater.DataBind();  
 
    foreach (IContent content in newsList)  
        newsIDs.Add(content.ID);  
 
    // start with the first news Item 
    UpdateNews((IContent)newsList[0]);  
} 

The newsNavRepeater is simply a repeater of LinkButtons that will tie to the individual news item ids.

<div class="sf_newsRotator">
    <telerik:radajaxpanel id="NewsAjaxPanel" runat="server" width="550" height="185" loadingpanelid="NewsLoadingPanel">
        <div id="newsFrame" class="newsFrame"> 
            <h3><asp:HyperLink ID="lnkTitle" runat="server" /></h3> 
            <asp:HyperLink ID="lnkFullStory" runat="server" CssClass="fullstory" Text="Full Story &raquo;" /> 
            <div class="sf_newsImage"> 
                <asp:HyperLink ID="lnkThumbnail" runat="server" /> 
            </div> 
            <p>
                <strong style="font-size: .9em;">Posted <asp:Label ID="PublicationLabel" runat="server" /></strong><br /> 
                <asp:Literal ID="SummaryLabel" runat="server" /> 
            </p> 
        </div> 
        <div class="newsNav"> 
            <asp:Repeater ID="newsNavRepeater" runat="server" OnItemDataBound="newsNavRepeater_ItemDataBound" OnItemCommand="newsNavRepeater_ItemCommand">
                <ItemTemplate> 
                    <asp:LinkButton ID="lnkItem" runat="server" /> 
                </ItemTemplate> 
            </asp:Repeater> 
        </div> 
    </telerik:radajaxpanel> 
    <a class="allnews" href="/news/default.aspx" title="View All News from the City of McAllen"> All News</a> 
    <a href="/news.rss" class="rss" target="_blank" title="Subscribe to the City of McAllen Latest News RSS Feed"> 
        <img src="/images/icons/icon-rss.gif" alt="RSS Feed" style="vertical-align: middle;" />
    </a>
</div>
<telerik:radajaxloadingpanel id="NewsLoadingPanel" runat="server" height="190px" width="550px" transparency="50" enableviewstate="false"> 
    <div style="background: #ccc; height: 100%"> 
        <img style="margin-top: 75px; z-index: 1" alt="Loading..." src='<%= RadAjaxLoadingPanel.GetWebResourceUrl(Page, "Telerik.Web.UI.Skins.Default.Ajax.loading.gif") %>' style="border: 0px;" /> 
    </div> 
</telerik:radajaxloadingpanel>

The buttons are populated using the OnItemBound event to fill each link with the appropriate post information from the bound IContent DataItem.

protected void newsNavRepeater_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
    // get link 
    LinkButton lnk = e.Item.FindControl("lnkItem") as LinkButton;

    // get dataitem IContent newsItem = e.Item.DataItem as IContent;
    int itemIndex = e.Item.ItemIndex + 1;
    lnk.Text = itemIndex.ToString();
    lnk.CommandArgument = (itemIndex - 1).ToString();
    lnk.ToolTip = newsItem.GetMetaData("Title").ToString();
} 

Once the navigation is bound, we need to bind the first news item, which is the first item in the newsList collection above (newsList[0]). Note how this item has to be cast as IContent and passed into the UpdateNews method.

private void UpdateNews(IContent newsItem)  
{  
    // make sure item still exists 
    if (newsItem == null)  
    {  
        // if not, reset data! 
        BindData();  
        return;  
    }  
 
    // bind current item data 
    string newsTitle = newsItem.GetMetaData("Title").ToString();  
    lnkTitle.Text = newsTitle;  
    lnkTitle.NavigateUrl = string.Format("/news/default{0}.aspx", newsItem.Url.ToLower());  
    lnkThumbnail.ImageUrl = newsItem.GetMetaData("Thumbnail").ToString();  
    lnkThumbnail.ToolTip = newsTitle;  
    lnkThumbnail.NavigateUrl = lnkTitle.NavigateUrl;  
    lnkThumbnail.Text = newsTitle;  
    SummaryLabel.Text = newsItem.GetMetaData("Summary").ToString();  
    PublicationLabel.Text = ((DateTime)newsItem.GetMetaData("Publication_Date")).ToString("dddd MMMM dd, yyyy");  
 
    LinkButton selectedLink = newsNavRepeater.Controls[CurrentItem].FindControl("lnkItem") as LinkButton;  
    selectedLink.CssClass = "selected";  
} 

Notice also that this method ensures that a valid item was found, if not (such as a newsitem deleted or new items posted) it will reset, binding all new data and start all over again. Also, at the bottom, we since the Controls array of the newsNavRepeater has as many elements as the newsList, we can grab use that index to select the associated LinkButton and set its CSS class so that we can highlight the selected item!

All that is left to do is handle the OnItemCommand event of the newsNavRepeater (since these buttons are what trigger a different item). Remember that the argument for each LinkButton set in BindData is the index of that news item's Guid in the newsList Collection. So all we have to do is retrieve that one news item (using the News API) and run the UpdateNews method again! Notice how before we update the CurrentItem with the new value, we use it to "unselect" the previous news item.

protected void newsNavRepeater_ItemCommand(object source, RepeaterCommandEventArgs e)  
{  
    // unselect css before changing 
    LinkButton selectedLink = newsNavRepeater.Controls[CurrentItem].FindControl("lnkItem") as LinkButton;  
    selectedLink.CssClass = "";  
 
    CurrentItem = Int32.Parse(e.CommandArgument.ToString());  
    IContent newsItem = manager.Content.GetContent(newsIDs[CurrentItem]);  
    UpdateNews(newsItem);  
} 

And that's all there is to it! Wrap all of this inside of a RadAjaxPanel and you've got yourself an interactive news roll! There's still a few things to add, namely a Timer control to automatically cycle the news, but I will cover that my next entry. I hope this has been helpful, and as always your comments are welcome and appreciated!

Enjoyed this post and/or found it useful?
SelArom Dot Net Profile Image
SelAromDotNet

Josh loves all things Microsoft and Windows, and develops solutions for Web, Desktop and Mobile using the .NET Framework, Azure, UWP and everything else in the Microsoft Stack.

His other passion is music, and in his spare time Josh spins and produces electronic music under the name DJ SelArom.



Scroll to top