Ramani Sandeep's Blog

DotNetting – Fast , Easy Way of Developing Applications

Archive for January, 2009

Capcha Image Code in C#

Posted by Ramani Sandeep on January 29, 2009

Introduction

CAPTCHA stands for “completely automated public Turing test to tell computers and humans apart.” What it means is, a program that can tell humans from machines using some type of generated test. A test most people can easily pass but a computer program cannot.

You’ve probably encountered such tests when signing up for an online email or forum account. The form might include an image of distorted text, like that seen above, which you are required to type into a text field.

The idea is to prevent spammers from using web bots to automatically post form data in order to create email accounts (for sending spam) or to submit feedback comments or guestbook entries containing spam messages. The text in the image is usually distorted to prevent the use of OCR (optical character reader) software to defeat the process. Hotmail, PayPal, Yahoo and a number of blog sites have employed this technique.

This article demonstrates how to create such an image and employ it within an ASP.NET web form.

Using the code

The code file contains the source for one class and two web forms. To use it, just create a new web project and add those items.

* CaptchaImage.cs – defines the CapchaImage object which actually creates the image.
* Default.aspx, Default.aspx.cs – a sample web form.
* JpegImage.aspx, JpegImage.aspx.cs - a web form designed to output a JPEG image rather than HTML.

Let’s look at each component and it’s purpose.

CaptchaImage.cs

The CaptchaImage object creates an image given parameters for the text to be displayed, the image dimensions and, optionally, the font to use.The heart of the code is the GenerateImage() method, shown below, which creates a bitmap image of the specified width and height. This method is called from the CaptchaImage constructor, so the image is ready as soon as you create a new instance of the object.To create the image, we first fill in the background using a hatched brush (the “dirtier” the image appears, the harder it is for an OCR program to read it).

To make the text fit within the image, we start with an initial font size based on the image height and use the Graphics.MeasureString() method to find the resulting dimensions of the drawn text. If the text exceeds the image dimensions, we reduce the font size and test again and again until a suitable font size is found.

// ===========================================

// Creates the bitmap image.

// ===========================================

private void GenerateImage()
{
// Create a new 32-bit bitmap image.

Bitmap bitmap = new Bitmap(
this.width,
this.height,
PixelFormat.Format32bppArgb);

// Create a graphics object for drawing.

Graphics g = Graphics.FromImage(bitmap);
g.SmoothingMode = SmoothingMode.AntiAlias;
Rectangle rect = new Rectangle(0, 0, this.width, this.height);

// Fill in the background.

HatchBrush hatchBrush = new HatchBrush(
HatchStyle.SmallConfetti,
Color.LightGray,
Color.White);
g.FillRectangle(hatchBrush, rect);

// Set up the text font.

SizeF size;
float fontSize = rect.Height + 1;
Font font;
// Adjust the font size until the text fits within the image.

do
{
fontSize–;
font = new Font(
this.familyName,
fontSize,
FontStyle.Bold);
size = g.MeasureString(this.text, font);
} while (size.Width > rect.Width);

// Set up the text format.

StringFormat format = new StringFormat();
format.Alignment = StringAlignment.Center;
format.LineAlignment = StringAlignment.Center;

// Create a path using the text and warp it randomly.

GraphicsPath path = new GraphicsPath();
path.AddString(
this.text,
font.FontFamily,
(int) font.Style,
font.Size, rect,
format);
float v = 4F;
PointF[] points =
{
new PointF(
this.random.Next(rect.Width) / v,
this.random.Next(rect.Height) / v),
new PointF(
rect.Width – this.random.Next(rect.Width) / v,
this.random.Next(rect.Height) / v),
new PointF(
this.random.Next(rect.Width) / v,
rect.Height – this.random.Next(rect.Height) / v),
new PointF(
rect.Width – this.random.Next(rect.Width) / v,
rect.Height – this.random.Next(rect.Height) / v)
};
Matrix matrix = new Matrix();
matrix.Translate(0F, 0F);
path.Warp(points, rect, matrix, WarpMode.Perspective, 0F);

// Draw the text.

hatchBrush = new HatchBrush(
HatchStyle.LargeConfetti,
Color.LightGray,
Color.DarkGray);
g.FillPath(hatchBrush, path);

// Add some random noise.

int m = Math.Max(rect.Width, rect.Height);
for (int i = 0; i < (int) (rect.Width * rect.Height / 30F); i++)
{
int x = this.random.Next(rect.Width);
int y = this.random.Next(rect.Height);
int w = this.random.Next(m / 50);
int h = this.random.Next(m / 50);
g.FillEllipse(hatchBrush, x, y, w, h);
}

// Clean up.

font.Dispose();
hatchBrush.Dispose();
g.Dispose();

// Set the image.

this.image = bitmap;
}

