building a contentrotator asp

Upload: salisu-webmaster

Post on 08-Apr-2018

216 views

Category:

Documents


0 download

TRANSCRIPT

  • 8/7/2019 Building a ContentRotator ASP

    1/17

    Building a ContentRotator ASP.NET Server

    Control

    Summary: This article examines the steps involved in creating a custom, compiled ASP.NET server

    control that randomly rotates through specified content, much like the built-inAdRotator control

    randomly rotates through a series of predefined banner advertisements. In examining the innards

    of the ContentRotator control this article touches upon several aspects of custom ASP.NET control

    development. (20 printed pages)

    Download ContentRotator.msi.

    Contents

    Introduction

    Thinking About Content RotationSpecifying Content Items

    Specifying Content Through a Content File

    Specifying Content Items Declaratively

    Specifying Content ProgrammaticallyDetermining the Content Item to Display

    Reading Content Data from the Content File

    Reading Content Data Programmatically

    Reading Content from the Control's Declarative Syntax

    Picking a Random Content ItemServing Dynamic ContentCustomizing the Selected Content Item

    ConclusionSpecial Thanks toAbout the Author

    Introduction

    Back in the late 90s, anything seemed possible. The World Wide Web and its effect on business

    was growing astronomicallykids were dropping out of college to build Web sites and become

    millionaires overnight while companies were shelling out millions of dollars for prime-time

    television commercials to have a sock puppet extol the virtues of buying pet food online. Yes, this

    was the time of the New Economy, when attracting millions of Web surfers to a Web site was

    deemed more economically valuable than selling boring old products from a stuffy building.

    At the height of these exuberant times, Microsoft introduced theAdRotator COM component for

    classic ASP, a tool that allowed budding Web entrepreneurs to easily add banner advertisements

    to their site. The first step in displaying a banner ad was to have an advertisements file listing the

    banners that could be displayed. This text file contained four bits of information about each

    banner:

  • 8/7/2019 Building a ContentRotator ASP

    2/17

    y A URL to the banner's imagey The URL that the image links toy The alternate text for the imagey The frequency by which the banner was to be shown relative to the other banners in the

    file

    Armed with this text file, all an ASP developer had to do was call the

    AdRotator'sGetAdvertisement(advertisementFile) method, passing in the path to the

    advertisements file, and the AdRotator would return the HTML markup for displaying a randomly

    selected banner advertisement from the specified file. At this point, I imagine, the money started

    rolling in.

    When ASP.NET shipped in 2002, the New Economy was officially kaput. The NASDAQ composite,

    which had peaked around 5,000 in 2000, had slumped back down to below 2,000. Despite the

    dot-com meltdown, however, Microsoft must still have been bullish on online advertising, for they

    gussied up the AdRotator control from classic ASP and released it as one of the standard Web

    controls that shipped with ASP.NET. The ASP.NET version of the AdRotator offered several

    benefits over the classic ASP version:

    y The advertisements file was now XML-formatted, making it easier for page developers tocreate and edit the file's contents.

    y Each ad could contain a keyword indicating the category to which it belonged. This, inaddition to the AdRotator's new KeywordFilter property, could be used to limit the

    set of banners displayed for a particular AdRotator control instance.

    y In addition to the standard set of advertisement properties, page developers could addtheir own additional settings in the advertisements file. These added settings could then

    be accessed programmatically by a page developer in the AdRotator'sAdCreated event.

    While the AdRotator makes it easy to randomly display a banner from a predefined list, it has anumber of shortcomings, in my opinion, two of the main ones being:

    1. The AdRotator's content can only be specified through an XML-formatted advertisementsfile. This is not a major limitation, but it would be nice if one could specify content

    declaratively through the AdRotator's markup or programmatically through source code,

    in a similar fashion to how ListItems can be specified declaratively or programmatically for

    the DropDownList Web control.

    2. The AdRotator is rather limited in the markup it can generate. As its name implies, it isdesigned to display ads, which are either text or an image linked to some URL. With a bit

    more work, the AdRotator could have been created to display anytype of content, not

    just banners or text ads.

    It is this second shortcoming that particularly bugs me, as with just a bit more work the AdRotator

    could have been made into a very generic content rotator, as opposed to being designed to

    solely display advertisements. In this article we'll right this shortcoming and others by creating a

    full-featured ContentRotator server control.

    Thinking About Content Rotation

  • 8/7/2019 Building a ContentRotator ASP

    3/17

    Before diving into any coding project, it's important to take ample time to answer the following

    three questions.

    1. Is there an existing control that accomplishes what I need? If your days are anythinglike mine, with meetings, e-mails, and other such busy work taking up more hours of the

    day than I'd like to admit, writing code is oftentimes the most fun activity of the day.

    There's nothing more enjoyable than being genuinely excited about a problem and

    finding a solution to the problem through code.

    However, "fun" and "economically viable" are two different things. It may be enjoyable to

    create a particular control, but it probably doesn't make economic sense to spend one's

    time building, testing, and tweaking the control if there already exists one that offers the

    needed functionality.

    When I find myself gearing up to create a new server control, the first thing I do is head

    over to the ASP.NET Control Gallery to see if my efforts might be in vain. A quick

    inspection of the Control Gallery shows that there's an entire category dedicated to Content

    Rotators. Many of these content rotators, however, are the type that rotate throughcontent on a single page, such as news or stock tickers.

    I found one control that rotates through arbitrary static HTML content like the AdRotator

    controlDuncan Mackenzie'sContentRotator User Control, presented in the article Rotating

    Is Fun. While Duncan's User Control offered the base functionality of the AdRotator, albeit

    with arbitrary HTML content, I decided not to use his solution as it does not provide quite

    the functionality I was looking for. (For example, Duncan's control does not allow for

    content to be tagged with a keyword.)

    In my searches I was unable to find a content rotator that met my requirements.

    Therefore, I decided to create my own. (Woo hoo! This meeting can wait, bossI have acoding project to complete now!)

    2. What functionality does my control need to provide? If you decide that you need tocreate your own control, be certain not to rush into the fun partcoding; rather, have a

    clear picture on what, exactly, you need the control to do before writing your first line of

    code. One good way to determine the control's requirements is to devise common use

    cases that describe how end users (page developers, in this case) will be using your

    control. Among the first things I ask myself when building server controls are:

    o How will page developers use this control?o What will they need to have this control work as desired, ando How should the syntax look?

    After my initial brainstorming I came up with the following four use cases:

    Harry, an up-and-coming ASP.NET developer, wants to enhance his company's intranet so

    that the homepage randomly displays a short biography and picture of one of the

    company's employees. Since Harry's company has only a dozen employees, he's content

    with hard-coding the bio and picture markup in a file for now, but would like the ability to

    upgrade this later to retrieve the items from a database as the company grows. His aim is

  • 8/7/2019 Building a ContentRotator ASP

    4/17

    to have the ContentRotator randomly select one employee from that file and display his

    information on the homepage for each visit.

    Jisun is a developer at BuyPetFoodOnline.com, a new startup that has pegged its venture

    capital on the hopes that John Q. Public is interested in buying Alpo online. All the brands

    of pet food sold at BuyPetFoodOnline.com are maintained in a Microsoft SQL Server

    database. On the site's home page, Jisun wants to display either a list of the top 10 selling

    brands of pet food, a list of all types of dog food, or a list of all types of cat food.

    Furthermore, she wants the content for the top 10 selling brands to appear, on average,

    twice as often as the dog or cat food content combined.

    Todd runs a fitness Web site and has thousands of members registered on his site, with

    information such as their names, birth dates, weights, and other fitness-related data. At

    the bottom of each page, Jim would like to display a random fitness statistic tailored to

    the visitor's personal information. For example, he'd like to have messages like the

    following to appear: [[username]], in the [[numberOfDaysSinceBirth]] days you've been

    alive, your heart has beat over [[averageHeartRateTimesDaysAlive]] times," where each of

    the placeholders are filled with values specific to the logged-on user.

    Budding ASP.NET developer Darren is very new to XML and is worried he'll make a

    mistake when specifying content items in an XML-formatted content file. Darren is

    familiar with the DropDownList Web control and used to the DropDownList's declarative

    syntax for specifying ListItems. Darren would like to be able to specify content items for

    the ContentRotator in the same manner. Similarly, he would like to be able to

    programmatically manipulate the ContentRotator's content items with syntax similar to

    that required for working with the DropDownList'sListItems programmatically.

    The features of the ContentRotator flow from these use cases, which provide direction in

    the ContentRotator's implementation.

    3. Is there a possibility for code reuse? One of the main benefits of object-orientedprogramming is the ease with which existing functionality can be incorporated and

    extended. When creating a new server control it is very likely that there already exists an

    ASP.NET server control that provides similar functionality. Is it possible to merely extend

    this existing server control, rather than build one from the ground up?Building on top of

    an existing control will likely save countless hours of coding and testing.

    Before I began creating the code for the ContentRotator, I examined the possibility of

    having the ContentRotator extend the existing ASP.NET AdRotator control. The AdRotator

    class already contains the methods and properties needed for reading in items from an

    advertising file and randomly selecting an appropriate one. Might I be able to reuse this

    class and just override the specific methods that emit a banner or text ad, crafting them

    into methods that return more generic content?

    I considered this avenue, but decided against it for a couple of reasons. First, many of the

    AdRotator's methods are not marked virtual, meaning that they cannot be overridden.

    In particular, because the methods that parse the advertisements file are not virtual, my

    derived class would have to make use of the AdRotator's existing XML format. This would

  • 8/7/2019 Building a ContentRotator ASP

    5/17

    not necessarily limit the ContentRotator's functionality (as the AdRotator can have

    arbitrary XML elements added), but it would be a cosmetic downer, since it would use the

    advertisement file's and elements. Also, the AdRotator

    requires an element, which is an unacceptable requirement for a generic

    content rotator.

    After proceeding through this thought process with prudence, I was ready to start writing the

    codefinally, the fun part! Throughout the remainder of this article I'll take you through some of

    the more interesting parts of the code for the ContentRotator control that will not only shed light

    on the inner workings of this particular control, but also provide an example for accomplishing

    similar functionality in the server controls that you create.

    Specifying Content Items

    The ContentRotator control provides three ways to specify content items:

    1. Through a separate content file in XML format.2. Through the ContentRotator's declarative syntax.3. Through server-side programmatic means.

    The first option of using an external file provides better reuse of content items, since a single

    content file can be used by many content rotators on different pages in a single Web site.

    However, there may be times where you want to quickly put up a simple ContentRotator control

    without having to bother with creating a separate content file. In these cases, you can use the

    second option and provide the content items to iterate through in the control's declarative syntax.

    The final option allows you to programmatically specify the content items. This option is useful if

    the set of possible content items needs to be dynamically selected, or if they exist in a database

    or some other nonstatic store.

    Specifying Content Through a Content File

    When specifying content items in an XML-formatted content file, the content items must be

    presented according to a particular XML schema. Specifically, the content file must start with a

    element that contains a element for each content item. Each

    item has three optional attributes:

    y impressionsspecifies the weight of the content item, which is used to determine theprobability of the item being displayed.

    y keywordspecifies the content item's keyword. The ContentRotator control contains aKeywordFilter property that, if set, limits the content items to be considered for

    display to just those with a matching keywordparameter.

    y contentPathcontent items can contain either static HTML markup or dynamic, code-driven content. If you want to make use of dynamic content you can specify the path to a

    User Control via this attribute. If the selected content item's contentPath attribute is

    set, the content is generated by the specified User Control.

  • 8/7/2019 Building a ContentRotator ASP

    6/17

    The element can also contain text that provides the static markup to display. This

    static markup is displayed if the contentPath attribute is not provided.

    The following shows an example of a properly formatted content file with four content items. The

    first content item lacks any of the optional attributes, consisting only of the text content to

    display. The second content item has both the impressions andk

    eyword attributes provided,while the third content item has just the keyword attribute set. Note that if you want to display

    HTML markup in the content item's text section you need to either XML-escape the markup as in

    the second example, by using < and > rather than < and >, or by wrapping the entire

    contents within a section. The fourth and final content item refers to a User

    Control, RichContent.ascx, which is specified through the contentPath attribute.

    Additionally, the impressions attribute is set to 5.

    Things are just average... neither positive nor negative...You will soon see a workplace promotion.

    Happiness is just around the corner!]]>

    The content file needs to be saved on the Web server's file system. To display content from a

    particular file, simply add a ContentRotator to an ASP.NET page and set its ContentFileproperty to the virtual path of the content file.

    Note Keep in mind that XML is case-sensitive, so the casing of the XML elements is important. If

    you fail to use the proper casingusing instead of, for examplethe

    content items will not be retrieved from the content file, resulting in a ContentRotator control that

    does not emit any content items.

    Specifying Content Items Declaratively

    Many of the built-in ASP.NET Web controls allow most of their properties to be specified in the

    Web control's declarative syntax. For example, one can specify the particular DataGridColumnsthat should constitute a DataGrid through the declarative syntax. The ContentRotator

    offers similar declarative syntax for specifying its content items. For each item the ContentRotator

    should consider for random display, add a element within the

    tag. Each element can contain the following

    attributes:

    y Content

  • 8/7/2019 Building a ContentRotator ASP

    7/17

    y Impressionsy Keywordy ContentPath

    These attributes map to the text portion of the element and the impressions,

    k

    eyword, andcontentPath attributes in the XML content file schema, respectively. (You canoptionally specify the Content attribute as the inner tag's text content, as shown below in the

    first two instances.) The following shows the declarative syntax for a

    ContentRotator with the same four content items as used above:

    Things are just average... neither positive nornegative...You willsoon see a workplace promotion

    Note that with the declarative syntax you do not need to escape HTML characters in the Content

    attribute.

    Specifying Content Programmatically

    To aid with the concept of content and content items, the ContentRotator includes two classes:

    y ContentItemabstractly represents a content item with properties like Content,ContentPath, Keyword, Impressions, and so on.

    y ContentItemCollectiona strongly typed collection ofContentItem instances.The ContentRotator control contains an Items property that is of type

    ContentItemCollection. You can programmatically specify the content items that the

    ContentRotator should consider when randomly selecting an item by addingContentItem

    instances to the Items property. As with other ASP.NET server controls, content added to the

    Items property is stored in the control's view state, so you need to add these items only on the

    first page visit and not on subsequent postbacks. The following code snippet shows how to

    programmatically add the same set of content items used in the earlier two examples:

    private void Page_Load(object sender, System.EventArgs e){if (!Page.IsPostBack)

    {// only need to load content items on first page visit

    they are persisted across// postbacks in the ViewState...

    ContentRotator1.Items.Add(new ContentItem("Things are just

  • 8/7/2019 Building a ContentRotator ASP

    8/17

    average... neither positive nor negative..."));

    ContentRotator1.Items.Add(new ContentItem(" You willsoon see a workplace promotion ", string.Empty, "positiveComments",3));

    ContentItemthirdCI = new ContentItem();thirdCI.Content = "Happiness is just around thecorner!";thirdCI.Keyword = "positiveComments";ContentRotator1.Items.Add(thirdCI);

    // Add dynamiccontentContentRotator1.Items.Add(new ContentItem(string.Empty,"~/RichContent1.ascx", string.Empty, 5));

    }}

    As you can see, the ContentItem class has a number of constructor overloads that can reduce

    creating a newContent

    Item

    instance and setting its properties down to just a single line of

    code.

    If you'd rather not read about the code details and would instead prefer to start using the

    ContentRotator in your ASP.NET application, feel free to skip the rest of the article and download

    the control from the link at the top of this article. The download includes the full source code for

    the ContentRotator control (in C#) along with a sample ASP.NET Web application (also in C#) that

    shows solutions for the use cases discussed above. Additionally, the download includes a spiffy-

    looking compiled help file to assist page developers who decide to use the ContentRotator

    control.

    Determining the ContentItem to Display

    Each time a page with a ContentRotator control is visited, the ContentRotator control must decide

    what content item to randomly display. Each content item has an associated impressions value

    that influences how likely it is to be selected relative to the other content items. This impressions

    parameter is a positive integer value and, if not specified, defaults to a value of 1. Additionally,

    each content item can have an optionalkeywordparameter. The ContentRotator control's

    KeywordFilter property, if specified, restricts the set of content items that are considered for

    display to those content items with a matching keywordvalue.

    The algorithm used to randomly choose a content item works by laying out each applicable

    content item end-to-end, forming a line. The length of each content item is its impressions value,

    meaning that the total length of the line is the sum of the applicable content items' impressions.

    Next, a random number less than the total length is chosen, and the content item to display is the

    one that lies at the location of the random number. Figure 1 illustrates this algorithm graphically.

  • 8/7/2019 Building a ContentRotator ASP

    9/17

    Figure 1. (Click to enlarge)

    In order to apply this algorithm the ContentRotator control first needs to retrieve the list of

    content items for consideration. Recall that this list may reside on disk as an XML file, be specified

    in the ContentRotator's declarative syntax, or be provided programmatically. Let's examine how

    this list of content items can be accessed for each of these three techniques.

    Reading Content Data from the Content File

    The ContentRotator has an Items property of type ContentItemCollection that contains the

    set of applicable content items considered by the ContentRotator control. (Recall that the set of

    applicable content items depends on whether the control'sKeywordFilter property is set and,if it is, the keywordparameters of the content items.) This Items property is populated in the

    Load event by a call to the GetFileData(virtualFilePath) method. This method returns a

    ContentItemCollection instance that containsall the content items in the content file

    specified by the virtualFilePath parameter.

    Opening, reading, and parsing the entire content file on each and every page visit would be

    inefficient and unnecessary, especially considering that the file is likely to be changed

    infrequently. To improve performance, the items in the content file are cached using a file

    dependency. This means that the items in the content file will reside in the cache for improved

    performance, but the cache item will be invalidated automatically when the underlying content

    file is modified. The following code from the GetFileData(filePath) method illustrates thiscaching behavior:

    // See if the item exists in the cachestringcacheKey = string.Concat("ContentRotateCacheKey:",physicalFilePath);

    ContentItemCollectioncachedContent = (ContentItemCollection)HttpContext.Current.Cache[cacheKey];if (cachedContent == null){

    // it's *not* in the cache, must manually get the file data andcache itcachedContent = LoadFile(physi

    calFilePath);if (cachedContent == null)

    return null;else

    // Add the content to the cacheHttpContext.Current.Cache.Insert(cacheKey, cachedContent,newCacheDependency(physicalFilePath));}

    // return the cached content

  • 8/7/2019 Building a ContentRotator ASP

    10/17

    returncachedContent;

    The variable physicalFilePath contains the physical path to the content file, and is used in

    forming the cache key. This ensures that each unique content file will have its own cache entry.

    Next, the Cache object is accessed, retrieving the value of the cache item named cacheKey. If

    this item is nulleither because no such item has been inserted into the cache or the cache

    item has become invalidatedthe contents from the content file are loaded and inserted into the

    cache along with a cache dependency based on the content file.

    The LoadFile(physicalFilePath) method iterates through the content file using an

    XPathNodeIterator, stripping out the various attributes and text content and building up a

    ContentItem instance for each item in the content file. EachContentItem instance is added to

    a ContentItemCollection, which is returned at the conclusion of the method. The following

    code shows the iteration through each item in the content file;fStream is an opened file stream

    to the content file.

    // Use an XPathNavigator to iterate through the XML elements in the

    ContentFilereader = new XmlTextReader(fStream);XPathDocumentxpDoc = new XPathDocument(reader);XPathNavigatorxpNav = xpDoc.CreateNavigator();

    XPathNodeIteratorxmlItems = xpNav.Select("/contents/content");

    XPathExpressioncontentExpr = xpNav.Compile("string(text())");XPathExpressioncontentPathExpr = xpNav.Compile("string(@contentPath)");XPathExpressionkeywordExpr = xpNav.Compile("string(@keyword)");XPathExpressionimpressionsExpr = xpNav.Compile("string(@impressions)");

    if (xmlItems == null)

    throw new FormatExc

    eption("ContentFile in invalid format.");else

    {while (xmlItems.MoveNext())

    {string content = (string) xmlItems.Current.Evaluate(contentExpr);stringcontentPath = (string)xmlItems.Current.Evaluate(contentPathExpr);string keyword = (string) xmlItems.Current.Evaluate(keywordExpr);stringimpressionsStr = (string)xmlItems.Current.Evaluate(impressionsExpr);

    int impressions = 1; // default impressions value is 1if (impressionsStr != null &&impressionsStr.Length> 0)

    impressions = Convert.ToInt32(impressionsStr,CultureInfo.InvariantCulture);

    contentItems.Add(new ContentItem(content.Trim(),contentPath, keyword, impressions));

    }}

  • 8/7/2019 Building a ContentRotator ASP

    11/17

    An XPathNodeIterator steps through each element, applying an

    XPathExpression to pick out each attribute and the text content. As each item is

    iterated, a ContentItem instance is created and its properties assigned the values from the

    XPathExpressions. Each ContentItem instance is added to a ContentItemCollection

    instance, which is later returned from the LoadFile(physicalFilePath) method.

    Reading Content Data Programmatically

    In order to programmatically add items to any type of Web control you need to perform the

    following three steps:

    1. Create a class that represents the items to be added.2. Create a class that represents a collection of the items.3. Add a property to the Web control of the type of class defined in step 2. This property

    holds the set of items for the control.

    To see these steps in action, consider the built-in ASP.NET DropDownList Web control, which

    consists of a set of items that constitute the DropDownList's available selections. Each item is

    represented by an instance of the ListItem class (step 1). The ListItemCollection class

    provides a strongly typed collection ofListItem instances (step 2), and the DropDownList

    class has an Items property of type ListItemCollection (step 3). From an ASP.NET page's

    source code portion, a DropDownList can have ListItem instances programmatically added to it

    using syntax like:

    DropDownList1.Items.Add(newListItem(text,value));

    In order to accomplish similar functionality with the ContentRotator I created the ContentItem

    class to represent a particular content item. As discussed earlier, this class has properties specific

    to a content item, such as Content, Impressions, and so on. Next, I created aContentItemCollection class that provides a strongly typed collection ofContentItem

    instances. Lastly, I created an Items property of type ContentitemCollection in the

    ContentRotator class.

    With these classes and properties created, a page developer can programmatically add content to

    the ContentRotator in syntax not unlike that of the DropDownList:

    ContentRotator1.Items.Add(new ContentItem(content));ContentRotator1.Items.Add(new ContentItem(content, contentPath,keyword, impressions));...

    Reading Content from the Control's Declarative Syntax

    In addition to being able to specify items programmatically, a number of Web controls also

    enable the page developer to specify the set of items through the control's declarative syntax. For

    example, with the DropDownList the ListItems can be spelled out declaratively like so:

  • 8/7/2019 Building a ContentRotator ASP

    12/17

    text1...

    textN

    Adding this functionality to the ContentRotator is fairly simple and straightforward since wealready have a ContentItem class defined as well as an Items property for the ContentRotator

    control. All we need to do is use two attributes to indicate that the items specified in the

    declarative syntax map to the ContentRotator'sItems property.

    First, at the class level, add the ParseChildren() attribute, indicating that the child markup

    should be parsed and that it maps to the Items property:

    [ParseChildren(true, "Items")]publicclass ContentRotator : Control{

    ...

    }

    And last, add the PersistenceMode() attribute to the Items property declaration, indicating

    that the Items property is to be persisted as the inner, default property.

    [PersistenceMode(PersistenceMode.InnerDefaultProperty]publicContentItemCollection Items{get { ... }}

    That's all there is to it! With these two attributes, a page developer can specify content items in

    the control's declarative syntax like so:

    ...

    One thing to note is that with our current code all properties of the ContentItem instance must

    be specified as attributes of the element. Ideally, a page developer would

    be able to specify static content through either the Content attribute or as the text content that

    appears between the and tags. To accomplish

    that we need to add a small bit of code to the ContentItem class indicating how the classshould parse inner text content.

    Specifically, the ContentItem class needs to implement the IParserAccessor interface,

    which defines a single method, AddParsedSubObject(object). The

    AddParsedSubObject(object) method passes in the content of the

    element in the declarative syntax. If the inner content is plain text, a LiteralControl instance

  • 8/7/2019 Building a ContentRotator ASP

    13/17

    will be passed in. In this case, we want to assign the LiteralControl'sText property to the

    Content property of the ContentItem instance. This is accomplished with the following code:

    publicclass ContentItem : IParserAccessor{

    ...

    public void AddParsedSubObject(object obj){

    if (obj is LiteralControl)Content = ((LiteralControl) obj).Text;

    elsethrow new HttpException(...);

    }#endregion

    }

    Picking a Random Content Item

    After the ContentRotator'sItems property has been retrieved in the Load event, the

    SelectContentFromItems() method is called. This method returns a randomly selected

    ContentItem instance from the Items collection. If the ContentRotator'sKeywordFilter

    property is set, it removes the nonapplicable items from the collection. It then determines the

    sum of the impressions parameters for the remaining items and chooses a random number

    between 0 and the total impressions value minus one.Based on this random number, the

    appropriate content item is selected and returned.

    The following code shows how, if the KeywordFilter property is set, the nonapplicable items

    are removed from consideration and how the sum of impressions is computed. Following this, a

    random impressions is selected and the appropriate ContentItem instance is returned.

    // Determine the sum of the ImpressionsinttotalWeight = 0, i = 0;stringcontrolsKeywordFilter = this.KeywordFilter;ContentItemCollectionfilteredArray = new ContentItemCollection();

    for (i = 0; i

  • 8/7/2019 Building a ContentRotator ASP

    14/17

    totalWeight = 0;

    // Now grab the appropriate ContentItem based on randomWeighti = 0;while (i

  • 8/7/2019 Building a ContentRotator ASP

    15/17

    Figure 2. (Click to enlarge)

    By default, the ContentRotator will randomly select a content item to display on everypage visit,

    including postbacks to the same page. If you want the same randomly retrieved content item

    displayed across postbacks, simply set the ContentRotator'sNewContentOnPostbacks to false.

    If you are using dynamic content that requires postbackssuch as a User Control that prompts

    the user for some input and, on postback, displays detailed information about that inputthen

    you will need to set NewContentOnPostbacks to false. If you leave

    NewContentOnPostbacks with its default value of true, the ContentRotator might select adifferent content item when the user posts back from the randomly selected User Control,

    thereby losing the postback data.

    In fact, regardless of whether static or dynamic content is loaded, the ContentRotator's control

    hierarchy has its view state disabled if the NewContentOnPostbacks property is set to true.

    This is because on postback the ContentRotator might choose a different content item to display

    from the page's previous visit. If the ContentRotator did not disable view state in this case, an

  • 8/7/2019 Building a ContentRotator ASP

    16/17

    exception would be thrown during the load view state stage if a different content item had been

    randomly selected. If, however, the NewContentOnPostbacks property is set to false, the view

    state for the ContentRotator's control hierarchy is maintained.

    Note For more information on loading User Controls programmatically with the

    LoadControl() method, be sure to read my earlier article,An Extensive Examination of User

    Controls. Information regarding view state and related issues can be found in Understanding

    ASP.NET View State.

    Customizing the Selected Content Item

    One of the feature requirements of the ContentRotator was to allow a page developer to specify

    dynamic placeholders in a content item. For example, a page developer should be able to create a

    content item with the content text "The current time is [[time]]," and have the [[time]] placeholder

    dynamically replaced by the current system time. I deduced that there were two approaches I

    could take to solving this problem:

    1. Provide a predefined set of placeholders that could be used for dynamic replacement.2. Allow the page developer to decide what placeholders to use and what values to replace

    them with.

    The first option would be the easier of the two to implement: I could just have a check in the

    Load event of the ContentRotator control that methodically replaced all predefined placeholders

    in the selected content item with their corresponding dynamic values. While this approach would

    be easy to implement, it would not afford the page developer nearly the level of customizability I

    thought he deserved.

    Therefore, I placed the onus of defining placeholders and injecting dynamic values squarely on

    the shoulders of the page developer. A page developer can make up whatever placeholders he'd

    like to, adding them into the text content of the content items in the content file or through the

    declarative syntax. All I needed to do was provide a mechanism for the page developer to tap into

    the selected content item. I did this by having the ContentRotator expose a ContentCreated

    event. This event is fired on each page visit whenever the content item to display is selected.

    A page developer can create an event handler for this event. In the event handler, the page

    developer will receive the ContentItem instance that was selected for display. At this point, the

    text of the content item can be searched for placeholders to be replaced with their dynamic

    values. The following example illustrates this behavior. The ContentRotator has three content

    items defined via the declarative syntax, two with dynamic placeholders.

  • 8/7/2019 Building a ContentRotator ASP

    17/17

    In the code-behind class of the ASP.NET page an event handler is created and wired up to the

    ContentRotator'sContentCreated event. The second parameter to this event handler is of type

    ContentCreatedEventArgs, which contains the selected ContentItem instance in its

    ContentItem property. As the following code shows, the event handler replaces any instances of

    [[time]] with the current system time and any instances of [[url]] with the current page's URL.

    private void ContentRotator1_ContentCreated(object sender,skmContentRotator.ContentCreatedEventArgs e){

    // Replace [[time]] with DateTime.Now.TimeOfDaye.ContentItem.Content = e.ContentItem.Content.Replace("[[time]]",DateTime.Now.TimeOfDay.ToString());

    // Replace [[url]] with Request.RawUrle.ContentItem.Content = e.ContentItem.Content.Replace("[[url]]",Request.RawUrl);}

    Note The placeholders[[time]] and [[url]] in this exampleare at the page developer's

    discretion. That is, the page developer can use whatever names and markup he chooses as

    placeholders. Rather than [[time]], for example, we could have used *currentTime* as the

    placeholder, replacing all instances of [[time]] with *currentTime* in the content file and event

    handler.

    Conclusion

    With the dot-com boom a fading memory of the past, controls like Microsoft's Ad Rotator are

    scurrying away into obscurity. However, the concept of the Ad Rotator is a good one, and there

    are many scenarios in which a generic content rotation control would prove invaluable. The

    ContentRotator control presented in this article should fit the bill for most of these situations. The

    ContentRotator provides similar semantics and syntax to that of the Ad Rotator control, includingan impressions value for content items, keyword filtering, and an event that allows page

    developers to tap into the selected content item. In addition, the ContentRotator allows for its

    content items to also be specified declaratively and allows for both static and dynamic content.

    Happy Programming!