Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

Database Driven Banner Rotator Control

4.92/5 (16 votes)
7 Jan 2010CPOL11 min read 51K   2.8K  
A database-driven banner rotator control rotating based on a specific time (and not on page refresh). Each banner will have a display time (in seconds) to move to the next one.

Introduction

In this article, I would like to present my Banner Rotator control. The idea of it is different from that of the ASP.NET AdRotator control. This control is database-driven, and banners are rotated at a specific time (not during page refresh). Each banner will have a display time (in seconds) to move to the next banner (a JavaScript does the rotation on the client side).

The source code (ASP.NET Server Control Project) and the ASP.NET website for testing the control are attached at the top of this article. I am going to explain, step by step, the following:

  • The control features
  • How to use it
  • The different ways of using the control
  • Explanation of the ASP.NET Server Control (Class Library)

The control features

  • Database-driven: Banner specifications such as width, height, and if clickable are got from the database, so is the list of banners. The banners load from the database once and they rotate on the client-side.
  • Supports most image formats (JPG, GIF, BMP, and PNG, and also Flash files (swf)).
  • Cache: User can enable caching in the control and specify the cache duration. This will reduce the load of the database.
  • Hit counter functionality for each banner.
  • Supports multiple languages.
  • Fully customizable by the developer: they can control the way it will be display, repeating vertically and horizontally, reserve the banner area even if there are no banners loaded, and specify a condition for the banner display based on the page URL, querystring and any user-defined conditions.

How to use it

  • Copy the DLL file "BannerRotatorControl.dll" into your ASP.NET application's "bin" folder.
  • Register the control in your ASPX page:
  • ASP.NET
    <%@ Register TagPrefix="BMS" Namespace="BannerRotatorControl" 
                 Assembly="BannerRotatorControl"   %>
  • Use it in your ASPX page like this:
  • ASP.NET
    <BMS:BannerRotator  runat="server" ID="banner1" 
      ConnectionString="<%$ ConnectionStrings:bms_db %>" 
      BannersLanguage="en" LocationCode="TOP" 
      BannerImagesURL="<%$ appSettings:bms_data %>" 
      BannerClickFile="~/click.aspx" ScriptsPath="/scripts/" 
      ShowOnlyActivatedBanners="true"   />

There are five mandatory properties of the BannerRotator control to be set:

  • ConnectionString: Connection string of your database that contains the banner control tables. (The files bms_db.mdf, bms_db.ldf are included in the source.)
  • BannersLanguage and LocationCode: These are mandatory properties. They are the main properties that tell the BannerRotator to read data from the database based on the following tables:
  • 03.jpg

    BMS_BANNERS_AREAS contains the list of banner areas. In the above ASP.NET control, I have set BannersLanguage="en" LocationCode="TOP" which tells the control to read the banner area record in which lang="en" and location_code="TOP" (highlighted one) and draw a banner area based on the information specified in these record fields:

    • Widthpx
    • Heightpx
    • NoAds_image: This will control the banner area to be reserved even if there are no banners for this area.

    01.jpg

    Master table of languages

    02.jpg

    Master table containing the list of locations (Location means the page location - Left, Right, Top, and Bottom)
  • BannerImagesURL: This property tells the control the banner image file location URL. I have set the property above to read from the appSettings key of the web.config:
  • XML
    <add key="bms_data" value="http://localhost/biznet-data/bms_test/{0}/banners/"/>

    "biznet-data" is a virtual directory created in IIS, and points to the folder "c:\Biznet_data\bms_test\en\banners\". Note that I have used {0} to be replaced by the banner language.

Some other properties in the above ASP.NET control code includes:

  • BannerClickFile: This will specify if the banner is clickable or not. This is the path of the file click ASPX page in your ASP.NET website. There, you have write code to update the banner click counts and redirect to the specified URL link:
  • C#
    protected void Page_Load(object sender, EventArgs e)
    {
        string urlstr = BannerRotatorControl.BannerRotator.UpdateBannerClick(
               ConfigurationManager.ConnectionStrings["bms_db"].ToString());
        if (!string.IsNullOrEmpty(urlstr))
            Response.Redirect(urlstr);
    }
  • ScriptsPath: This property is important if you plan to use Flash files in your banner control. You should specify the Flash JavaScript script path for the Flash loader:
  • 04.jpg

  • ShowOnlyActivatedBanners: Set this to true if you want to show only the activated banners, or false to show all banners (set it to false if you use the banner rotator in the Banner Management Admin site).

Different ways of using the BannerRotator control

