Validation which accept( empty, numeric or Float values)

Now we have to discuss about the validationexpression ie:

the textbox should accept empty values , numeric ( float )

.Implementation Code :
^\d*\.?\d*$
HTML



<asp:TextBox ID="TextBox3" runat="server"></asp:TextBox>
<asp:RegularExpressionValidator ID="RegularExpressionValidator3"
        runat="server" ErrorMessage="Invalid format" 
ControlToValidate="TextBox3" ValidationExpression="^\d*\.?\d*$" 
ValidationGroup="d"></asp:RegularExpressionValidator>
<asp:Button ID="Button5" runat="server" Text="Button"
ValidationGroup="d" />

Matches

( ) -> empty value
4535
520.20
2.54
Non-Matches
21.0dfdf
47854
Rpks

If you have better solution, just tell me !

Ajax Cascading DropDownList in GridView with database ASP.NET sample

There are several cases when you have two or three dropdowns in gridview and want second one (and third one) to be populated based on selection of first and second dropdownlist.

In this example i've implemented Ajax cascading drop down list in EditItemTemaplete of GridView for updation of records in grid by fetching data from database to populate dropdowns,I've also implemented ajax auto complete extender textbox in it to edit name field.

Make sure you have created ajax enabled website and installed ajax toolkit and ajax web extensions properly.
In this example gridview displays Name, City, and Country on page load.


And City and Country field turns into cascading dropdown list when user clicks on Edit link



Ajax cascading dropdown extender uses webservice to fetch data from database and populate dropdowns, city dropdown is populated based on country selected in country dropdown.

Important points to remember
1. Put AjaxControlToolkit.dll in bin folder of your application.
2. Set EventValidation to false in page directive of your aspx page.

<%@ Page Language="C#" EnableEventValidation="false"
AutoEventWireup="true" CodeFile="Default.aspx.cs"
Inherits="_Default" %>

3. If you are getting Error method 500 or 12031 than read this to resolve this error
4. Webservice must have the webmethod with following signature and exact parameters

[WebMethod]
public CascadingDropDownNameValue[] GetColorsForModel(
string knownCategoryValues,
string category)

You can change the method name but return type must be CascadingDropDownNameValue[] with knownCategoryValues,category as parameters First of all add a new webservice and name it CascadingDropDown.asmx In code behind of this asmx file write following code.

Add these namespaces.


using System.Collections.Generic;
using System.Configuration;
using System.Data.SqlClient;
using AjaxControlToolkit;
using System.Collections.Specialized;


/// <summary>
/// Summary description for CascadingDropDown
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.Web.Script.Services.ScriptService()]
public class CascadingDropDown : System.Web.Services.WebService
{
//Create global Connection string
string strConnection = ConfigurationManager.ConnectionStrings
["dbConnectionString"].ConnectionString;

public CascadingDropDown () {

//Uncomment the following line if using designed components
//InitializeComponent();
}
/// <summary>
/// WebMethod to populate country Dropdown
/// </summary>
/// <param name="knownCategoryValues"></param>
/// <param name="category"></param>
/// <returns>countrynames</returns>
[WebMethod]
public CascadingDropDownNameValue[] GetCountries
(string knownCategoryValues, string category)
{
//Create sql connection and sql command
SqlConnection con = new SqlConnection(strConnection);
SqlCommand cmd = new SqlCommand();
cmd.Connection = con;
cmd.CommandType = System.Data.CommandType.Text;
cmd.CommandText = "Select * from Country";

//Create dataadapter and fill the dataset
SqlDataAdapter dAdapter = new SqlDataAdapter();
dAdapter.SelectCommand = cmd;
con.Open();
DataSet objDs = new DataSet();
dAdapter.Fill(objDs);
con.Close();

//create list and add items in it
//by looping through dataset table
List<CascadingDropDownNameValue> countryNames
= new List<CascadingDropDownNameValue>();
foreach (DataRow dRow in objDs.Tables[0].Rows)
{
string countryID = dRow["CountryID"].ToString();
string countryName = dRow["CountryName"].ToString();
countryNames.Add(new CascadingDropDownNameValue
(countryName, countryID));
}
return countryNames.ToArray();

}

[WebMethod]
public CascadingDropDownNameValue[] GetCities
(string knownCategoryValues, string category)
{
int countryID;
//this stringdictionary contains has table with key value
//pair of cooountry and countryID
StringDictionary countryValues =
AjaxControlToolkit.CascadingDropDown.
ParseKnownCategoryValuesString(knownCategoryValues);
countryID = Convert.ToInt32(countryValues["Country"]);

SqlConnection con = new SqlConnection(strConnection);
SqlCommand cmd = new SqlCommand();
cmd.Connection = con;
cmd.CommandType = System.Data.CommandType.Text;
cmd.Parameters.AddWithValue("@CountryID", countryID);
cmd.CommandText =
"Select * from City where CountryID = @CountryID";

SqlDataAdapter dAdapter = new SqlDataAdapter();
dAdapter.SelectCommand = cmd;
con.Open();
DataSet objDs = new DataSet();
dAdapter.Fill(objDs);
con.Close();
List<CascadingDropDownNameValue> cityNames =
new List<CascadingDropDownNameValue>();
foreach (DataRow dRow in objDs.Tables[0].Rows)
{
string cityID = dRow["CityID"].ToString();
string cityName = dRow["CityName"].ToString();
cityNames.Add(new CascadingDropDownNameValue
(cityName, cityID));
}
return cityNames.ToArray();
}

}