Once the font is set, we define a GraphicsPath() which essentially converts the text to a set of lines and curves. This can then be distorted using the GraphicsPath.Warp() method with some randomly generated values. The effect is similar to holding a cardboard sign up by opposite corners and giving it a bit of a twist. The resulting path is drawn onto the image, again using a hatch brush to give it a “dirty” appearance.

To complete the distortion, small blots are randomly painted over the image. You could experiment with other effect to thwart OCRs, but keep in mind that it should still be legible to humans, some of whom may have visual impairments

Default.aspx

This is a very simple sample web form that contains only a few basic elements, namely an <IMG> tag for the image, a text box and a “Submit” button.

<form id=”Default” method=”post” runat=”server”>
<img src=”JpegImage.aspx”><br>
<p>
<strong>Enter the code shown above:</strong><br>
<asp:TextBox id=”CodeNumberTextBox” runat=”server”></asp:TextBox>
<asp:Button id=”SubmitButton” runat=”server” Text=”Submit”>
</asp:Button><br>
</p>
<p>
<em class=”notice”>
(Note: If you cannot read the numbers in the above<br>
image, reload the page to generate a new one.)</em>
</p>
<p><asp:Label id=”MessageLabel” runat=”server”></asp:Label></p>
</form>

Note that the SRC attribute of the <IMG> tag points to the web form JpegImage.aspx.

The code-behind for Default.aspx simply generates a random text string for the image and validates that this text was entered by the user when the form is submitted.

The key is to store the text string in the Session object.

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

// Create a random code and store it in the Session object.

this.Session["CaptchaImageText"] = GenerateRandomCode();

else
{
// On a postback, check the user input.

if (this.CodeNumberTextBox.Text ==
this.Session["CaptchaImageText"].ToString())
{
// Display an informational message.

this.MessageLabel.CssClass = “info”;
this.MessageLabel.Text = “Correct!”;
}
else
{
// Display an error message.

this.MessageLabel.CssClass = “error”;
this.MessageLabel.Text = “ERROR: Incorrect, try again.”;

// Clear the input and create a new random code.

this.CodeNumberTextBox.Text = “”;
this.Session["CaptchaImageText"] = GenerateRandomCode();
}
}
}

The reason for storing the text string in the Session object is so that it can be accessed by JpegImage.aspx.

JpegImage.aspx

For this web form, no HTML is needed (what’s there is just the default code generated by Visual Studio when the file was created). Instead of HTML, the code will produce a JPEG image.In the code-behind, we first create a CaptchaImage object, using the text retrieved from the Session object. This creates a bitmap image for us.

private void Page_Load(object sender, System.EventArgs e)
{
// Create a CAPTCHA image using the text stored in the Session object.

CaptchaImage ci = new CaptchaImage(
this.Session["CaptchaImageText"].ToString(),
200, 50, “Century Schoolbook”);

// Change the response headers to output a JPEG image.

this.Response.Clear();
this.Response.ContentType = “image/jpeg”;

// Write the image to the response stream in JPEG format.

ci.Image.Save(this.Response.OutputStream, ImageFormat.Jpeg);

// Dispose of the CAPTCHA image object.

ci.Dispose();
}

We then modify the HTTP response headers to set the Content-type to “image/jpeg” so the client browser will know we are sending an image.The last step is to retrieve the bitmap image from CaptchaImage.Image and write it to the HTTP response output stream in JPEG format. Fortunately, the Save() method of the Bitmap object makes this simple. Any other supported image format could be used as well so long as the Content-type header is set accordingly.

Posted in ASP.NET, C# 2.0 | Tagged: , , , | 10 Comments »

Recursive Queries in SQL Server 2005

Posted by Ramani Sandeep on January 26, 2009

