简单的 AJAX、ASP.NET 和 C# 实现页面等待或处理屏幕






4.67/5 (27投票s)
一种简单的 AJAX 方法,包括一个 HTML 页面客户端,以及一个进行 AJAX 调用并接收响应的 JavaScript。一个远程页面将接收参数信息,并使用必要的信息响应 JavaScript 调用方法。
引言
我们经常会遇到需要进程状态的操作项、用例或屏幕界面,一般的开发策略是在屏幕上显示一个请等待图标/图像,并在进程完成后页面进行回发。本文档旨在帮助开发人员为用户提供一个实时的可视化进程页面,从而提供丰富的用户界面。我将讨论一种在 ASP.NET Web 应用程序中使用 AJAX 实现此功能的非常简单的方法。而且,好消息是您无需安装或升级现有的开发环境到 AJAX 框架或使用扩展器,这可以通过现有的 XMLHttpRequestBrowser
对象来实现。
你们大多数人都知道 AJAX 代表 Asynchronous JavaScript and XML。据我所知,微软过去曾将其称为 DHTML,后来这个概念/技术才以 AJAX 的名义流行起来。我相信您在开发过程中或各种在线文章中都遇到过 AJAX。只需在任何搜索引擎中输入 AJAX,它就会返回大量与 AJAX 及其实现相关的文章,但这不在本文档的范围之内。
简要描述
这是一种简单的 AJAX 方法,包括一个 HTML 页面客户端和一个进行 AJAX 调用并接收响应的 JavaScript。一个远程页面将接收参数信息,并使用必要的信息响应 JavaScript 调用方法。这个概念将为订单处理屏幕或基于股票基本面和技术分析的股票评估屏幕提供实时用户界面。我曾用它来评估我的股票清单,这也是本文的基本思想。假设您有一个订单项列表,所有订单项都必须同时处理。通常,用户希望在每个订单项开始处理时看到一个进度/状态,您可以设计您的应用程序,以便用户在当前订单项开始处理时在其旁边看到一个指示器。一旦订单处理请求完成,订单时间旁边将显示一个成功完成图像,如下图所示
实现和使用代码
为了简单起见,我将逐步介绍代码和必要的 HTML 页面。我已将所有基于 AJAX 的 JavaScript 方法和函数分离到一个 .js 文件中,并将该文件作为源包含在 HTML 页面中。
我已将 GetXmlHttpObject
分离并实现在 HTML 页面上,只是为了让读者更容易理解。以下是实现的步骤
- 使用 Visual Studio 创建一个“default.aspx”(我使用的是 Visual Studio 2005,但您可以使用任何版本,甚至 HTML/文本编辑器也可以)。
- 创建第二个页面,名为“startproc.aspx”。(一个远程页面,它将接收来自 JavaScript 的 AJAX 调用的请求,并用必要的信息进行响应。)
- 创建第三个页面,名为“endproc.aspx”。(您可以完全跳过此页面,并使用不同的实现利用“startproc.aspx”,但为了简单起见,我使用了此页面。)
- 创建一个 .js 文件并将其命名为“ajaxsrc.js”。
- 创建一个 Images 目录并添加三张图片。
Default.aspx
这是唯一用作用户界面的页面。AJAX 回调方法将通过传递订单项信息从远程页面请求信息,并在收到远程页面返回的信息后立即更改状态,而无需进行任何回发调用。
步骤 1:将以下代码添加到“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.');
}//if
else{
//script for running IE
ProcessOrder('startproc.aspx');
}//else
}//submitOrder()
//***************************************************************
//*** Ajax Implementation
//***************************************************************
function ProcessOrder(pagename){
var url = null;
url = pagename;
document.getElementById('btnOrder').disabled = true;
try{
xmlHttp = GetXmlHttpObject(CallbackMethod);
SendXmlHttpRequest(xmlHttp, url);
}//try
catch(e){}
}//end: functino getAccess
function getOrder(item){
//This is used only to keep as simple as possible and declartion/scope of
//the variables can be implemented in many different ways.
//I have used for the purpose of understanding the model.
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');
}//if img
else if(item.search(/tick:/) == 0){
//Successful
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 ticker : right
else if(item.search(/error:/) == 0){
//Failed
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 Wrong
else if(item == 'syerror:'){
alert('System Error Occured');
return;
}//else if
else
alert('unknown Error');
return;
}//if
else{
alert('Request Processed Successfully.');
document.getElementById('btnOrder').disabled = false;
return;
}//else
}//try
catch(e){}//catch
}//getPage(url);
//CallbackMethod will fire on state Change
function CallbackMethod(){
try{
//readyState of 4 or 'complete'
//represents that data has been returned
if (xmlHttp.readyState == 4 || xmlHttp.readyState == 'complete'){
var response = xmlHttp.responseText;
if (response.length > 0){
getOrder(response);
}//if
}//if
}//try
catch(e){}
}//end CallbackMethod
//*******************************************************************************
</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
代码隐藏文件用于生成订单集合并将其显示在屏幕上。我使用代码隐藏通过 HTML 表格和静态订单集合构建订单网格。您可以访问数据库或文件对象,并填充 DataTable
以替换静态订单集合。
步骤 2:将以下代码添加到“default.aspx.cs”。
public partial class _Default : System.Web.UI.Page {
protected void Page_Load(object sender, EventArgs e){
fillSecurityOrder();
}//page_Load
protected void fillSecurityOrder(){
//There are numerous ways to deal with the styles. I tried here one of a
//different approach to not to add a styles.css.
//I do not encourage to use this approach of using the styles as i believe
//this will have a maintenance overhead.
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;";
//set up the table Properties.
//You could add styles as per your standards.
Table theTable = new Table();
theTable.BorderStyle = BorderStyle.Solid;
theTable.BorderWidth = 1;
theTable.CellPadding = 1;
theTable.CellSpacing = 1;
theTable.Width = Unit.Pixel(220);
//Setup the Table Header details.
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);
//Set up the body details.
//To keep it simple i am using a static elements.
//But you can create a dynamic element collection
//from a file or a datastore.
DataTable tblOrderCollection = OrderCollection();
int iStyle = 0;
string applyStyle = string.Empty;
foreach (DataRow dr in tblOrderCollection.Rows){
//diff way to apply alternate styles.
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);
}//foreach
//In order to track the OrderItem on the receiving end
//via the callback method and provide
//a realtime analysis of the process status i have used
//a Session objects that can share the
//information between the Ajax and the CallbackMethod.
Session["OrderBucket"] = tblOrderCollection;
pnlOrder.Controls.Add(theTable);
}//fillSecurityOrder()
//Refactor the Item
protected Label createItem(string Ticker){
Label theLabel = new Label();
//Caution is needed so that
//you can always create a unique itemID
theLabel.ID = Ticker;
theLabel.Text = Ticker;
//return the Label
return theLabel;
}//createItem
//Refactor the Item
protected Image createImage(string Ticker){
Image theImage = new Image();
//Caution is needed so that You can always create a unique itemID
theImage.ID = "img" + Ticker;
theImage.ImageUrl = "images/indicator.gif";
theImage.Style["visibility"] = "hidden";
//return the Image
return theImage;
}//createImage
//get OrderCollection
protected DataTable OrderCollection(){
//For prototype only. The Order Collection can be created
//and filled dynamic based on your
//database connection or a file system object.
DataTable theDataTable = new DataTable();
theDataTable.Columns.Add(new DataColumn("sOrderItem", typeof(string)));
theDataTable.Columns.Add(new DataColumn("isProc", typeof(Int32)));
//Create Row One
DataRow drOne;
drOne = theDataTable.NewRow();
drOne["sOrderItem"] = ".NET";
drOne["isProc"] = 0;
theDataTable.Rows.Add(drOne);
//Create Row Two
DataRow drTwo;
drTwo = theDataTable.NewRow();
drTwo["sOrderItem"] = "Ajax";
drTwo["isProc"] = 0;
theDataTable.Rows.Add(drTwo);
//Create Row Three
DataRow drThree;
drThree = theDataTable.NewRow();
drThree["sOrderItem"] = "Framework";
drThree["isProc"] = 0;
theDataTable.Rows.Add(drThree);
//Create Row Four
DataRow drFour;
drFour = theDataTable.NewRow();
drFour["sOrderItem"] = "Code Project";
drFour["isProc"] = 0;
theDataTable.Rows.Add(drFour);
//return the OrderCollection
return theDataTable;
}//OrderCollection()
}//class
Startproc.aspx
此页面用于接收当前订单项并通知 AJAX 回调方法该订单项已收到,并且可以通知用户。您可以更改图像/图标、消息或状态符号,让用户感觉当前订单项已开始处理。当您将此页面添加到您的项目时,请不要对 HTML 设计进行任何修改或更改,因为这是一个远程页面,用户界面不由该项目中的任何客户端对象处理。
步骤 3:将以下内容添加到“startproc.aspx.cs”
public partial class startproc : System.Web.UI.Page{
protected void Page_Load(object sender, EventArgs e){
//Clear the Cache.
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Clear();
//Check if the Session object exits.
if (Session["OrderBucket"] != null){
//get the Session Order Collection to a DataTable.
DataTable theTable = (DataTable)Session["OrderBucket"];
//Find or get details of each Order from the Order Collection.
foreach (DataRow dr in theTable.Rows){
int iproc = Convert.ToInt32(dr["isProc"].ToString());
if (iproc == 0){
//This is for understanding purpose only. I am using an
//int to change the process status
//so the UI can represent the current state as
//processed successfully or failed.
dr["isProc"] = 2;//set up 2 as this processed.
theTable.GetChanges();
//Update the Session Order Collection so that the callback
//method will move to next Order Item that is in the queue.
Session["OrderBucket"] = theTable;
Response.Write("Img:" + dr["sOrderItem"].ToString());
Response.End();
}//if
}//foreach
//Respond when all the Order Items have been processed.
Response.Write("done");
Response.End();
}//if
}//Page_Load
}//class
Endproc.aspx
此页面用于接收当前订单项信息,表明该订单项将进行处理,并响应 default.aspx 页面上的 AJAX 回调方法。此页面与 startproc.aspx 分离,仅用于演示进程中如何处理开始和结束进程。当您将此页面添加到您的项目时,请不要对 HTML 设计进行任何修改或更改,因为这是一个远程页面,用户界面不由该项目中的任何客户端对象处理。
步骤 4:将以下代码添加到“endproc.aspx.cs”
public partial class endproc : System.Web.UI.Page{
protected void Page_Load(object sender, EventArgs e){
//Clear the Cache
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Clear();
//Check if the Session object exists.
if (Session["OrderBucket"] != null){
//get the Session Order Collection to a DataTable.
DataTable theTable = (DataTable)Session["OrderBucket"];
//Find or get details of each Order from the Order Collection.
foreach (DataRow dr in theTable.Rows){
int iproc = Convert.ToInt32(dr["isProc"].ToString());
if (iproc == 2){
//Setting to a different process, for understanding purpose only.
//In the overall process for a given Order Item you can set up different
//process stages as the order Item is passed to different processes
dr["isProc"] = 1;
theTable.GetChanges();
//*********************************************/
//Actual process will be handled here, such as calling an API,
//Webservice, writing to a differnt system,
//synchronize with other object etc.
//I am showing a thread to sleep for 2000 milli sec. to
//simulate as if it is processed.
System.Threading.Thread.Sleep(2000);
//*********************************************/
//Update the Session Order Collection so that the callback method
//will move to next Order Item that is in the queue.
Session["OrderBucket"] = theTable;
//Iam trying to simulate a process that
// has problem and would report on the UI
//i.e. i have taken an example "framwork"
//as some problems in processing the order.
if (string.Compare(dr["sOrderItem"].ToString(), "Framework") == 0)
Response.Write("error:" + dr["sOrderItem"].ToString());
else
Response.Write("tick:" + dr["sOrderItem"].ToString());
Response.End();
}//if
}//foreach
//Respond when all the Order Items have been processed.
Response.Write("done");
Response.End();
//theDCFAgent.CallAsyncServices();
}//if
}//PageLoad
}//class
Ajaxsrc.Js
创建此页面是为了将所有 JavaScript 相关函数和方法集中到一个地方。您也可以将 AJAX 的特定实现包含在此文件中,但为了简单起见,我尝试将其分离并用于 HTML 页面,这样,如果您想调用多个 AJAX 回调方法,那么您可以将 AJAX 实现分离到 HTML 页面。简而言之,我将此 Js 文件视为 AJAX 框架。
步骤 5:将以下代码复制到“ajaxsrc.js”
// JScript File
function GetXmlHttpObject(handler)
{
try{
var objXmlHttp = null;
objXmlHttp = GetMSXmlHttp();
if (objXmlHttp){
objXmlHttp.onreadystatechange = handler;
}
return objXmlHttp;
}
catch(e){}
}
function GetMSXmlHttp()
{
//alert('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) {
//alert('CreateXmlHttp');
var xmlHttp = null;
try {
xmlHttp = new ActiveXObject(clsid);
lastclsid = clsid;
return xmlHttp;
}
catch(e) {}
}
function GetMSXmlHttp() {
//alert('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);
}//end SendXmlHttpRequest
步骤 6:创建一个子目录或 images 文件夹,并添加以下三张图片
图片名称 |
图片 ID |
描述 |
图片1 ![]() |
indicator.gif |
此图像用于向用户显示进程已启动。您可以创建自己的类似“请等待”图标的图像。 |
图片2 ![]() |
用于向用户显示订单项进程已成功完成。 |
|
图片3 ![]() |
wrong.gif |
用于向用户显示订单项进程未能成功完成。 |
最后,完成以上所有步骤后,您将看到项目/解决方案如下
结论
众所周知,针对给定的配置/设置级别,有许多不同的最佳实践来实现代码。我在这里不讨论特定的实现,但希望分享一个使用这些技术/概念并实现解决方案的想法。上述模型已在 IE 5.5 及更高版本上测试。
祝您编程愉快……