We can use the control in different scenarios; I am going to list the most common ones:

  1. Single reserved banner area rotating multiple banners
  2. In this scenario, we want to have a banner area (468 X 60 pixels) on the top of the page which shows an "Advertising Space" image if there are no banners added in this area yet. Otherwise, it will show the banners and rotate them based on the display time (in seconds) specified for each banner.

    The BannerRotator control code will be:

    ASP.NET
    <BMS:BannerRotator  runat="server" ID="banner1" 
      ConnectionString="<%$ ConnectionStrings:bms_db %>" 
      BannersLanguage="en" LocationCode="TOP" 
      BannerImagesURL="<%$ appSettings:bms_data %>" 
      BannerClickFile="~/click.aspx" ScriptsPath="/scripts/" 
      ShowOnlyActivatedBanners="true"  Repeat="NoRepeat"  />

    06.jpg

    Based on the properties set in the control, the control will read the above record, and because the "Repeat" property is set to "NoRepeat", it will select the top record ordered by the "ls_order" field ascending. The result of the banner display will be as follows:

    bcci_en_noad468X60.jpg

    Because no banners were added yet in the "BMS_BANNERS" table under this record, and also the "NoAds_image" field is not null, this will tell the banner to reserve the area and show the image specified in that field.

    We add banner records in the "BMS_BANNERS" table under this record, as in the image below:

    07.jpg

    Based on the above records image, the banner rotator control will start displaying the banners and rotate them based on the listing order "ls_order".

    • The first banner that will be displayed is "BCCI_en_ad_eztrade468X60.jpg" for 3 seconds, and it will be clickable since "link_url" is set to "http://www.eztrade.bh/".
    • The second banner is "bcci_en_shops_eztrade.gif", displayed for 2 seconds, and it will not be clickable because "link_url" is null.
    • The last one is "bcci_en_banner1.swf" (a Flash banner), displayed for 7 seconds.
    • Then it will loop back again.
  3. Multiple banner areas with a single banner each
  4. In this scenario, we want to display four equal sized banners (202 X 96 pixels) on the left side of the page and listed vertically. They are not clickable.

    First, add four records in the "BMS_BANNERS_AREAS" table as below:

    08.jpg

    Then, add the code in your ASPX page, as below:

    ASP.NET
    <BMS:BannerRotator   runat="server" ID="BannerRotator1" 
         ConnectionString="<%$ ConnectionStrings:bms_db %>" 
         BannersLanguage="en" LocationCode="LFT" 
         ScriptsPath="/scripts/" BannerImagesURL="<%$ appSettings:bms_data %>" 
         Repeat="RepeatY" VerticalSpacing="5" 
         ShowOnlyActivatedBanners="true"/>

    The code above will read all records with location_code="LFT" and lang="en", ordered by "ls_order" because the "Repeat" property is set to "RepeatY" (list vertically). It will not reserve any banner area because all the records have null values in the "NoAds_image" field. It will only show the banners added in that area, if any.

    We add a banner record in the "BMS_BANNERS" table for each banner area above:

    09.jpg

    Based on the above records, the result of the banner control will be like this:

    10.jpg

    I have set the "VerticalSpacing" property to "5".

  5. Multiple reserved banner areas with multiple banners
  6. In this scenario, we want to have three banner areas with different sizes on the right side of the page. The first two banner areas have a size of 157 X 55 pixels, and the third one is 157 X 264 pixels. We want to list them vertically. All of them should show the "Advertising Space" image, if there are no banners added yet.

    The code will be:

    ASP.NET
    <BMS:BannerRotator runat="server" ID="BannerRotator2" 
        ConnectionString="<%$ ConnectionStrings:bms_db %>" 
        BannerImagesURL="<%$ appSettings:bms_data %>"
        BannersLanguage="en" LocationCode="RGT"  
        Repeat="RepeatY" VerticalSpacing="5"  
        ShowOnlyActivatedBanners="true" 
        BorderWidth="1" BorderColor="Red" 
        BorderStyle="Solid" BannerClickFile="~/click.aspx" />

    11.jpg

    Based on the records, the banner control will display three banner areas with the "Advertising Space" image. Because the "NoAds_image" field is not null for all of them, and there are no banners added yet for all of them, the result will be like this:

    12.jpg

    We add two records in the "BMS_BANNERS" table under the first banner area:

    13.jpg

    Base on the above records image, the banner rotator control will start displaying the banners and rotate them based on the listing order "ls_order" (first banner area).

    • First banner will be displayed is "bcci_en_btn.jpg" for 4 seconds and it will be clickable since the "link_url" is set to "http://www.bahraintradanet.com/".
    • Second banner is "bcci_en_gtn.jpg" for 6 seconds and it will be clickable since the "link_url" is set to "http://www.gulftradanet.com/".
    • Then it will loop back again.
  7. Conditionally displaying banner in a banner area
  8. In this scenario, we are going to take the first scenario and add a little advanced functionality. We want to control the display of banners based on "PageURL", "QueryString", "Session", "Cookies", and "UserDefined".

    As I mentioned above, the top banner are has three banners: banner_id=14, banner_id=15, and banner_id=16. We want to display banner_id=14 only when the page URL is "/BannerRotator_test/default.aspx" or when we have the query string "q=1".

    14.jpg

    The ASP.NET code:

    ASP.NET
    <BMS:BannerRotator BannersLanguage="en" ConnectionString="<%$ 
     ConnectionStrings:bms_db %>" LocationCode="TOP" 
     ScriptsPath="/scripts/" runat="server" ID="banner1" 
     BannerImagesURL="<%$ appSettings:bms_data %>" 
     BannerClickFile="~/click.aspx" 
     ShowOnlyActivatedBanners="true" Repeat="NoRepeat"  >
        <BMS:BannerDisplayCheck ParameterType="PageURL"   />
        <BMS:BannerDisplayCheck ParameterType="QueryString"  ParameterName="q" />
        <BMS:BannerDisplayCheck ParameterType="Session" ParameterName="my_session" />
        <BMS:BannerDisplayCheck ParameterType="Cookie" ParameterName="my_cookie" />
        <BMS:BannerDisplayCheck ParameterType="UserDefined"  />
    </BMS:BannerRotator>

    The "BannerDisplayCheck" parameters will tell the banner rotator to check in the "BMS_BANNER_DISPLAY_CRITERIAS" table and compare values and display the banner according to the matching criteria. Here are the parameter types and usage:

    • PageURL: This will tell the banner control to lookup "BMS_BANNER_DISPLAY_CRITERIAS" with a "criteria_code" equal to "PageURL" and compare the current ASPX page URL with the "criteria_value" field. If matched, then the banner_id of this row can be displayed.
    • QueryString: This will tell the banner control to lookup "BMS_BANNER_DISPLAY_CRITERIAS" with a "criteria_code" equal to "QueryString" and check the current ASPX page URL querystring name matching "ParameterName". Then, it compares the querystring value with the "criteria_value" field. If matched, then the banner_id of this row can be displayed.
    • Session: This will tell the banner control to lookup "BMS_BANNER_DISPLAY_CRITERIAS" with a "criteria_code" equal to "Session" and check if there is any session object with the same name as "ParameterName". Then it compares the session object value with the "criteria_value" field. If matched, then the banner_id of this row can be displayed.
    • Cookie: This will tell the banner control to lookup "BMS_BANNER_DISPLAY_CRITERIAS" with a "criteria_code" equal to "Cookie" and check if there is any cookie object with the same name as "ParameterName". Then it compare the cookie object value with the "criteria_value" field. If matched, then the banner_id of this row can be displayed.
    • UserDefined: This will tell the banner control to lookup "BMS_BANNER_DISPLAY_CRITERIAS" with a "criteria_code" equal to "UserDefined", and it will compare the value entered by the user in the "UserDefinedParameterValue" property of the control value with the "criteria_value" field. If matched, then the banner_id of this row can be displayed.