One of the really killer features included in SQL Server 2005 was Common Table Expressions. One of the really nice uses for them is recursive queries. Imagine any kind of hierarchical set of date (org. chart, security which allows nested roles, parts/assemblies, etc.). You can use CTE’s to walk up or down these trees to build it’s result set. Let’s look at a simple example of this. I’m going to create a table named “ItemGroups” which is nothing more than a listing of items which have a PK, Title, Description, and foreign key to a parent it may be a child of.

CREATE TABLE [dbo].[ItemGroups](
[iid] [int] IDENTITY(1,1) NOT NULL,
[Title] [varchar](60) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
[Description] [text] COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[fk_ItemGroups] [int] NULL,
CONSTRAINT [PK_ItemGroups] PRIMARY KEY CLUSTERED
(
[iid] ASC
)WITH (PAD_INDEX  = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

Then, I’m going to add some sample data to this table:

———————————————————————————————–
iid     Title                                       Description                     fk_ItemGroups
———————————————————————————————–
1       Root                                         This is the root                                            NULL
2       Child 1                                    This is a child of root                              1
3       Child 2                                  This is a child of root                               1
4       Grandchild 1                     This is a child of Child 1                        2
5       Grandchild 2                    This is a child of Child 2                       3
6       Great Grandchild 1      This is a child of Grandchild 1          4
———————————————————————————————-

If we draw this out as a “tree”, it would look something like this (note the modern looking ASCII art…)

  • Root
    • Child 1
      • Grandchild 1
        • Great Grandchild 1
    • Child 2
      • Grandchild 2

OK, great – let’s suppose we want to walk up or down this tree from a known starting point. How might we use a CTE to do that?
It might help to understand the basic format of a CTE:

WITH SomeTableName (List of resulting fields)
(
SELECT — Starting point or anchor of the query
UNION ALL
SELECT — Recursive portion of the query
)
SELECT — Final select from SomeTableName

We have the “WITH” portion which describes what our CTE cursor would look like (we can reference this in the recursive portion of the query and in the final SELECT). Then we have the first SELECT which selects the starting record(s) for the recursive portion of our query. It’s basically the starting point. The second SELECT pulls in matching records which are children or parents of the record in the anchor portion.

Let’s see what that would look like against our table, assuming we want to walk down the hierarchy – in this code, we’re going to be starting with the root node.

DECLARE @startNode int
SET @startNode = 1; -- Note the semicolon – it’s required for the commandimmediately before the CTE

WITH Items (iid, Title, Description, fk_ItemGroups) AS
(
	-- This is the ‘Anchor’ or starting point of the recursive query
		SELECT ig.iid,
			ig.Title,
			ig.Description,
			ig.fk_ItemGroups
		FROM ItemGroups ig
		WHERE ig.iid = @startNode
	UNION ALL -- This is the recursive portion of the query
		SELECT ig.iid,
			ig.Title,
			ig.Description,
			ig.fk_ItemGroups
		FROM ItemGroups ig
		INNER JOIN Items -- Note the reference to CTE table name
		ON ig.fk_ItemGroups = Items.iid
)
SELECT * FROM Items

If we run this, here’s our results (notice that the query automatically stops recursing when no more matches are found).

————————————————————————————————
iid     Title                                        Description                            fk_ItemGroups
————————————————————————————————
1       Root                                         This is the root                                           NULL
2       Child 1                                   This is a child of root                              1
3       Child 2                                  This is a child of root                              1
5       Grandchild 2                   This is a child of Child 2                        3
4       Grandchild 1                    This is a child of Child 1                         2
6       Great Grandchild 1      This is a child of Grandchild 1           4
———————————————————————————————–

If we change the starting node to 2 and rerun this, you’ll see we only get Child 1 and it’s children:

——————————————————————————————
2       Child 1                                  This is a child of root                                1
4       Grandchild 1                    This is a child of Child 1                          2
6       Great Grandchild 1      This is a child of Grandchild 1           4

—————————————————————————————–

And if we change it to start at Grandchild 1, we get:

—————————————————————————————
4       Grandchild 1                    This is a child of Child 1                       2
6       Great Grandchild 1      This is a child of Grandchild 1        4

—————————————————————————————

What if we’d like to walk “up” the hierarchy instead? That’s just as easy. In the recursive portion of the query, we need to change our join condition. The first query will return the record we want to start on (aliased as ‘Item’ in this example). To walk up the chain, our fk_ItemGroups will match our parents iid. So change the ON to: ” Items.fk_ItemGroups = ig.iid”.

Let’s rerun the last query:

——————————————————————————
4       Grandchild 1      This is a child of Child 1             2
2       Child 1                    This is a child of root                   1
1       Root                          This is the root                              NULL

——————————————————————————

It might be useful to know how many levels deep of recursion were required to retrieve a row. We can modify our query to include this info by adding a new column, “Level”. In our root query we set it to start at 0, and we increment it in the recursive portion of the query:

SET @startNode = 4; -- Note the semicolon – it’s required for the command immediately before the CTE

WITH Items (iid, Title, Description, fk_ItemGroups, [Level]) AS
(
	--This is the ‘Anchor’ or starting point of the recursive query
		SELECT ig.iid,
			ig.Title,
			ig.Description,
			ig.fk_ItemGroups,
			0 AS Level
		FROM ItemGroups ig
		WHERE ig.iid = @startNode
	UNION ALL --This is the recursive portion of the query
		SELECT ig.iid,
			ig.Title,
			ig.Description,
			ig.fk_ItemGroups,
			Items.Level + 1
		FROM ItemGroups ig
		INNER JOIN Items -- Note the reference to CTE table name
		ON Items.fk_ItemGroups = ig.iid
)
SELECT * FROM Items

———————————————————————————————–
iid     Title                          Description                     fk_ItemGroups      Level
———————————————————————————————–
4       Grandchild 1        This is a child of Child 1     2                                     0
2       Child 1                       This is a child of root          1                                       1
1       Root                            This is the root                        NULL                           2

——————————————————————————————–

I’ve mostly ignored the final SELECT * FROM Items, but in a “real” query you tend to use this portion of it to pull in all your detail from various supporting tables.In a few cases I’ve found that I’ve actually needed to walk up and down a hierarchy from a given starting point. I’ve ended up just creating two CTEs – one to walk up and one to walk down the hierarchy. I insert the results of each of them into a temp. variable, then pull the final results.< portion. anchor the in record of parents or children are which records matching pulls SELECT second The point. starting basically It?s query. our portion recursive for record(s) selects first have we Then SELECT). final and query this reference can (we like look would cursor CTE what describes ?WITH?>

DECLARE @curItems TABLE (iid int);

-- Walks up the hierarchy
WITH Items (iid]) AS
(
		--This is the ‘Anchor’ or starting point of the recursive query
		SELECT ig.iid
		FROM ItemGroups ig
		WHERE ig.iid = @startNode
	UNION ALL -- This is the recursive portion of the query
		SELECT ig.iid
		FROM ItemGroups ig
		INNER JOIN Items -- Note the reference to CTE table name
		ON Items.fk_ItemGroups = ig.iid
)

INSERT INTO @curItems (iid) (SELECT iid FROM Items);

-- Walks down the hierarchy
WITH Items (iid]) AS
(
		-- This is the ‘Anchor’ or starting point of the recursive query
		SELECT ig.iid
		FROM ItemGroups ig
		WHERE ig.iid = @startNode
	UNION ALL -- This is the recursive portion of the query
		SELECT ig.iid
		FROM ItemGroups ig
		INNER JOIN Items -- Note the reference to CTE table name
		ON ig.fk_ItemGroups = Items.iid
)