Now in html source of aspx page I've put two dropdowns in Edit Item Templates of grid view

<EditItemTemplate>
<asp:DropDownList ID="ddlCountry" runat="server">
</asp:DropDownList><br />
<ajaxToolkit:CascadingDropDown ID="CascadingDropDown1"
runat="server"
Category="Country"
TargetControlID="ddlCountry"
PromptText="-Select Country-"
LoadingText="Loading Countries.."
ServicePath="CascadingDropDown.asmx"
ServiceMethod="GetCountries">
</ajaxToolkit:CascadingDropDown>
</EditItemTemplate>

Here TargetControlID is id of dropdown on which cascading dropdown is to be implemented, Service path is path to webservice and ServiceMethod is method to fetch the data from databse and populate dropdown. You also need to add reference to webservice in script manager

<asp:ScriptManager ID="ScriptManager1" runat="server">
<Services>
<asp:ServiceReference Path="CascadingDropDown.asmx" />
</Services>
</asp:ScriptManager>

The complete html source is like this

<%@ Page Language="C#" EnableEventValidation="false"
AutoEventWireup="true" CodeFile="Default.aspx.cs"
Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server">
<Services>
<asp:ServiceReference Path="AutoComplete.asmx" />
<asp:ServiceReference Path="CascadingDropDown.asmx" />
</Services>
</asp:ScriptManager>
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<asp:GridView ID="GridView1" runat="server"
AutoGenerateColumns="False"
DataSourceID="SqlDataSource1"
OnRowUpdating="GridView1_RowUpdating">

<Columns>
<asp:CommandField ShowEditButton="True" />
<asp:TemplateField HeaderText="ID" SortExpression="ID">
<ItemTemplate>
<asp:Label ID="lblID" runat="server"
Text='<%#Eval("ID") %>'>
</asp:Label>
</ItemTemplate>
<EditItemTemplate>
<asp:Label ID="lblID" runat="server"
Text='<%#Bind("ID") %>'>
</asp:Label>
</EditItemTemplate>
</asp:TemplateField>

<asp:TemplateField HeaderText="Name"
SortExpression="Name">
<ItemTemplate>
<asp:Label ID = "lblName" runat="server"
Text='<%#Eval("Name") %>'>
</asp:Label>
</ItemTemplate>
<EditItemTemplate>
<asp:TextBox ID="txtName" runat="server"
Text='<%#Bind("Name") %>' >
</asp:TextBox>
<ajaxToolkit:AutoCompleteExtender
runat="server"
ID="autoComplete1"
TargetControlID="txtName"
ServicePath="AutoComplete.asmx"
ServiceMethod="GetCompletionList"
MinimumPrefixLength="1"
CompletionInterval="10"
EnableCaching="true"
CompletionSetCount="12" />
</EditItemTemplate>
</asp:TemplateField>