The class library

The idea of the control is to get banners from the database and rotate them based on a specified time for each banner. Initially, I utilised AJAX to do the job. I used the AJAX Timer and UpdatePanel controls. I used the timer tick event to display the next banner. But that was a very bad approach because it takes up a lot of resources from the server and bandwidth. The biggest disadvantage of the UpdatePanel is that it still sends all the viewstate back and forth, so you use up a lot of bandwidth. Another one's that most of the page life cycle still gets run.

I continued using AJAX with Web Services for better performance and less bandwidth usage, returning JSON data back and using JavaScript in the browser to make up the HTML and inject it in the page. This enhanced performance, but I was not happy because, again for each timer tick event, I had to read the next banner record from the database.

Finally, I came up with this control, which reads all the banners once from the database and generates HTML and JavaScript to do the rotation on the client side, using the JavaScript function "setTimeout". This is done in the control "RenderContents" event. Below is an example of the generated banner rotation code:

HTML
<div id="Banner115"></div>
JavaScript
<script type="text/javascript">

    var BannerHtml_arr15= new Array();
    var swfId_arr15 = new Array();
    var DisplayTimeSeconds_arr15 = new Array();
    
    var banner_count15 =0;
    var BannerHtml15=null;
    var swfId15=null;
    var BannerIndex15=0;

    banner_count15=3
    
    //array items generated from the database
    BannerHtml_arr15[0]="<a href='http://www.codeproject.com/BannerRotator" + 
                        "_test/click.aspx?clickid=6418cd46-791a-40e1-9c11-e3e732977" + 
                        "dc2452¶ms=PageURL%7c%2fBannerRotator_test%2fDefault" + 
                        ".aspx' target='_blank' title='Eztrade.bh'><img" + 
                        " src='http://localhost/biznet-data/bms_test/en/banners/" + 
                        "BCCI_en_ad_eztrade468X60.jpg' style='border:Solid " + 
                        "0px black;' width='468' height='60'></a>";
    swfId_arr15[0]='';
    DisplayTimeSeconds_arr15[0]='3000';
 
    BannerHtml_arr15[1]="<img src='http://localhost/biznet-data/" + 
                        "bms_test/en/banners/bcci_en_shops_eztrade.gif' style='border:" + 
                        "Solid 0px black;' width='468' height='60' title='Eztrade Shops'>";
    swfId_arr15[1]='';
    DisplayTimeSeconds_arr15[1]='2000';
 
    BannerHtml_arr15[2]="";
    swfId_arr15[2]='http://localhost/biznet-data/bms_test/en/banners/bcci_en_banner1';
    DisplayTimeSeconds_arr15[2]='7000';
 

    function DisplayBanner15()
    {
        if (BannerHtml15!=null || swfId15!=null)
        {
            if (BannerHtml15!=null) {
                document.getElementById('Banner115').innerHTML  =BannerHtml15;
                }

            else if (swfId15!=null) {
                //loading flash object using java script.
                var lcId15 = new Date().getTime();
                var flashProxy15 = new FlashProxy(lcId15, 
                    '/BannerRotator_test/scripts/JavaScriptFlashGateway.js');
                var flashObject15 = new FlashTag(swfId15+'.swf',468,60, '8,0,0,0');
                
                flashObject15.setFlashvars('lcId=' + lcId15);
                flashObject15.write(document.getElementById('Banner115'));
                }
        }


        if (banner_count15>1)
            setTimeout('DisplayBanner15()', 
                       DisplayTimeSeconds_arr15[BannerIndex15],'JavaScript')

        BannerIndex15++;

        if (BannerIndex15 >= banner_count15)
            BannerIndex15=0;

        SetValues15();
    }


    function SetValues15()
    {
        if (BannerHtml_arr15[BannerIndex15]!=null && BannerHtml_arr15[BannerIndex15]!='')
            BannerHtml15 = BannerHtml_arr15[BannerIndex15];
        else
            BannerHtml15 = null;

        if (swfId_arr15[BannerIndex15]!=null && swfId_arr15[BannerIndex15]!='')
            swfId15 = swfId_arr15[BannerIndex15];
        else
            swfId15 = null;
    }


    function LoadBanners15()
    {
        if (BannerIndex15==0)
        {
            SetValues15();
            DisplayBanner15();
        }
    }
    
    
    LoadBanners15();
