Introduction
It is very common that we come across an action item, or a use case, or a screen interface that requires a process status, and the general development strategy is to show a please-wait icon/image on the screen, and the page gets post back once the process is completed. This article/document is designed to help the developer to make a real time visual process page to the user, giving a rich user interface. I will discuss a very simple approach of implementing this using AJAX in an ASP.NET web application. And, the good thing is that you do not have to install or upgrade your existing development environment to an AJAX Framework or use extenders, and this can be implemented with the existing XMLHttpRequestBrowser
object.
Most of you already know that AJAX stands for Asynchronous JavaScript and XML. To my best knowledge, Microsoft used to refer this as DHTML in the past, and later this concept/technology become popular as AJAX. I am sure you have come across AJAX in your development process or in various online articles. Simply type AJAX at any search engine, and it will bring back tons of articles related to AJAX and its implementation, which is not the scope of this article/document.
Brief Description
This is a simple AJAX approach that includes an HTML page client and a JavaScript making an AJAX call and receiving a response. A remote page will receive the parameter information and respond to the JavaScript call method with the necessary information. This concept will provide a real time user interface for an order processing screen or a stock evaluation screen based on the fundamental and technical analysis of the stock. I have used this to evaluate a list of my stocks, which is the basic idea of this article. Let's say, you have a list of order items and all the order items have to be processed at the same time. Generally, users want to see a progress/status for each order item as they start processing, and you can design your application so that the user will see an indicator next to the current order item as it starts the process. Once the order process request is completed, a successful completion image is shown next to the order time, as shown in the following diagrams:
Diagram-1: Showing the list of order items
Diagram-2: Once the Submit Order button is clicked, the page starts it process without a postback, and you can see the status of the process as shown above.
Diagram-3: You can change the visual presentation. I am showing the wrong image as the Failed status for the order item framework.
Implementation and Using the Code
To keep it simple, I will walk through the code and the necessary HTML pages. I have separated all the AJAX based JavaScript methods and functions to a .js file and included that file as the source in the HTML pages.
I have separated the GetXmlHttpObject
and implemented it on the HTML page, just to keep it simple for the reader. Following are the steps to implement:
- Create a “default.aspx” using Visual Studio (I have used Visual Studio 2005, but you can try this using any version, or even an HTML/text editor should be fine).
- Create a second page called “startproc.aspx”. (A remote page that will receive the request from the AJAX call from the JavaScript, and will respond with the necessary information.)
- Create a third page called “endproc.aspx”. (You can skip this page completely and make use of the “startproc.aspx” with a different implementation, but to keep it simple, I have used this page.)
- Create a .js file and name it “ajaxsrc.js”.
- Create an Images directory and add three images.
Default.aspx
This is the only page that is used as the user interface. The AJAX callback method will request information from the remote page by passing the order item information, and changes the state immediately once it receives information back from the remote page, without making any postback calls.
Step 1: Add the following code to “default.aspx”:
<script language="javascript"
type="text/javascript" src="ajaxsrc.js">
</script>
<script language="javascript" type="text/javascript">
var xmlHttp;
function submitOrder(){
if(navigator.appName == 'Netscape'){
alert('I have only tried to work with IE browser, but i am sure' +
' you can figure it out to make it work for other then ' +
'IE browser.\n You can use XmlHttpRequest object to support ' +
'cross-browser.\nHope this helps.');
}
else{
ProcessOrder('startproc.aspx');
}
}
function ProcessOrder(pagename){
var url = null;
url = pagename;
document.getElementById('btnOrder').disabled = true;
try{
xmlHttp = GetXmlHttpObject(CallbackMethod);
SendXmlHttpRequest(xmlHttp, url);
}
catch(e){}
}
function getOrder(item){
var iimg = item.indexOf(':');
var sitem = item.substring(iimg+1, item.length);
try{
if(item != 'done'){
if(item.search(/Img:/) == 0){
var img1 = document.getElementById('Img' + sitem);
img1.src = document.getElementById('ImgProc').src;
img1.style.visibility = 'visible';
ProcessOrder('endproc.aspx');
}
else if(item.search(/tick:/) == 0){
var r_img = document.getElementById('ImgR').src;
var img1 = document.getElementById('Img' + sitem);
img1.src = r_img;
img1.style.visibility = 'visible';
ProcessOrder('startproc.aspx');
}
else if(item.search(/error:/) == 0){
var w_img = document.getElementById('ImgW').src;
var img = document.getElementById('Img' + sitem);
img.src = w_img;
img.style.visibility = 'visible';
ProcessOrder('startproc.aspx');
}
else if(item == 'syerror:'){
alert('System Error Occured');
return;
}
else
alert('unknown Error');
return;
}
else{
alert('Request Processed Successfully.');
document.getElementById('btnOrder').disabled = false;
return;
}
}
catch(e){}
}
function CallbackMethod(){
try{
if (xmlHttp.readyState == 4 || xmlHttp.readyState == 'complete'){
var response = xmlHttp.responseText;
if (response.length > 0){
getOrder(response);
}
}
}
catch(e){}
}
</script>
//head
//Body
<form id="form1" runat="server">
<div>
<table style="position: relative;" border="0"
cellpadding="1" cellspacing="1">
<tr>
<td colspan="2">Process my Order:</td>
</tr>
<tr>
<td style="width:10px;"></td>
<td valign="top">
<asp:Panel ID="pnlOrder" runat="server"
Style="position: relative"></asp:Panel>
</td>
</tr>
<tr>
<td></td>
<td><input type="button" value="Submit Order"
id="btnOrder" name="btnOrder"
onclick="submitOrder();"/></td>
</tr>
</table>
</div>
<asp:Image ID="ImgW" runat="server" ImageUrl="images/wrong.gif"
Style="position: relative;visibility:hidden;" />
<asp:Image ID="ImgR" runat="server" ImageUrl="images/right.gif"
Style="position: relative;visibility:hidden;"/>
<asp:Image ID="ImgProc" runat="server" ImageUrl="images/indicator.gif"
Style="position: relative;visibility:hidden;"/>
</form>
Default.aspx.cs
The code-behind file is used to generate the order collection and display it on the screen. I have used the code-behind to frame the order grid using the HTML table and a static order collection. You can access a database or a file object and populate the DataTable
to replace the static order collection.
Step 2: Add the following code to “default.aspx.cs”.
public partial class _Default : System.Web.UI.Page {
protected void Page_Load(object sender, EventArgs e){
fillSecurityOrder();
}
protected void fillSecurityOrder(){
string headerStyle = "font-weight:bold;background-color:" +
" #99CCFF;font-family:Trebuchet MS;";
string rowevenStyle = "background-color: #FBEFE1;" +
"font-family:Trebuchet MS;font-size: 9pt;";
string rowoddStyle = "background-color: #F9FDDD;" +
"font-family:Trebuchet MS;font-size: 9pt;";
Table theTable = new Table();
theTable.BorderStyle = BorderStyle.Solid;
theTable.BorderWidth = 1;
theTable.CellPadding = 1;
theTable.CellSpacing = 1;
theTable.Width = Unit.Pixel(220);
TableRow theRow = new TableRow();
TableCell theCell1 = new TableCell();
TableCell theCell2 = new TableCell();
theCell1.Text = "Order Item";
theCell1.Style.Value = headerStyle;
theCell2.Text = "Status";
theCell2.Style.Value = headerStyle;
theRow.Controls.Add(theCell1);
theRow.Controls.Add(theCell2);
theTable.Rows.Add(theRow);
DataTable tblOrderCollection = OrderCollection();
int iStyle = 0;
string applyStyle = string.Empty;
foreach (DataRow dr in tblOrderCollection.Rows){
iStyle++;
int imod = iStyle % 2;
if (imod == 1) applyStyle = rowoddStyle;
else applyStyle = rowevenStyle;
TableRow Row = new TableRow();
TableCell CellOne = new TableCell();
TableCell CellTwo = new TableCell();
CellOne.Controls.Add(createItem(dr["sOrderItem"].ToString()));
CellOne.Style.Value = applyStyle;
CellOne.Height = 20;
CellTwo.Controls.Add(createImage(dr["sOrderItem"].ToString()));
CellTwo.Style.Value = applyStyle;
CellTwo.Height = 20;
Row.Controls.Add(CellOne);
Row.Controls.Add(CellTwo);
theTable.Controls.Add(Row);
}
Session["OrderBucket"] = tblOrderCollection;
pnlOrder.Controls.Add(theTable);
}
protected Label createItem(string Ticker){
Label theLabel = new Label();
theLabel.ID = Ticker;
theLabel.Text = Ticker;
return theLabel;
}
protected Image createImage(string Ticker){
Image theImage = new Image();
theImage.ID = "img" + Ticker;
theImage.ImageUrl = "images/indicator.gif";
theImage.Style["visibility"] = "hidden";
return theImage;
}
protected DataTable OrderCollection(){
DataTable theDataTable = new DataTable();
theDataTable.Columns.Add(new DataColumn("sOrderItem", typeof(string)));
theDataTable.Columns.Add(new DataColumn("isProc", typeof(Int32)));
DataRow drOne;
drOne = theDataTable.NewRow();
drOne["sOrderItem"] = ".NET";
drOne["isProc"] = 0;
theDataTable.Rows.Add(drOne);
DataRow drTwo;
drTwo = theDataTable.NewRow();
drTwo["sOrderItem"] = "Ajax";
drTwo["isProc"] = 0;
theDataTable.Rows.Add(drTwo);
DataRow drThree;
drThree = theDataTable.NewRow();
drThree["sOrderItem"] = "Framework";
drThree["isProc"] = 0;
theDataTable.Rows.Add(drThree);
DataRow drFour;
drFour = theDataTable.NewRow();
drFour["sOrderItem"] = "Code Project";
drFour["isProc"] = 0;
theDataTable.Rows.Add(drFour);
return theDataTable;
}
}
Startproc.aspx
This page is used to receive the current order item and notify the AJAX callback method that the order item is received and that the user can be notified. You can change an image/icon, or a message, or a status symbol to let the user feel that the current order item has now started processing. When you add this page to your project, do not make any modifications or changes to the HTML design as this is a remote page and the user interface is not handled in any client object in this project.
Step 3: Add the following to “startproc.aspx.cs”:
public partial class startproc : System.Web.UI.Page{
protected void Page_Load(object sender, EventArgs e){
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Clear();
if (Session["OrderBucket"] != null){
DataTable theTable = (DataTable)Session["OrderBucket"];
foreach (DataRow dr in theTable.Rows){
int iproc = Convert.ToInt32(dr["isProc"].ToString());
if (iproc == 0){
dr["isProc"] = 2;
theTable.GetChanges();
Session["OrderBucket"] = theTable;
Response.Write("Img:" + dr["sOrderItem"].ToString());
Response.End();
}
}
Response.Write("done");
Response.End();
}
}
}
Endproc.aspx
This page is used to receive the current order item information stating that the order item shall be processed, and to respond to the AJAX callback method on the default.aspx page. This page is separated from startproc.aspx only to show how a start and end process is handled within the process. When you add this page to your project, do not make any modifications or changes to the HTML design as this is a remote page and the user interface is not handled in any client object in this project.
Step 4: Add the following code to “endproc.aspx.cs”:
public partial class endproc : System.Web.UI.Page{
protected void Page_Load(object sender, EventArgs e){
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Clear();
if (Session["OrderBucket"] != null){
DataTable theTable = (DataTable)Session["OrderBucket"];
foreach (DataRow dr in theTable.Rows){
int iproc = Convert.ToInt32(dr["isProc"].ToString());
if (iproc == 2){
dr["isProc"] = 1;
theTable.GetChanges();
System.Threading.Thread.Sleep(2000);
Session["OrderBucket"] = theTable;
if (string.Compare(dr["sOrderItem"].ToString(), "Framework") == 0)
Response.Write("error:" + dr["sOrderItem"].ToString());
else
Response.Write("tick:" + dr["sOrderItem"].ToString());
Response.End();
}
}
Response.Write("done");
Response.End();
}
}
}
Ajaxsrc.Js
This page is created to keep all the JavaScript related functions and methods at one place. You can also include the AJAX specific implementation in this file, but to keep it simple, I tried to separate and use it on the HTML page, so that if you want to call multiple AJAX callback methods, then you can if you separate the AJAX implementation to the HTML page. In short, I treated this Js file as the AJAX framework.
Step 5: Copy the following code to “ajaxsrc.js”:
function GetXmlHttpObject(handler)
{
try{
var objXmlHttp = null;
objXmlHttp = GetMSXmlHttp();
if (objXmlHttp){
objXmlHttp.onreadystatechange = handler;
}
return objXmlHttp;
}
catch(e){}
}
function GetMSXmlHttp()
{
var xmlHttp = null;
var clsids = ["Msxml2.XMLHTTP.6.0","Msxml2.XMLHTTP.5.0",
"Msxml2.XMLHTTP.4.0","Msxml2.XMLHTTP.3.0",
"Msxml2.XMLHTTP.2.6","Microsoft.XMLHTTP.1.0",
"Microsoft.XMLHTTP.1","Microsoft.XMLHTTP"];
for(var i=0; i<clsids.length && xmlHttp == null; i++) {
xmlHttp = CreateXmlHttp(clsids[i]);
}
return xmlHttp;
}
function CreateXmlHttp(clsid) {
var xmlHttp = null;
try {
xmlHttp = new ActiveXObject(clsid);
lastclsid = clsid;
return xmlHttp;
}
catch(e) {}
}
function GetMSXmlHttp() {
var xmlHttp = null;
var clsids = ["Msxml2.XMLHTTP.6.0",
"Msxml2.XMLHTTP.4.0",
"Msxml2.XMLHTTP.3.0"];
for(var i=0; i<clsids.length && xmlHttp == null; i++) {
xmlHttp = CreateXmlHttp(clsids[i]);
}
return xmlHttp;
}
function SendXmlHttpRequest(xmlhttp, url){
xmlhttp.open('POST', url, true);
xmlhttp.send(null);
}
Step 6: Create a sub-directory or an images folder, and add the following three images:
Image Name
| Image ID
| Description
|
Image1 | indicator.gif
| This image is used to show the user that the process has been started. You can create your own image that looks like a Please Wait icon.
|
Image2 | | Used to show the user that the process completed successfully for the order item.
|
Image3 | wrong.gif
| Used to show the user that the process failed to complete successfully for the order item. |
Finally, after all the above steps, you will see the project/solution as follows:
Conclusion
As we all know, there are many different best practices to implement the code for a given configuration/setting level. I am not discussing a particular implementation here, but would like to share a concept of using these technologies/concepts and implementing the solution. The above model was tested on IE 5.5 and higher.
Happy coding…