<asp:TemplateField HeaderText="Country"
SortExpression="Country">
<ItemTemplate>
<asp:Label ID="lblCountry" runat="server"
Text='<%#Eval("Country") %>'>
</asp:Label>
</ItemTemplate>
<EditItemTemplate>
<asp:DropDownList ID="ddlCountry" runat="server">
</asp:DropDownList>
<ajaxToolkit:CascadingDropDown
ID="CascadingDropDown1"
runat="server"
Category="Country"
TargetControlID="ddlCountry"
PromptText="-Select Country-"
LoadingText="Loading Countries.."
ServicePath="CascadingDropDown.asmx"
ServiceMethod="GetCountries">
</ajaxToolkit:CascadingDropDown>
</EditItemTemplate>
</asp:TemplateField>

<asp:TemplateField HeaderText="City"
SortExpression="City">
<ItemTemplate>
<asp:Label ID="lblCity" runat="server"
Text='<%#Eval("City") %>'>
</asp:Label>
</ItemTemplate>
<EditItemTemplate>
<asp:DropDownList ID="ddlCity" runat="server"
OnSelectedIndexChanged="ddlCity_SelectedIndexChanged">
</asp:DropDownList><br />
<ajaxToolkit:CascadingDropDown
ID="CascadingDropDown2"
runat="server"
Category="City"
TargetControlID="ddlCity"
ParentControlID="ddlCountry"
PromptText="-Select City-"
LoadingText="Loading Cities.."
ServicePath="CascadingDropDown.asmx"
ServiceMethod="GetCities">
</ajaxToolkit:CascadingDropDown>
</EditItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
</ContentTemplate>
</asp:UpdatePanel>
<div>

<asp:SqlDataSource ID="SqlDataSource1" runat="server"
ConnectionString="<%$ ConnectionStrings:dbConnectionString %>"
SelectCommand="SELECT [ID], [Name], [City],[Country]
FROM [Location]"

UpdateCommand="Update Location set [Name] = @Name,
[City] = @City,[Country] = @Country
where ID = @ID"
>
<UpdateParameters>
<asp:Parameter Name="Name" />
<asp:Parameter Name="ID" />
<asp:Parameter Name="Country"/>
<asp:Parameter Name="City"/>
</UpdateParameters>
</asp:SqlDataSource>

</div>
</form>
</body>
</html>

Finally write this code in code behind of aspx page to update record

protected void GridView1_RowUpdating
(object sender, GridViewUpdateEventArgs e)
{
//Find dropdown to get selected Item text
DropDownList ddlGridCountry = (DropDownList)
GridView1.Rows[e.RowIndex].FindControl("ddlCountry");
string strCountry =
ddlGridCountry.SelectedItem.Text.ToString();

DropDownList ddlGridCity = (DropDownList)
GridView1.Rows[e.RowIndex].FindControl("ddlCity");
string strCity =
ddlGridCity.SelectedItem.Text.ToString();

SqlDataSource1.UpdateParameters.Clear();
SqlDataSource1.UpdateParameters.Add
("Country", strCountry);
SqlDataSource1.UpdateParameters.Add("City", strCity);
}

Hope this helps !

ASP.NET Add AutoNumber Column in GridView or DataList

Send Email using Gmail ASP.NET C# VB.NET

If you want to send email using your Gmail account or using Gmail's smtp server in ASP.NET application or if you don't have a working smtp server to send mails using your ASP.NET application or aspx page than sending e-mail using Gmail is best option.

First of all add below mentioned namespace in code behind of aspx page from which you want to send the mail.

using System.Net.Mail;
Now write this code in click event of button
C# code
protected void Button1_Click(object sender, EventArgs e)
{
MailMessage mail = new MailMessage();
mail.To.Add("jainamit.agra@gmail.com");
mail.To.Add("amit_jain_online@yahoo.com");
mail.From = new MailAddress("jainamit.agra@gmail.com");
mail.Subject = "Email using Gmail";
string Body = "Hi, this mail is to test sending mail"+
"using Gmail in ASP.NET";
mail.Body = Body;
mail.IsBodyHtml = true;
SmtpClient smtp = new SmtpClient();
smtp.Host = "smtp.gmail.com";
smtp.Credentials = new System.Net.NetworkCredential
("YourUserName@gmail.com","YourGmailPassword");
smtp.EnableSsl = true;
smtp.Send(mail);
}

VB.NET code