</script>

Before I start, here is a screenshot of the banner control database:

05.jpg

The code is called from the "RenderContents" event of the control. Initially, we get a list of banner areas by calling the following function:

C#
protected DataTable GetBannerArea()
{
    DataTable retVal = null;


    string sql = "select ";
    //selecting a single record (first record based on order of "ls_order" column)
    if (_Repeat == RepeatOptions.NoRepeat)
        sql += "top 1 ";

    sql += " * from BMS_BANNERS_AREAS where location_code=@location_code" + 
           " and lang=@lang and Active_flag='1' order by ls_order";

    SqlConnection conn = new SqlConnection(_ConnectionString);
    SqlDataAdapter da = new SqlDataAdapter(sql, conn);

    try
    {
        da.SelectCommand.Parameters.AddWithValue("location_code", _LocationCode);
        da.SelectCommand.Parameters.AddWithValue("lang", _lang);
        DataTable dt = new DataTable();

        if (_EnableCaching)
        {
            DataTable dt_c = HttpContext.Current.Cache["AdRotator_" + 
                             _Repeat.ToString() + "_" + 
                             _LocationCode + "_" + _lang] as DataTable;
            if (dt_c != null)
                dt = dt_c;
            else
            {
                da.Fill(dt);
                HttpContext.Current.Cache.Insert("AdRotator_" + 
                     _Repeat.ToString() + "_" + _LocationCode + "_" + 
                     _lang, dt, null, 
                     DateTime.Now.AddMinutes(_CacheDuration), TimeSpan.Zero);
            }
        }
        else
            da.Fill(dt);
            da.Dispose();
            conn.Dispose();
            retVal = dt;

        }
        catch (Exception ex)
        {
            throw new Exception("Exception Accured in : protected" + 
              " void GetBannerArea(). For More Details " + 
              "See Inner Exception Message", ex);
        }
    return retVal;
}

The above function will retrieve the banner areas from the "BMS_BANNERS_AREAS" table based on "Location_code" and "Lang". Each row will define a banner area with a dimension. I call the function to get a DataTable with the results.

C#
//initially we get the list of banner areas in a datatable
DataTable dt_banner_areas = GetBannerArea();

Then, I loop through the records to render the HTML and JavaScript code. Refer to the generated example above.