INSERT INTO @curItems (iid) (SELECT iid FROM Items)

-- Code which does final select here

DECLARE @curItems TABLE (iid int);

-- Walks up the hierarchy
WITH Items (iid]) AS
(
		-- This is the ‘Anchor’ or starting point of the recursive query
		SELECT ig.iid
		FROM ItemGroups ig
		WHERE ig.iid = @startNode
	UNION ALL -- This is the recursive portion of the query
		SELECT ig.iid
		FROM ItemGroups ig
		INNER JOIN Items -- Note the reference to CTE table name
		ON Items.fk_ItemGroups = ig.iid
)

INSERT INTO @curItems (iid) (SELECT iid FROM Items);

-- Walks down the hierarchy
WITH Items (iid]) AS
(
		-- This is the ‘Anchor’ or starting point of the recursive query
		SELECT ig.iid
		FROM ItemGroups ig
		WHERE ig.iid = @startNode
	UNION ALL -- This is the recursive portion of the query
		SELECT ig.iid
		FROM ItemGroups ig
		INNER JOIN Items -- Note the reference to CTE table name
		ON ig.fk_ItemGroups = Items.iid
)

INSERT INTO @curItems (iid) (SELECT iid FROM Items)

As you can see, it’s pretty simple to use CTE’s. The syntax looks a little weird at first but once you’ve written one or two queries it’s pretty straightforward.