Imports System.Net.Mail
Protected Sub Button1_Click
(ByVal sender As Object, ByVal e As EventArgs)
Dim mail As MailMessage = New MailMessage()
mail.To.Add("jainamit.agra@gmail.com")
mail.To.Add("amit_jain_online@yahoo.com")
mail.From = New MailAddress("jainamit.agra@gmail.com")
mail.Subject = "Email using Gmail"
String Body = "Hi, this mail is to test sending mail"+
"using Gmail in ASP.NET"
mail.Body = Body
mail.IsBodyHtml = True
Dim smtp As SmtpClient = New SmtpClient()
smtp.Host = "smtp.gmail.com"
smtp.Credentials = New System.Net.NetworkCredential
("YourUserName@gmail.com","YourGmailPassword")
smtp.EnableSsl = True
smtp.Send(mail)
End Sub

Change YourUserName@gmail.com to your gmail ID and YourGmailPassword to Your password for Gmail account and test the code.

Than you need to check your Gmail username and password.

Hope this helps

Integration Testing Your ASP.NET MVC Application

Let’s start with a quick recap:

  • Unit tests test individual software components. They supply mocks or fake versions of dependencies (such as a database) so that the unit test doesn’t rely on any external code and any failures can be pinpointed exactly. Unit testing is central to Test Driven Development – the entire TDD process is driven by unit testing.

  • Integration tests test your entire software stack working together. These tests don’t mock or fake anything (they use the real database, and real network connections) and are good at spotting if your unit-tested components aren’t working together as you expected. In general, it’s best to put most of your effort into building a solid suite of unit tests, and then adding a few integration tests for each major feature so you can detect any catastrophic incompatibilities or configuration errors before your customers do.

ASP.NET MVC famously has great support for unit testing. That’s well covered elsewhere on the web, so I won’t write more about it here. But what about integration testing?

These tools host a real web browser and automate a series of navigation steps, clicking on buttons and letting you write assertions about what should appear on the screen. Browser automation is fully capable of testing your JavaScript code just as easily as your business logic. However, there are also some drawbacks to browser automation:

  • Browser automation test scripts are very fragile. As soon as you alter the HTML in your views, the tests may start failing and sometimes all need to be re-recorded. Many developers are happy with browser automation at first, but after a few months are sick of re-recording the tests or maintaining thousands of lines of scripts, and give up on it.

  • Browser automation test scripts rely on parsing the generated HTML, which loses all the strongly-typed goodness of your MVC model objects.

Introducing MvcIntegrationTestFramework

What if you could write NUnit tests (or xUnit or whatever you prefer) that directly submit a URL, query string, cookies, request headers, etc., into your application – without needing the app to be hosted in any web server but still running in the real (non-mocked) ASP.NET runtime – and get back the response text to make assertions about? In fact, what if you also got back the ActionResult (e.g., a ViewResult) that was executed, a reference to any unhandled exception it threw, and could also write assertions about cookie values or the contents of Session? What if you could string together a series of test requests to simulate a user’s browsing session, checking that a whole feature area works from end to end?
Well, my friend, all this can be yours with MvcIntegrationTestFramework

A Simple Integration Test Example
Consider the following action method:


public ActionResult Index()
{
ViewData["Message"] = "Welcome to ASP.NET MVC!";
return View();
}

You can write an integration for this using MvcIntegrationTestFramework as follows:


[Test]
public void Root_Url_Renders_Index_View()
{
appHost.SimulateBrowsingSession(browsingSession => {
// Request the root URL
RequestResult result = browsingSession.ProcessRequest("/");

// You can make assertions about the ActionResult...
var viewResult = (ViewResult) result.ActionExecutedContext.Result;
Assert.AreEqual("Index", viewResult.ViewName);
Assert.AreEqual("Welcome to ASP.NET MVC!", viewResult.ViewData["Message"]);
// ... or you can make assertions about the rendered HTML
Assert.IsTrue(result.ResponseText.Contains("<!DOCTYPE html")

This is pretty similar to a typical unit test for that action method, except that you also get access to the finished rendered HTML, in case that interests you. Unlike a unit test, this integration test goes through the entire request-processing pipeline, so it tests your routing configuration, your controller factory, any dependencies your controller has, and even your view template.


[Test]
public void LogInProcess()
{
string securedActionUrl = "/home/SecretAction";

appHost.SimulateBrowsingSession(browsingSession => {
// First try to request a secured page without being logged in
RequestResult initialRequestResult = browsingSession.
        ProcessRequest(securedActionUrl);
string loginRedirectUrl = initialRequestResult.Response.RedirectLocation;
Assert.IsTrue(loginRedirectUrl.StartsWith
       ("/Account/LogOn"), "Didn't redirect to logon page");

// Now follow redirection to logon page
string loginFormResponseText = browsingSession.ProcessRequest(loginRedirectUrl).ResponseText;
string suppliedAntiForgeryToken = MvcUtils.ExtractAntiForgeryToken(loginFormResponseText);

// Now post the login form, including the verification token
RequestResult loginResult = browsingSession.ProcessRequest(loginRedirectUrl,
HttpVerbs.Post, new NameValueCollection
{
{ "username", "steve" },
{ "password", "secret" },
{ "__RequestVerificationToken", suppliedAntiForgeryToken }
});
string afterLoginRedirectUrl = loginResult.Response.RedirectLocation;
Assert.AreEqual(securedActionUrl, afterLoginRedirectUrl, "Didn't redirect back to SecretAction");

// Check that we can now follow the redirection back to the protected action, and are let in
RequestResult afterLoginResult = browsingSession.ProcessRequest(securedActionUrl);
Assert.AreEqual("Hello, you're logged in as steve", afterLoginResult.ResponseText);
});
}

In this test, multiple requests are made as part of the same browsing session. The integration testing framework will preserve cookies (and therefore session state) from one request to the next, so you can test interaction with things like Forms Authentication and ASP.NET MVC’s AntiForgeryToken helpers.

This has advantages over browser automation in that (in my opinion) it’s easier to write this test – it’s concise, you don’t need to learn a browser scripting language, and you don’t need to rerecord the script if you restructure the HTML. You can make assertions at the HTML level, or you can make assertions at the ActionResult level, with fully strongly-typed access to any ViewData or Model values that were being supplied.

On the flip side, browser automation is still the only way to test your JavaScript.

Integration Testing Your Own ASP.NET MVC Application

To add these kinds of tests to your own ASP.NET MVC application, create a new class library project called MyApp.IntegrationTests or similar. Add a reference to MvcIntegrationTestFramework.dll, which you can get by downloading the demo project and compiling it, and also add a reference to your chosen unit testing framework (e.g., NUnit or xUnit).

Next, if you’re using NUnit, create a basic test fixture class as follows:


using MvcIntegrationTestFramework.Hosting;
using MvcIntegrationTestFramework.Browsing;

[TestFixture]
public class MyIntegrationTests
{
// Amend this path to point to whatever folder contains your ASP.NET MVC application
// (i.e., the folder that contains your app's main web.config file)
private static readonly string mvcAppPath = Path.GetFullPath( AppDomain.CurrentDomain.BaseDirectory + "\\..\\..\\..\\MyMvcApplication");
private readonly AppHost appHost = new AppHost(mvcAppPath);
}

Also – and seriously you can’t skip this step – you must add a post-build step so that each time you compile, Visual Studio will copy the assemblies from your integration test project’s \bin folder into your main ASP.NET MVC application’s \bin folder. I’ll explain why this is necessary later.


image

The test fixture you just created tells MvcIntegrationTestFramework to find your ASP.NET MVC application at the specified disk location, and to host it (without using IIS or any other web server). The resulting AppHost object then provides an API for issuing requests to your application.

[TestFixture]public class MyIntegrationTests{ // Leave rest as before [Test] public void HomeIndex_DemoTests(){ appHost.SimulateBrowsingSession(browsingSession => {
RequestResult result = browsingSession.ProcessRequest("home/index");             
// Routing config should match "home" controller            
Assert.AreEqual("home", result.ActionExecutedContext.RouteData.Values["controller"]);             
// Should have rendered the "index" view            
ActionResult actionResult = result.ActionExecutedContext.Result;            
Assert.IsInstanceOf(typeof(ViewResult), actionResult);            
Assert.AreEqual("index", ((ViewResult)actionResult).ViewName);             
// App should not have had an unhandled exception            
Assert.IsNull(result.ResultExecutedContext.Exception);  
}); }}

Conclusion

Personally, I find this approach to integration testing more productive than browser automation in most cases, because the tests are fast to write and are less prone to break because of trivial HTML changes. I wouldn’t want to have thousands of integration tests, though – for one thing, they’d take ages to run – but end-to-end testing for key scenarios like logging in, registering, going through shopping carts, etc., proves pretty convincingly that the major functional areas of your app are working overall, leaving unit tests to prove the fine detail of each component.

If you have better solution, just tell me !

Mouse pointer as ASP.NET AJAX progress indicator

Update: If you’re looking for something more graphical, also see my example of using a

CSS style as AJAX progress indicator

The ASP.NET AJAX UpdatePanel control provides us a quick and easy way to AJAX enable websites without changing our familiar design patterns. It’s certainly much better than using full postbacks in many situations.

However, it lacks usability. While the user waits on the async postback to occur, they are left without any of the usual indicators. We’ve spent decades training our users to wait when they see an hourglass icon. Why throw all that away for a spinning Web 2.0 progress indicator that means little to an average user?

Luckily, the ASP.NET AJAX framework provides us with tools to correct this shortcoming.

We’ll start with a run of the mill UpdatePanel setup:

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" 
Inherits="_Default" %>
<html>
<body>
<div id="Container">
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server" />
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<asp:Label ID="Label1" runat="server"
Text="Update Me" />
<br /><br />
<asp:Button ID="Button1" runat="server"
OnClick="Button1_Click" Text="Button" />
</ContentTemplate>
</asp:UpdatePanel>
</form>
</div>
</body>
</html>

public partial class _Default : System.Web.UI.Page
{
protected void Button1_Click(object sender, EventArgs e)
{
Thread.Sleep(3000);
Label1.Text = DateTime.Now.ToString();
}
}

This is how it works with just an UpdatePanel:

First, we need to expand the Container div to fill the entire page using CSS:

html,body
{
height:99%
}

#Container
{
height:99%;
min-height:99%;
}

html>body #outer
{
height:auto
}

Next, we need to hook and handle the initializeRequest and endRequest events exposed by the ASP.NET AJAX framework, using this JavaScript embedded in the page anywhere after the ScriptManager control:

<script language="javascript">
var prm = Sys.WebForms.PageRequestManager.getInstance();

prm.add_initializeRequest(InitializeRequest);
prm.add_endRequest(EndRequest);

function InitializeRequest(sender, args)
{
$get('Container').style.cursor = 'wait';
}

function EndRequest(sender, args)
{
$get('Container').style.cursor = 'auto';
}
</script>
To further enhance usability, we can disable the update button for the duration of the async postback:

<script language="javascript">
var prm = Sys.WebForms.PageRequestManager.getInstance();

prm.add_initializeRequest(InitializeRequest);
prm.add_endRequest(EndRequest);

function InitializeRequest(sender, args)
{
$get('Container').style.cursor = 'wait';
// Get a reference to the element that raised the postback,
// and disables it.
$get(args._postBackElement.id).disabled = true;
}
function EndRequest(sender, args)
{
$get('Container').style.cursor = 'auto';
// Get a reference to the element that raised the postback
// which is completing, and enable it.
$get(sender._postBackSettings.sourceElement.id).disabled = false;
}
</script>
Now, we have a much better user experience:

Of course, this is only the tip of the iceberg. The code could use some refinement, but it demonstrates the concept well.

ASP.NET AJAX Timer Trouble? Location is key.

If you’ve made much use of the ASP.NET AJAX Timer control, you may have noticed that it can behave somewhat unexpectedly. In this post, I’m going to take a closer look at how the Timer works and the most significant factor that influences it: Location.

Where the timer is placed on the page actually varies how it operates. As a Timer’s delay interval approaches the processing time of its resulting PostBack, the difference that the Timer’s location makes becomes very significant.

That said, I know that you’re probably still going to be using them even when you shouldn’t. So, let’s take a closer look at how the Timer actually works.

The Timer control’s underlying mechanism

It’s important to understand that the Timer control is simply an elaborate abstraction for combining JavaScript’s setTimeout and ASP.NET’s __doPostBack.

Assuming that UpdatePanel1’s OnLoad event was handled in the same way that the Timer1’s OnTick was handled, a Timer declared like this:

<asp:Timer runat="server" id="Timer1" Interval="5000" />

Could be very roughly approximated by JavaScript such as:

function pageLoad() {
window.setTimeout("__doPostBack('UpdatePanel1', '')", 5000);
}

There’s nothing magic about the Timer control. It attempts to approximate the Timer control that we’re used to in WinForms development, but is still subject to the limitations of the stateless HTTP protocol.

Keeping this in mind, let’s look at two cases. In both, the Timer will have an Interval of five seconds and will trigger a partial postback taking four seconds to complete. However, due to Timer placement, the actual time between updates in these two cases will differ by around 80%.

Case 1: A Timer inside UpdatePanel content

protected void Timer1_Tick(object sender, EventArgs e)
{
System.Threading.Thread.Sleep(4000);
Literal1.Text = DateTime.Now.ToLongTimeString();
}

This is probably the easiest way of implementing a Timer. You can just drop it in the UpdatePanel, create an OnTick handler, and not worry about setting up triggers or anything else.

However, even though the Timer interval is very clearly specified as five seconds, this UpdatePanel will actually take just over nine seconds for each Tick to fire. The reason for this large discrepancy is the location of the Timer. Take a look at the partial postback response, and it becomes clear why:

Timer response when contained in an UpdatePanel

Because the Timer is inside the UpdatePanel’s ContentTemplate, it will be reinitialized every time the UpdatePanel refreshes. It actually comes within one second of triggering another OnTick event, but is reset by the partial postback’s return.

This may or may not be desirable in a given scenario, but can be fairly unexpected.

Case 2: A Timer outside UpdatePanel content

<asp:ScriptManager runat="server" ID="ScriptManager1" />
<asp:Timer runat="server" ID="Timer1"
Interval="5000" OnTick="Timer1_Tick" />
<asp:UpdatePanel runat="server" ID="UpdatePanel1">
<Triggers>
<asp:AsyncPostBackTrigger ControlID="Timer1" EventName="Tick" />
</Triggers>
<ContentTemplate>
<asp:Literal runat="server" ID="Literal1" />
</ContentTemplate>
</asp:UpdatePanel>

protected void Timer1_Tick(object sender, EventArgs e)
{
System.Threading.Thread.Sleep(4000);
Literal1.Text = DateTime.Now.ToLongTimeString();
}

Implementing the Timer this way removes the reinitialization interference, but exposes another potential problem. Since the Timer ticks are now fixed at an absolute five second interval, only one second less than our partial postback requires to complete, the UpdatePanel spends nearly all of its time in async postback.

Depending on your application this may be exactly what you want, but it also might be very detrimental to the usability and performance of the application. For example, if you were to locate a one second Timer outside an UpdatePanel that took two seconds to refresh, it would never manage to complete a single update.

If you have better solution, just tell me


Create New ASP.NET 2.0 with ASP.NET AJAX 1.0 Projects

ASP.NET AJAX 1.0 Multi-Targeting Support

VS 2008 out of the box allows you to open and edit existing ASP.NET 2.0 applications built with the separate ASP.NET AJAX 1.0 download we shipped last year. The VS 2008 multi-targeting support works just fine with these projects, and you can use the improved JavaScript and web designer support with them - while still targeting .NET 2.0 and ASP.NET AJAX 1.0.

New ASP.NET AJAX 1.0 Project Templates for VS 2008

Out of the box VS 2008 doesn't include project templates for creating brand new ASP.NET 2.0 with ASP.NET AJAX 1.0 applications. Right before Christmas we shipped a web free web download for VS 2008 that enables these project templates options. You can download them here (note: you also need to make sure you have ASP.NET AJAX 1.0 installed on your machine in order to use them).

Once these additional project templates are installed, you can use File->New Project or File->New Web Site within VS 2008 to create ASP.NET AJAX 1.0 applications that run on ASP.NET 2.0:

New ASP.NET AJAX 1.0 Web Site:

New ASP.NET AJAX 1.0 Web Application:

Applications built using these project templates do not require .NET 3.5 to be installed on a server in order to work - you can copy them to any existing web server that has .NET 2.0 and ASP.NET AJAX 1.0 installed and they will work fine.

Source: Scott