C#
//loop through the banner areas
foreach (DataRow dr in dt_banner_areas.Rows)
{
    // assigning values
    _banner_area_id = Convert.ToString(dr["banner_area_id"]);
    _Width = Convert.ToString(dr["widthpx"]);
    _Height = Convert.ToString(dr["hieghtpx"]);

    if (!string.IsNullOrEmpty(Convert.ToString(dr["NoAds_image"])))
        _NoAdFile = string.Format(_bms_data_url, _lang) + 
                    Convert.ToString(dr["NoAds_image"]);

    _NoAdAlt = Convert.ToString(dr["NoAds_imageAlt"]);


    //again check the banner areas count for doing 
    //the html table. and placing the banner areas.
    if (dt_banner_areas.Rows.Count > 1)
    {
        if (_Repeat == RepeatOptions.RepeatX)
            sb.Append("" + Environment.NewLine);

        else if (_Repeat == RepeatOptions.RepeatY)
            sb.Append("" + Environment.NewLine);
    }

    //start doing the html and javascript. better you view source
    //of the aspx page to see the resulted html.
    sb.Append(@"<div id=""Banner1" + 
      _banner_area_id + @""">" + Environment.NewLine);
    sb.Append("</div>" + Environment.NewLine);

    //writing up the javasctript that will do the banner rotation.
    sb.Append(@"<script type=""text/javascript"">" + 
              Environment.NewLine);

    sb.Append(@"var BannerHtml_arr" + _banner_area_id + 
              "= new Array();" + Environment.NewLine);
    sb.Append(@"var swfId_arr" + _banner_area_id + 
              " = new Array();" + Environment.NewLine);
    sb.Append(@"var DisplayTimeSeconds_arr" + _banner_area_id + 
              " = new Array();" + Environment.NewLine);


    sb.Append(@"var banner_count" + _banner_area_id + 
              " =0;" + Environment.NewLine);

    sb.Append(@"var BannerHtml" + _banner_area_id + 
              "=null;" + Environment.NewLine);
    sb.Append(@"var swfId" + _banner_area_id + 
              "=null;" + Environment.NewLine);

    sb.Append(@"var BannerIndex" + _banner_area_id + 
              "=0;" + Environment.NewLine);

    // getting the generated javascript array list
    sb.Append(GenerateJavascriptArrayItems());


    sb.Append(@"function DisplayBanner" + 
              _banner_area_id + "()" + Environment.NewLine);
    sb.Append(@"{" + Environment.NewLine);

    // sb.Append(@"// alert(swfId" + _banner_area_id + ");" + Environment.NewLine);

    sb.Append(@"if (BannerHtml" + _banner_area_id + 
              "!=null || swfId" + _banner_area_id + 
              "!=null)" + Environment.NewLine);
    sb.Append(@"{" + Environment.NewLine);
    sb.Append(@"if (BannerHtml" + _banner_area_id + "!=null) {" + Environment.NewLine);
    sb.Append(@"document.getElementById('Banner1" + 
              _banner_area_id + "').innerHTML  =BannerHtml" + 
              _banner_area_id + ";" + Environment.NewLine);
    sb.Append(@"}" + Environment.NewLine);
    sb.Append(@"else if (swfId" + _banner_area_id + "!=null) {" + Environment.NewLine);


    sb.Append(@"//loading flash object using java script." + Environment.NewLine);
    sb.Append(@"var lcId" + _banner_area_id + 
              " = new Date().getTime();" + Environment.NewLine);
    sb.Append(@"var flashProxy" + _banner_area_id + 
              " = new FlashProxy(lcId" + _banner_area_id + ", '" + 
              VirtualPathUtility.ToAbsolute("~" + _JavaScriptFlashGateway_path + 
              "JavaScriptFlashGateway.js") + "');" + Environment.NewLine);

    sb.Append(@"var flashObject" + _banner_area_id + 
              " = new FlashTag(swfId" + _banner_area_id + 
              "+'.swf'," + _Width + "," + _Height + 
              ", '8,0,0,0');" + Environment.NewLine);
    sb.Append(@"flashObject" + _banner_area_id + 
              ".setFlashvars('lcId=' + lcId" + _banner_area_id + 
              ");" + Environment.NewLine);
    sb.Append(@"flashObject" + _banner_area_id + 
              ".write(document.getElementById('Banner1" + 
              _banner_area_id + "'));" + Environment.NewLine);
    sb.Append(@"}" + Environment.NewLine);
    sb.Append(@"}" + Environment.NewLine);

    sb.Append(@"if (banner_count" + _banner_area_id + ">1)" + Environment.NewLine);
    sb.Append(@"setTimeout('DisplayBanner" + _banner_area_id + 
              "()',DisplayTimeSeconds_arr" + _banner_area_id + 
              "[BannerIndex" + _banner_area_id + 
              "],'JavaScript')" + Environment.NewLine);
    sb.Append(@"BannerIndex" + _banner_area_id + "++;" + Environment.NewLine);
    sb.Append(@"if (BannerIndex" + _banner_area_id + 
              " >= banner_count" + _banner_area_id + 
              ")" + Environment.NewLine);
    sb.Append(@"BannerIndex" + _banner_area_id + "=0;" + Environment.NewLine);
    sb.Append(@"SetValues" + _banner_area_id + "();" + Environment.NewLine);
    sb.Append(@"}" + Environment.NewLine);

    sb.Append(@"function SetValues" + _banner_area_id + "()" + Environment.NewLine);
    sb.Append(@"{" + Environment.NewLine);

    sb.Append(@"if (BannerHtml_arr" + _banner_area_id + 
              "[BannerIndex" + _banner_area_id + 
              "]!=null && BannerHtml_arr" + _banner_area_id + 
              "[BannerIndex" + _banner_area_id + "]!='')" + Environment.NewLine);
    sb.Append(@"BannerHtml" + _banner_area_id + 
              " = BannerHtml_arr" + _banner_area_id + 
              "[BannerIndex" + _banner_area_id + 
              "];" + Environment.NewLine);
    sb.Append(@"else" + Environment.NewLine);
    sb.Append(@"BannerHtml" + _banner_area_id + " = null;" + Environment.NewLine);

    sb.Append(@"if (swfId_arr" + _banner_area_id + 
              "[BannerIndex" + _banner_area_id + 
              "]!=null && swfId_arr" + _banner_area_id + 
              "[BannerIndex" + _banner_area_id + "]!='')" + Environment.NewLine);
    sb.Append(@"swfId" + _banner_area_id + " = swfId_arr" + 
              _banner_area_id + "[BannerIndex" + 
              _banner_area_id + "];" + Environment.NewLine);
    sb.Append(@"else" + Environment.NewLine);
    sb.Append(@"swfId" + _banner_area_id + " = null;" + Environment.NewLine);


    sb.Append(@"}" + Environment.NewLine);

    sb.Append(@"function LoadBanners" + 
              _banner_area_id + "()" + Environment.NewLine);
    sb.Append(@"{" + Environment.NewLine);

    sb.Append(@"if (BannerIndex" + _banner_area_id + "==0)" + Environment.NewLine);
    sb.Append(@"{" + Environment.NewLine);
    sb.Append(@"SetValues" + _banner_area_id + "();" + Environment.NewLine);
    sb.Append(@"DisplayBanner" + _banner_area_id + "();" + Environment.NewLine);
    sb.Append(@"}" + Environment.NewLine);
    sb.Append(@"}" + Environment.NewLine);

    sb.Append(@"LoadBanners" + _banner_area_id + "();" + Environment.NewLine);
    sb.Append(@"</script>" + Environment.NewLine);
}

Note, I call the function "GenerateJavascriptArrayItems" to generate the array items for the JavaScript. This function is the one that gets all the banner records added under a particular banner area.

C#
protected string GenerateJavascriptArrayItems()
{
    string retVal = null;

    // getting list of banner within an area banner.
    // based on the "_banner_area_id"
    DataTable dt = GetBannersWithinArea();
    int i = 0;

    if (dt.Rows.Count > 0)
    {
        retVal += "banner_count" + _banner_area_id + "=" + 
                  dt.Rows.Count.ToString() + 
                  Environment.NewLine + Environment.NewLine;

        //loop through the banners
        foreach (DataRow dr in dt.Rows)
        {
            //generate the banner
            GenerateBannerHTML(dr);

            retVal += "BannerHtml_arr" + _banner_area_id + 
                      "[" + i.ToString() + "]=\"" + 
                      _BannerHtml + "\";" + Environment.NewLine;
            retVal += "swfId_arr" + _banner_area_id + "[" + 
                      i.ToString() + "]='" + _swfId + 
                      "';" + Environment.NewLine;
            retVal += "DisplayTimeSeconds_arr" + _banner_area_id + 
                      "[" + i.ToString() + "]='" + 
                      _DisplayTimeSeconds + "';" + 
                      Environment.NewLine + Environment.NewLine;
            i++;

            //nullfy the values for next banner usage
            _BannerHtml = null;
            _swfId = null;
            _DisplayTimeSeconds = 0;
        }
    }
    else
    {
        //generate and empty banner. (just the banner area block 
        //with advertizing space image if provided)
        GenerateBannerHTML(null);
        retVal += "BannerHtml_arr" + _banner_area_id + "[0]=\"" + 
                  _BannerHtml + "\";" + Environment.NewLine;
        retVal += "swfId_arr" + _banner_area_id + "[0]='" + 
                  _swfId + "';" + Environment.NewLine;
        retVal += "DisplayTimeSeconds_arr" + _banner_area_id + 
                  "[0]='" + _DisplayTimeSeconds + "';" + 
                  Environment.NewLine + Environment.NewLine;
    }
    return retVal;
}

In the above function, I call another function "GetBannersWithinArea" to get the banners of a particular banner area, then loop through the records to generate the JavaScript array items. I am calling another function "GenerateBannerHTML". This function will assign the HTML markup to the variable "_BannerHtml".

C#
protected DataTable GetBannersWithinArea()
{
    DataTable retVal = null;

    //generating sql statement for selecting the banners
    string sql = "select * from BMS_BANNERS";
    sql += " where banner_area_id=" + _banner_area_id;

    //check for banner start date and expiry date
    sql += " and CONVERT(varchar(10),expiry_date,121) " + 
           ">=CONVERT(varchar(10),getdate(),121)  ";
    sql += " and CONVERT(varchar(10),getdate(),121) " + 
           ">=CONVERT(varchar(10),start_date,121)  ";

    //apply banner display condition based 
    //on the user define parameters in the control.
    string BannerDisplaySqlConditions = GetBannerDisplaySqlConditions();
    if (!string.IsNullOrEmpty(BannerDisplaySqlConditions))
        sql += BannerDisplaySqlConditions;

    if (_ShowOnlyActivatedBanners)
        sql += " and active_flag='1' ";

    sql += " order by ls_order";

    SqlConnection conn = new SqlConnection(_ConnectionString);
    SqlDataAdapter da = new SqlDataAdapter(sql, conn);
    DataTable dt = new DataTable();

    try
    {
        //applying cache if applicable.
        if (_EnableCaching)
        {
            string c_BannerDisplaySqlConditions = null;
            if (!string.IsNullOrEmpty(BannerDisplaySqlConditions))
            {
             c_BannerDisplaySqlConditions = BannerDisplaySqlConditions.Replace(" ", "_");
             c_BannerDisplaySqlConditions = c_BannerDisplaySqlConditions.Replace("'", "_");
             c_BannerDisplaySqlConditions = c_BannerDisplaySqlConditions.Replace(">", "_");
             c_BannerDisplaySqlConditions = c_BannerDisplaySqlConditions.Replace("<", "_");
             c_BannerDisplaySqlConditions = c_BannerDisplaySqlConditions.Replace("(", "_");
             c_BannerDisplaySqlConditions = c_BannerDisplaySqlConditions.Replace(")", "_");
             c_BannerDisplaySqlConditions = c_BannerDisplaySqlConditions.Replace(":", "_");
             c_BannerDisplaySqlConditions = c_BannerDisplaySqlConditions.Replace("/", "_");
             c_BannerDisplaySqlConditions = c_BannerDisplaySqlConditions.Replace(@"\", "_");
             c_BannerDisplaySqlConditions = c_BannerDisplaySqlConditions.Replace("=", "_");
            }

            //get data from cache if exists
            DataTable dt_c = HttpContext.Current.Cache["AdRotator_" + _banner_area_id + 
                      "_" + _ShowOnlyActivatedBanners + "_" + 
                      c_BannerDisplaySqlConditions] as DataTable;
            if (dt_c != null)
                dt = dt_c;
            else
            {
                //or get it from database and store it in the cache for later retrieval.
                da.Fill(dt);
                HttpContext.Current.Cache.Insert("AdRotator_" + _banner_area_id + 
                       "_" + _ShowOnlyActivatedBanners + "_" + 
                       c_BannerDisplaySqlConditions, dt, null, 
                       DateTime.Now.AddMinutes(_CacheDuration), TimeSpan.Zero);
            }
        }
        else
            da.Fill(dt);
        da.Dispose();
        retVal = dt;
    }
    catch (Exception ex)
    {
        throw ex;
    }
    finally
    {
        conn.Dispose();
    }
    return retVal;
}

The above function will retrieve the banner records from the table "BMS_BANNERS" based on "banner_area_id".

I would also like to mention that I have implemented a properties collection. These are the "BannerDisplayCheck" parameters which I mentioned under the title "Conditionally displaying banner in a banner area" above.

For that, I have created a separate class, BannerDisplayCheck.

C#
using System;
using System.Collections;
using System.ComponentModel;
using System.Web.UI;

namespace BannerRotatorControl
{
    [TypeConverter(typeof(ExpandableObjectConverter))]
    public class BannerDisplayCheck
    {
        public enum CriteriaTypes
        {
            PageURL, QueryString, Session, Cookie, UserDefined
        }

        protected CriteriaTypes _criteraType = CriteriaTypes.PageURL;
        protected string _CriteriaName = null;

        public BannerDisplayCheck()
            : this(CriteriaTypes.PageURL, string.Empty)
        {
        }

        public BannerDisplayCheck(CriteriaTypes CriteriaType, string Name)
        {
            _criteraType = CriteriaType;
             _CriteriaName = Name;
        }

        [
        Category("Behavior"),
        DefaultValue(""),
        Description("List of Different types of parameters: " + 
                    "PageURL, QuertString,  Session, Cookie"),
        NotifyParentProperty(true),
        ]
        public CriteriaTypes ParameterType
        {
            get
            {
                return _criteraType;
            }
            set
            {
                _criteraType = value;
            }
        }


        [
         Category("Behavior"),
         DefaultValue(""),
         Description("ParameterName: exmaple: Name " + 
                     "of the QueryString, Session,Coookie"),
         NotifyParentProperty(true)
        ]
        public string ParameterName
        {
            get
            {
                return _CriteriaName;
            }
            set
            {
                _CriteriaName = value;
               
            }
        }
    }
}

Then, in my main class "BannerRotator", I have implemented the "INamingContainer" interface:

C#
//[DefaultProperty("Text")]
[ParseChildren(true, "BannerDisplayCheckCollection")]
[PersistChildren(false)]
[ToolboxData("<{0}:BannerRotator runat="server">")]
public class BannerRotator : WebControl, INamingContainer

Then the following public property will take care of collecting attributes information from the user:

C#
[
Category("Behavior"),
Description("The BannerDisplayCheck collection"),
DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
PersistenceMode(PersistenceMode.InnerDefaultProperty),
DefaultValue(null), MergableProperty(false), Bindable(false)
]
public List<BannerDisplayCheck> BannerDisplayCheckCollection
{

    get
    {
        if (_BannerDisplayCheck == null)
        {
            _BannerDisplayCheck = new List<BannerDisplayCheck>();
        }
        return _BannerDisplayCheck;
    }
}

I use the collection in the function "GetBannerDisplaySqlConditions". This function will build up the lookup part of the SQL statement in the function "GetBannersWithinArea":

C#
protected string GetBannerDisplaySqlConditions()
{
    string retVal = null;

    if (_BannerDisplayCheck.Count > 0)
    {
        retVal += " and banner_id in (select banner_id " + 
                  "from BMS_BANNER_DISPLAY_CRITERIAS where ";
        foreach (BannerDisplayCheck bdc in _BannerDisplayCheck)
        {

            switch (bdc.ParameterType)
            {
                case BannerDisplayCheck.CriteriaTypes.QueryString:
                    if (!string.IsNullOrEmpty(
                        HttpContext.Current.Request[bdc.ParameterName]))
                    {
                        retVal += "(criteria_code='" + 
                                  bdc.ParameterType.ToString() + "'";
                        retVal += " and criteria_value='" + 
                                  HttpContext.Current.Request[bdc.ParameterName] + 
                                  "') or ";
                    }

                    break;

                case BannerDisplayCheck.CriteriaTypes.PageURL:
                    retVal += "(criteria_code='" + 
                              bdc.ParameterType.ToString() + "'";
                    retVal += " and criteria_value='" + 
                              HttpContext.Current.Request.RawUrl.Split('?')[0] + 
                              "') or ";
                    break;

                case BannerDisplayCheck.CriteriaTypes.UserDefined:
                    if (!string.IsNullOrEmpty(_UserDefinedParameterValue))
                    {
                        retVal += "(criteria_code='" + 
                                  bdc.ParameterType.ToString() + "'";
                        retVal += " and criteria_value='" + 
                                  _UserDefinedParameterValue + "') or ";
                    }

                    break;

                case BannerDisplayCheck.CriteriaTypes.Session:
                    if (!string.IsNullOrEmpty(Convert.ToString(
                                HttpContext.Current.Session[bdc.ParameterName])))
                    {
                        retVal += "(criteria_code='" + 
                                  bdc.ParameterType.ToString() + "'";
                        retVal += " and criteria_value='" + 
                                  HttpContext.Current.Session[
                                       bdc.ParameterName].ToString() + 
                                  "') or ";
                    }

                    break;

                case BannerDisplayCheck.CriteriaTypes.Cookie:

                    HttpCookie cookies = 
                        HttpContext.Current.Request.Cookies[bdc.ParameterName];
                    if (cookies != null)
                    {
                        if (!string.IsNullOrEmpty(Convert.ToString(
                            HttpContext.Current.Request.Cookies[bdc.ParameterName].Value)))
                        {
                            retVal += "(criteria_code='" + 
                                      bdc.ParameterType.ToString() + "'";
                            retVal += " and criteria_value='" + 
                                      HttpContext.Current.Request.Cookies[
                                        bdc.ParameterName].Value.ToString() + 
                                      "') or ";
                        }
                    }

                    break;

                default:
                    break;
            }
        }

        //removing the last un-needed or
        retVal = retVal.Substring(0, retVal.Length - 3);

        retVal += ")";
    }

#if (debug)
    HttpContext.Current.Response.Write(retVal);
    //  HttpContext.Current.Response.End();
#endif
    return retVal;
}

Conclusion

For more details of the source code, please download the source code from the top of this article. The source code is easy and well commented. Download the demo to see the different scenarios of using the control.

Hope this control is useful, and I would like to see comments and votes.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)