(更新:有的同窗說源代碼不想看,說明也不想看,只想要一個demo,這邊提供一下:http://url.cn/LPT50k (密碼:TPHU))javascript
工做好長時間了,這期間許多功能也寫成了很多的控件來使用,可是,都只是爲了代碼的結構清析一些而已。而這一次,我決定完成一個我一直在網上尋找卻沒尋找到的功能。就是,在異步(好比說,後臺的數據庫備份、後臺的文件加解密這類操做)時,前臺假死的狀況。asp自帶了updatePanel,裏面能夠放一個自帶的progress控件,怎麼說呢,這就是一個顯示而已,並且根本不能動。當你在備份數據的時候,你點備份,而後上面顯示「請等待....」,這叫progress,我擦。不過,微軟官方給瞭解釋了,說許多人習慣了觀察瀏覽器狀態欄上面的進度條,我想說那個條子好假,不信你試試。css
好了,下面來講一下我作的這個東西,固然,我先用ajax+ashx的方式想辦法讓它實現,下面是我設計前期本身畫的一張圖:html
這張圖上面有錯誤,可是,做爲個人第一個想法,我以爲它功不可沒,後面的圖都是在它的基礎上修改而來了,它也表明了個人初始想法,以及相關的知識殘缺。java
圖中已經能夠把我大部分的想法表達出來了,固然會有人說,一般ajax輪詢進度都是這麼實現的。可是,卻沒有人將它封裝成服務器控件。jquery
先說圖中明顯的錯誤:ajax
一、異步操做的時候,一旦請求終斷,線程則沒法再訪問到session,這是一個致命的異常。由於,我原本打算以session來保存信息,並依靠session的機制來釋放我已經保存的內容,能夠省下好多流程與精力,但事實證實,我仍是太年輕了~~。因此,我改用了application,寫就了個人第一個後臺ashx文件:數據庫
1 public class AjaxAction : IHttpHandler, IReadOnlySessionState 2 { 3 4 public void ProcessRequest(HttpContext context) 5 { 6 context.Response.ContentType = "text/plain"; 7 if (context.Request.QueryString["Action"] != null) 8 { 9 string command = context.Request.QueryString["Action"].ToString(); 10 System.Reflection.MethodInfo method = this.GetType().GetMethod(command); 11 if (method != null) 12 { 13 method.Invoke(this, new object[] { context }); 14 } 15 } 16 } 17 18 public void ProgressMothed(HttpContext context) 19 { 20 string guid = HttpContext.Current.Session.SessionID.ToString().Trim() + DateTime.Now.ToString("HHmmssffffff").Trim(); 21 Thread th = new Thread( 22 delegate() 23 { 24 for (int i = 0; i < 101; i++) 25 { 26 Thread.Sleep(100); 27 context.Application[guid] = i; 28 context.Response.Write(i.ToString()); 29 } 30 31 } 32 ); 33 th.IsBackground = true; 34 try 35 { 36 th.Start(); 37 context.Application.Add(guid, 0); 38 context.Response.Write(guid); 39 } 40 catch (Exception ex) 41 { 42 context.Response.Write("-1"); 43 } 44 } 45 public void GetPercentMethod(HttpContext context) 46 { 47 try 48 { 49 string guid = context.Request.QueryString["guid"].ToString(); 50 string per = context.Application[guid].ToString(); 51 context.Response.Write(per); 52 } 53 catch (Exception ex) 54 { 55 context.Response.Write("-1"); 56 } 57 } 58 public bool IsReusable 59 { 60 get 61 { 62 return false; 63 } 64 }
1 <script type="text/javascript"> 8 function AjaxMethod() { 9 $.ajax({ 10 url:location.href, 12 data: { Action: "ProgressMethod", ts: (new Date).getTime() }, 13 success: function (data) { 14 if (!(data == undefined || data == null || data == "")) { 15 if (data != "-1") { 16 AjaxGetPercent(data); 17 } else { 18 alert("此處理過程暫時沒法鏈接,請稍後再試"); 19 } 20 } else { 21 alert("訪問的處理不存在,請刷新後重試["+data+"]"); 22 } 23 } 24 }); 25 } 26 function AjaxGetPercent(guid) { 27 $.ajax({ 28 url: location.href, 30 data: { Action: "GetPercentMethod",guid:guid, ts: (new Date).getTime() }, 31 success: function (data) { 32 if (data != "-1") { 33 if (data < 100) { 34 $("#progress_ensleep_pb").css("display", "block"); 35 $("#progress_ensleep_text").html(data); 36 $("#progress_ensleep_pgress").css("width", data + "%"); 37 setTimeout(AjaxGetPercent(guid), 50); 38 } else { 39 $("#progress_ensleep_pgress").css("width", "100%");43 $("#progress_ensleep_text").html(100); 45 } 46 } else { 47 alert("操做過程當中出現異常,請重試"); 48 } 49 } 50 }); 51 } 52 </script>
能夠看出,我徹底按照我當初的想法來作的,只是加了一些容錯機制,試驗了一下,一個進度條在一個頁面上,完成正常,而且加入了樣式,很是好看。可是,若是一個頁面上有十個怎麼辦?這種狀況不少,很常見。並且,每個方法都要寫在ashx這個文件中,人家aspx.cs裏面的東西憑什麼往你ashx裏面寫?這樣的結果固然就是,個人整個項目亂成麪條(打了兩局dota的時間煮的麪條)——看看不清,理理不起來。想作成引入型的,只能作成控件。瀏覽器
可是,作成控件有如下幾個問題:服務器
一、一個頁面上要放n個控件,前臺要爲每一個控件完成狀況執行相應的js回調函數。session
二、這個控件正在執行的時候,若是頁面回傳,完了以後,它必須繼續跑,而且像沒回傳同樣。
三、事件!!!!
先說第三個,事件:
由於控件的目的是執行異步操做,能夠看見,我使用了子線程來處理,而子線程是由委託控件。一開始我是想,這是控件,我爲何放着事件不用呢?我把這個子線程要用到的方法寫成一個事件,由aspx.cs給這個事件寫方法體,不就能夠了麼?事實證實,我又秀了一次個人年輕~~~,ajax回傳的時候,根本就不會觸發控件的這一類事件,之因此說這一類,是由於像on_load()這些仍是觸發的,可是,它是要處理管道管理的,而按鈕的onclick之類的沒法觸發,由於ajax沒有帶viewstate過來。這就致使了,個人每一次ajax都不是postback,我了個擦啊~~0~~。而後我開始收集信息,就像三國殺逆風時你要數數剩餘的牌,dota逆風時你要看一看地圖上每個紅點閃現時它的裝備,war3時拼死一個步兵或者大g小g衝進對面看一看對面建築同樣……,而後我發現,我能夠用的只有ajax帶一的context以及session這兩個東西,固然後臺還有一個application,我不把它放入個人考慮範圍(你被虐慘了,你會想着你家還有一個牛逼的泉水麼?)。而後我使用了委託,是的,寫了一個委託成員。而後爆露出一個成員方法以釋放被使用的application(編碼習慣良好,反正比南京的空氣要好得多~~~)。
再說第第一個和第二個,
js回調方法:我把這個拋給使用者寫了,放在aspx頁面,只要把函數設到控件的屬性裏面就能夠了,而後在控件生成的時候,將這個方法名寫到回調的地方。而後爲控件生成的全部的js方法都起惟一的名字,即 將控件的ClientID放在方法的後面。這使得一個頁面上能夠實現多個控件。
回調後狀態的還原繼續:我在控件的On_load方法裏面,每一次都查找看是否有可用的application,若是有,即會生成一個$(documeent).ready(function(){...}),裏面會直接調用對percent(即當前進度)的ajax請求。
下面上代碼:
1 <%@ Control Language="C#" AutoEventWireup="true" CodeBehind="AjaxProgress.ascx.cs" Inherits="ESLib.Controls.AjaxProgress" %> 2 <script type="text/javascript">if (!window.jQuery) alert("使用AjaxProgress必須引用jquery!");</script> 3 <script type="text/javascript"> 4 $(document).ready(function () { 5 <% if (this.guid != null && this.guid != "") 6 {%> 7 <%="AjaxGetPercent_"+this.ClientID+"('"+this.guid+"');"%> 8 <%}%> 9 }); 10 function AjaxMethod_<%=this.ClientID%>(clientid) { 11 $.ajax({ 12 url:location.href, 13 //content: "Action=ProgressMethod&ClientID=" + clientid + "&ts=" + (new Date).getTime(), 14 data: { Action: "ProgressMethod", ClientID: '<%=this.ClientID%>', ts: (new Date).getTime() }, 15 success: function (data) { 16 if (!(data == undefined || data == null || data == "")) { 17 if (data != "-1") { 18 AjaxGetPercent_<%=this.ClientID%>(data); 19 } else { 20 alert("此處理過程暫時沒法鏈接,請稍後再試"); 21 } 22 } else { 23 alert("訪問的處理不存在,請刷新後重試["+data+"]"); 24 } 25 } 26 }); 27 } 28 function AjaxGetPercent_<%=this.ClientID%>(guid) { 29 $.ajax({ 30 url: location.href, 31 //content: "Action=GetPercentMethod&ClientID=" + clientid + "&guid=" + guid + "&ts=" + (new Date).getTime(), 32 data: { Action: "GetPercentMethod", ClientID: '<%=this.ClientID%>',guid:guid, ts: (new Date).getTime() }, 33 success: function (data) { 34 if (data != "-1") { 35 if (data < 100) { 36 $("#progress_ensleep_pb_<%=this.ClientID%>").css("display", "block"); 37 $("#progress_ensleep_text_<%=this.ClientID%>").html(data); 38 $("#progress_ensleep_pgress_<%=this.ClientID%>").css("width", data + "%"); 39 setTimeout(AjaxGetPercent_<%=this.ClientID%>(guid), 50); 40 } else { 41 $("#progress_ensleep_pgress_<%=this.ClientID%>").css("width", "100%"); 42 if ("<%=this.CloseWhenEnd.Trim()%>" == "true") { 43 $("#progress_ensleep_pb_<%=this.ClientID%>").css("display", "none"); 44 } 45 $("#progress_ensleep_text_<%=this.ClientID%>").html(100); 46 <%=this.JsSuccessCallBack%> 47 } 48 } else { 49 alert("操做過程當中出現異常,請重試"); 50 } 51 } 52 }); 53 } 54 </script> 55 <style type="text/css"> 56 .progressContentensleep{ 57 background-color:blue; 58 height:30px; 59 float:left; 60 width:300px; 61 } 62 .progressInnerensleep { 63 background-color:green; 64 height:30px; 65 } 66 .progressSpanensleep{ 67 color:white; 68 height:30px; 69 line-height:30px; 70 margin-top:-30px; 71 } 72 </style> 73 <div> 74 <div style="display:none"> 75 </div> 76 <div id='progress_ensleep_pb_<%=this.ClientID.Trim() %>' class='<%=this.OuterCssClass %>' style='display: none'> 77 <div id='progress_ensleep_pgress_<%=this.ClientID.Trim() %>' class='<%=this.InnerCssClass %>' style="width:0%"> 78 </div> 79 <div id='progress_ensleep_span_<%=this.ClientID.Trim() %>' class='<%=this.TextCss %>'><%=this.WarmText.Substring(0,this.WarmText.IndexOf('{')) %><span id='progress_ensleep_text_<%=this.ClientID.Trim() %>'"></span><%=this.WarmText.Substring(this.WarmText.IndexOf('}')+1,this.WarmText.Length-this.WarmText.IndexOf('}')-1) %></div> 80 </div> 81 </div>
控件的cs代碼:
1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Linq; 5 using System.Threading; 6 using System.Web; 7 using System.Web.UI; 8 using System.Web.UI.WebControls; 9 10 namespace ESLib.Controls 11 { 12 [ToolboxData("<{0}:AjaxProgress runat=server></{0}:AjaxProgress>")] 13 [ToolboxItem(true)] 14 public partial class AjaxProgress : System.Web.UI.UserControl 15 { 16 [Description("完成提示,如‘完成{0}%’")] 17 public string WarmText 18 { 19 get { return ViewState[this.ClientID.Trim()+"hfWarmText"].ToString() == "" ? "目前已完成{0}%" : ViewState[this.ClientID.Trim()+"hfWarmText"].ToString(); } 20 set { 21 ViewState[this.ClientID.Trim()+"hfWarmText"] = value; 22 } 23 24 } 25 [Description("進度條內芯樣式類")] 26 public string InnerCssClass 27 { 28 get { return ViewState[this.ClientID.Trim()+"hfinnerCss"].ToString() == "" ? "progressInnerensleep" : ViewState[this.ClientID.Trim()+"hfinnerCss"].ToString(); } 29 set { 30 ViewState[this.ClientID.Trim()+"hfinnerCss"] = value; 31 } 32 } 33 34 public string CloseWhenEnd 35 { 36 get { return ViewState[this.ClientID.Trim()+"hfCloseWhenEnd"].ToString() == "" ? "true" : ViewState[this.ClientID.Trim()+"hfCloseWhenEnd"].ToString() == "true" ? "true" : "false"; } 37 set { 38 ViewState[this.ClientID.Trim()+"hfCloseWhenEnd"]= value; 39 } 40 } 41 [Description("進度條容器樣式類")] 42 public string OuterCssClass 43 { 44 get { return ViewState[this.ClientID.Trim()+"hfouterCss"].ToString() == "" ? "progressContentensleep" : ViewState[this.ClientID.Trim()+"hfouterCss"].ToString(); } 45 set 46 { 47 ViewState[this.ClientID.Trim()+"hfouterCss"] = value; 48 } 49 } 50 51 [Description("進度條文字樣式類")] 52 public string TextCss 53 { 54 get { return ViewState[this.ClientID.Trim()+"hftextCss"].ToString() == "" ? "progressSpanensleep" : ViewState[this.ClientID.Trim()+"hftextCss"].ToString(); } 55 set 56 { 57 ViewState[this.ClientID.Trim()+"hftextCss"] = value; 58 } 59 } 60 [Description("達到100%後要執行的js方法,如:js()")] 61 public string JsSuccessCallBack 62 { 63 get { return ViewState[this.ClientID.Trim() + "hfJsSuccessCallBack"].ToString(); } 64 set { 65 ViewState[this.ClientID.Trim()+"hfJsSuccessCallBack"] = value; 66 } 67 } 68 [Description("進度條惟一標誌符")] 69 public string guid 70 { 71 get { 72 try 73 { 74 return HttpContext.Current.Request.Cookies[this.ClientID.Trim() + "guid"] == null ? "" : HttpContext.Current.Request.Cookies[this.ClientID.Trim() + "guid"].Value.ToString(); 75 } 76 catch (Exception ex) 77 { 78 return ""; 79 } 80 } 81 set 82 { 83 HttpCookie c = new HttpCookie(this.ClientID.Trim() + "guid"); 84 c.Value = value; 85 HttpContext.Current.Response.Cookies.Add(c); 86 } 87 } 88 89 public delegate void DoMethodDelegate(object guid); 90 public DoMethodDelegate DoMethod; 91 92 public void End(object guid) 93 { 94 Application.Remove(guid as String); 95 } 96 protected void Page_Load(object sender, EventArgs e) 97 { 98 if (!IsPostBack) 99 { 100 ViewState[this.ClientID.Trim()+"hfWarmText"] = ViewState[this.ClientID.Trim()+"hfWarmText"] == null ? "" : ViewState[this.ClientID.Trim()+"hfWarmText"]; 101 ViewState[this.ClientID.Trim()+"hfinnerCss"] = ViewState[this.ClientID.Trim()+"hfinnerCss"] == null ? "" : ViewState[this.ClientID.Trim()+"hfinnerCss"]; 102 ViewState[this.ClientID.Trim()+"hfouterCss"] = ViewState[this.ClientID.Trim()+"hfouterCss"] == null ? "" : ViewState[this.ClientID.Trim()+"hfouterCss"]; 103 ViewState[this.ClientID.Trim()+"hftextCss"] = ViewState[this.ClientID.Trim()+"hftextCss"] == null ? "" : ViewState[this.ClientID.Trim()+"hftextCss"]; 104 ViewState[this.ClientID.Trim()+"hfJsSuccessCallBack"] = ViewState[this.ClientID.Trim()+"hfJsSuccessCallBack"] == null ? "" : ViewState[this.ClientID.Trim()+"hfJsSuccessCallBack"]; 105 ViewState[this.ClientID.Trim() + "hfCloseWhenEnd"] = ViewState[this.ClientID.Trim() + "hfCloseWhenEnd"] == null ? "" : ViewState[this.ClientID.Trim() + "hfCloseWhenEnd"]; 106 } 107 else 108 { 109 } 110 if (HttpContext.Current.Request.QueryString["Action"] != null) 111 { 112 HttpContext.Current.Response.Clear(); 113 if (HttpContext.Current.Request.QueryString["ClientID"] != null && (HttpContext.Current.Request.QueryString["ClientID"].ToString().Trim() == this.ClientID.Trim())) 114 { 115 HttpContext.Current.Response.ContentType = "text/plain"; 116 if (HttpContext.Current.Request.QueryString["Action"] != null) 117 { 118 string command = HttpContext.Current.Request.QueryString["Action"].ToString(); 119 System.Reflection.MethodInfo method = this.GetType().GetMethod(command); 120 if (method != null) 121 { 122 method.Invoke(this,null); 123 } 124 } 125 } 126 } 127 } 128 129 public void ProgressMethod() 130 { 131 string guid = HttpContext.Current.Session.SessionID.ToString().Trim() + DateTime.Now.ToString("HHmmssffffff").Trim(); 132 //Thread th = new Thread(DoMethod(guid)); 133 try 134 { 135 ThreadPool.QueueUserWorkItem(new WaitCallback(this.DoMethod), guid);//未找處處理程序 136 Application.Add(guid, 0); 137 this.guid= guid; 138 HttpContext.Current.Response.Write(guid); 139 } 140 catch (Exception ex) 141 { 142 HttpContext.Current.Response.Write("-1"); 143 144 } 145 HttpContext.Current.Response.End(); 146 } 147 public void GetPercentMethod() 148 { 149 try 150 { 151 string guid = HttpContext.Current.Request.QueryString["guid"].ToString(); 152 if (Application[guid] != null) 153 { 154 HttpContext.Current.Response.Write(Application[guid].ToString()); 155 } 156 else 157 { 158 HttpCookie c = new HttpCookie(this.ClientID.Trim() + "guid"); 159 c.Expires = DateTime.Now.AddDays(-1); 160 HttpContext.Current.Response.Cookies.Add(c); 161 HttpContext.Current.Response.Write("100"); 162 } 163 } 164 catch (Exception ex) 165 { 166 HttpContext.Current.Response.Write("-1"); 167 } 168 HttpContext.Current.Response.End(); 169 } 170 } 171 }
因爲時間問題,就很少說了,代碼全在這裏了,我是以爲,我被這個東西擋了好屢次了,而後網上一直沒有相似的控件,讓人着實難受。
寫好的文件在這裏,http://url.cn/OK26ls (密碼:SEvf)
下面是用法,測試的童鞋能夠看一下:
使用說明:
<UC:AjaxProgress runat = "server" ID = "AjaxProgress2" JsSuccessCallBack = "sucessajax3()" InnerCssClass = "progressInner" OuterCssClass = "progressContent" WarmText = "{0}%" TextCss = "progressSpan" CloseWhenEnd="false" />
說明:
屬性
事件:
AjaxProgress2.DoMethod = DoMethod2;
public void DoMethod2(object guid)
{
for (int i = 0; i < 101; i++)
{
Thread.Sleep(500);
Application[guid as String] = i;
}
}
在aspx.cs中,必需要聲明一個方法,而且將此方法賦值給控件的委託DoMethod
觸發進度條函數爲:
AjaxMethod _【AjaxProgress的ClientID】()
解析:
AjaxProgress的ClientID,即在瀏覽器中的id,因爲沒有呈現器,因此,此處與ID相同,即樣例中的AjaxProgress2。
例如:
<asp:Button runat = "server" ID="Button2" Text = "執行3" OnClientClick = "AjaxMethod_AjaxProgress2();return false;" />
進度條後臺執行事件:
數據委託定義:public delegate void DoMethodDelegate(object guid);
因此,能夠定義成以下:
public void DoMethod2(object guid) { for (int i = 0; i < 101; i++) { Thread.Sleep(500); Application[guid as String] = i; } AjaxProgress2.End(guid); }