Posted in SQL Server | Tagged: , , | 1 Comment »

Scrolling News Web Control using ASP.Net and C#

Posted by Ramani Sandeep on January 6, 2009

On many of the web portals/sites it is observed that latest news start scrolling from right to left.

To do this need to write JavaScript in detail, there could be number of storing news format. I have written this web control to avoid extensive use of JavaScript by using Marquee at server side.

I have tested this application on following browsers:

1. Internet Explorer 6.0
2. Netscape Navigator 7.2
3. Mozilla Firefox 1.5

So no need to worry much about cross browser performance issue, it work simply great on all above browsers.

Requirement:

Need To make following tables in SQL Server Database, and put corresponding records in the tables.

News Table

create table News
(
newsId  int  primary key identity(1,1) Not null,
newTitle varchar(50) Not null,
dateCreated datetime
)

The code behind of page i.e. on page load I have following code

News.ascx.cs

protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            //Fill News Section
            FillNews();
        }
    }

    private void FillNews()
    {
        // Fetch News Records from Database and Store in Datatable…

        string news = null;

        for (int i = 0; i < DataTable.Rows.Count; i++)
        {
            news = news + DataTable.Rows[i]["News"].ToString();
            news = news + "  ||  ";
        }

        lblNews.Text = news;
    }

Which just prepare a connection with the database and get the newsTitle and dateCreated to scroll in the marquee in direction=’UP’. I have prepared a string which can be added dynamically in a table row to fit in appropriate table row, which generates scrolling news section with anchor (link) to open detail in the new window. Idea behind opening in new window to stick the user to same site. You can do following with the marquee and JavaScript functionality used in the same.

  • Can stop on mouseover of the link [OnMouseOver='this.stop();']
  • Can start on mouseout of the link [OnMouseOut='this.start();']
  • Can controll the speed of the scrolling [direction='up' scrollamount='2']

Register this control as below in a file in which you want to use this

<%@  Control Language="C#" AutoEventWireup="true" CodeFile="News.ascx.cs" Inherits="UserControls_News" %>
<table width="100%" cellspacing="0px" cellpadding="0px" class="NewsBgColor">
    <tr>
        <td class="Table_TD_Center">
            <marquee behavior="SCROLL" width="100%" scrolldelay="100″" onmouseover='this.stop();'
                onmouseout='this.start();'>
                <asp:Label ID="lblNews" runat="server" Text="" CssClass="RedLabel">
                </asp:Label>
            </marquee>
        </td>
    </tr>
</table>

That’s all … Happy Programming

Posted in ASP.NET, C# 2.0 | Tagged: , | 11 Comments »

AdRotator Using Database

Posted by Ramani Sandeep on January 1, 2009

Introduction
——————-
This article explains the concept of Asp.Net AdRotator control fetching ad information from the database and rotates the ads on certain time interval. This article also gives a tip to fetch ad information from an XML file.

The AdRotator Control presents ad images each time a user enters or refreshes a webpage. When the ads are clicked, it will navigate to a new Web location. Each time the page is loaded into the browser, an ad is randomly selected from a predefined list. Previously the predefined list means an XML File, which contains the information about the ads to be displayed. But in Asp.Net 2.0 we can maintain the list in any data source. In this article, we explained the AdRotator control to fetch ads information from both database and XML file, and display those ads in randomly with the help of Timer control. You can say it as an AJAX AdRotator Control.

AdRotator with XML File
———————————–
Open a project as Asp.Net Ajax enabled website in Microsoft Visual Studio 2005, drag and drop an UpdatePanel and add an AdRotator control and Timer Control into it. Set the UpdateMode property of UpdatePanel to conditional. Set the Timer Control Interval property to your desired time. The Interval is in milliseconds. Double click in the Timer Control, it will create a Timer1_Tick event in your code-behind. Add a single line of code as follows

protected void Timer1_Tick(object sender, EventArgs e)
{
UpdatePanel1.Update();
}

Now add an XML file into your project, name it as Ads.xml and specify the AdvertisementFile property of the AdRotator control to point this XML file. So your AdRotator control’s html code will like like

<asp:AdRotator ID=”AdRotator1″ runat=”server” AdvertisementFile=”~/Ads.xml” />

Now we will fill the Ads.xml file with advertisement information. The AdvertisementFile must have the following attributes.

1. ImageUrl: The location of the ad image file.
2. NavigateUrl: The Web location to navigate to when the image ad is clicked.
3. AlternateText: The text to provide as the ALT attribute of the image control when the ad is rendered. This can also be seen as ToolTip.
4. Keyword: Here you can specify some keyword related with the ads image to fetch.
5. Impressions: An integer number to specify the weight of the ad. If the number is larger, then more time the image ad will appear.

The ImageUrl is the only mandatory attribute, rest is optional. Let us have a look on the XML Advertisement file we create for you.
<?xml version=”1.0″ encoding=”utf-8″ ?>
<Advertisements>
<Ad>
<ImageUrl>http://www.anyserver.com/ads/859866.jpg</ImageUrl>
<NavigateUrl>
http://www.anyserver.com</NavigateUrl>
<AlternateText>anyserver.com</AlternateText>
<Keyword>Servers</Keyword>
<Impressions>80</Impressions>
</Ad>
<Ad>
<ImageUrl>http://www.yourserver.com/ads/309548.gif</ImageUrl>
<NavigateUrl>http://www.yourserver.com</NavigateUrl>
<AlternateText>YourServer.com</AlternateText>
<Keyword>MyServer</Keyword>
<Impressions>80</Impressions>
</Ad>
</Advertisements>

That’s it. Easy and save your project. View your project in browser, you can see the rotation of ads in your webpage, at the interval you fixed in the Timer Control.

Disadvantage of AdRotator with XML File
———————————————————–

One of the main disadvantage of AdRotator when functioning with XML file is real time maintainance. Yes if you want to add any new ads or you want to remove any existing ads is not an easy job when your ads is large in number. Everytime to have to find and replace or remove the appropriate tags from the xml file and then you have to upload to the server which is time consuming and hectic. To overcome this difficulty, Asp.Net 2.0 introduce the concept of builing AdRotator control with database. Let us all jump to study how it is easy to integrate an AdRotator with database rather than an XML file.

AdRotator with Database
———————————–

AdRotator control can be bind with any data source. For this article, we shall take MS SQL Server as our data source. We need to create a Table with the following columns in it.

Create Table YourAdRotatorTableName
(
AlternateText VarChar(100),
ImageUrl VarChar(100),
NavigateUrl VarChar(200)
)

These columns in the table are already fixed with the AdRotator control’s attributes. So we no need to specify it in the property tab of the AdRotator control. In case, if you change the column name in the database table, then you have to specify it property in the AlternateTextField, ImageUrlField, NavigateUrlField and KeywordFilter attributes of the AdRotator control.

But I don’t know why ImpressionField is missing in the property tab.

To start with, add another Aspx file to your project, add a ScriptManager and an UpdatePanel. Drag and drop an AdRotator with a Timer Control into the UpdatePanel. Set the Interval property to your desired time, which is in milliseconds. In the code-behind file, let us write a method to fetch ads information from the above table.

private DataTable FetchAdsFromDB()
{
string sql = “select * from YourAdRotatorTableName”;
SqlDataAdapter da = new SqlDataAdapter(sql, );
DataTable dt = new DataTable();
da.Fill(dt);
return dt;
}

The above method fetches ads information from the table and returns it as a database. You have to mention the DataSource of the AdRotator to this method in your Page_Load event.

AdRotator1.DataSource = FetchAdsFromDB();
AdRotator1.DataBind();

We should not forget the Timer Control and the AJAX functionality to be implemented with this AdRotator. So double click on the Timer control to create an event Timer1_Tick in your code-behind file. Just bind your FetchAdsFromDB method again to the

AdRotator control. Save the changes and run your application, you can see the Ads Rotating in your webpage.

protected void Timer1_Tick(object sender, EventArgs e)
{
AdRotator1.DataSource = FetchAdsFromDB();
AdRotator1.DataBind();
}

Advantages of AdRotator with Database

The main advantage of AdRotator with database is easy maintainance of ads. You can activate or deactivate any time you want. You can manage n-number of Ads in you application with any ads size.

Posted in ASP.NET | Tagged: , | 5 Comments »

 
Follow

Get every new post delivered to your Inbox.

Join 317 other followers