當 GridView 為每一筆資料列擺上 checkbox 時,別以為這樣對 user 很貼心喔,其實是夢靨的開始。 因為聰明的 user 在換頁後很快就會發現,前一頁有勾選的資料,在換頁之後,勾選狀態就不見了。原本你期待的是讚美與鼓勵,一夕間就變成 bug 與缺失了。想到下班後還要繼續加班改程式,所言至此 ,就不禁潸然流下男兒淚了...
不過,寫程式的最高心法就是:Copy Right (Copy is right,拷貝是對的)。往好處想,至少下次再遇到同樣的問題時,就可以不用再這麼辛苦從無到有了。如此累積下去,幾年後,每個人肯定都會身懷絕技。於是將這次解決的方法寫下來,日後再遇到同樣問題,有時間的話可以再去進一步最佳化;如果沒時間,至少也不會開天窗。
我參考了網路上的文章,針對這問題提出兩種解決的版本,一種是從後端(server side)來處理,另一種則是從前端(client side)來處理。兩者的差別,在於勾選後是否會觸發 postback。當然,個人是比較偏好 client side 來處理,主要是因為程式碼比較簡潔、程式執行比較快速。
------ server side -------
說明:
將已勾選資訊存放在 List<string> lcb ,並將它透過 viewstate 來包裝。在 gridview 的 RowDataBound 事件,針對每個 checkbox 去註冊 GetPostBackEventReference(),讓 checkbox 一被勾選就會觸發 postback,同時將唯一可識別的資訊當作 postback 的參數。並於 Page_Load 攔截所 有 postback 事件,判斷是否是 checkbox 所發出,如果是,就更新 List<string> lcb 與 gridview 上 checkbox 的勾選與否資訊。
.aspx
<asp:GridView ID="gv" runat="server" AutoGenerateColumns="False" EnableModelValidation="True" AllowPaging="True" DataSourceID="dsData" onrowdatabound="gv_RowDataBound" PageSize="5"> <Columns> <asp:TemplateField> <ItemTemplate> <asp:CheckBox ID="cb" runat="server" /> </ItemTemplate> </asp:TemplateField> <asp:BoundField DataField="id" HeaderText="ID" /> <asp:BoundField DataField="name" HeaderText="Name" /> </Columns> </asp:GridView> </div>.aspx.cs
List<string> lcb = new List<string>(); protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { ViewState["cbList"] = lcb; } string strTarget = "" + Request.Form["__EVENTTARGET"]; string strCtl = strTarget.Split('$')[strTarget.Split('$').Length - 1]; string strArg = "" + Request.Form["__EVENTARGUMENT"]; HandleEvent(strCtl, strArg); } protected void gv_RowDataBound(object sender, GridViewRowEventArgs e) { if (e.Row.RowType == DataControlRowType.DataRow) { lcb = (List<string>)ViewState["cbList"]; CheckBox cb = (CheckBox)e.Row.FindControl("cb"); cb.Attributes["OnClick"] = this.ClientScript.GetPostBackEventReference(cb, DataBinder.Eval(e.Row.DataItem,"id").ToString()); if (lcb.Find(x => x.Equals( DataBinder.Eval(e.Row.DataItem, "id").ToString ().Trim())) != null) cb.Checked = true; else cb.Checked = false; } } private void HandleEvent(string strCtl, string strArg) { switch (strCtl) { case "cb": UpdateCBList(strArg); break; } } private void UpdateCBList(string strArg) { lcb = (List<string>)ViewState["cbList"]; if (lcb.Find(x => x.Equals(strArg)) == null) { //no match,insert lcb.Add(strArg); } else { //match,remove lcb.Remove(strArg); } //final ViewState["cbList"] = lcb; }------ client side -------
說明:
在 gridview 的 RowDataBound 事件,將每一個 checkbox 都加上 key 的屬性,裡面存放唯一且可識別的資訊。再透過 jQuery 為每個 checkbox 註冊勾選時都要觸發函式:UpdateHiddenValue()。在這函式裡,會判斷被觸發的 checkbox 的 key ,是否已存在於隱藏欄位 hValue (注意,必須將這隱藏欄位加上 runat="server",即使分頁後,依然可以保存 hValue 裡面的值),如果不存在,則將 key 值加入 hValue ,否則則從 hValue 裡面移除。最後,讓 DataGridView 裡的 checkbox 都隨時與 hValue 保持勾選與否的資訊同步。
.aspx
<head runat="server"> <title></title> <script src="http://code.jquery.com/jquery-latest.js"></script> <script> $(function () { //定義 checkbox 被點選時所要觸發的動作 $("input[id$=cb]").click(function () { UpdateHiddenValue(this.key); }); //將隱藏欄位 hValue 的值與 DataGridView 的 checkbox 作對應更新 $("input[id$=cb]").each(function (i) { if ((',' + $("input[id*=hValue]").val()).indexOf(',' + $(this).attr('key') + ',') > -1) { //已存在,將 checkbox 打勾 this.checked = true; } else { //不存在,將 checkbox 勾選取消 this.checked = false; } }); }); function UpdateHiddenValue(strInput) { if ((',' + $("input[id*=hValue]").val()).indexOf(',' + strInput + ',') > - 1) { //已存在,將輸入的值從隱藏欄位 hValue 移除 var v = ',' + $("input[id*=hValue]").val(); v = v.replace(',' + strInput + ',', ','); v = v.substr(1, v.length - 1); $("input[id*=hValue]").val(v); } else { //未存在,將輸入的值加入隱藏欄位 hValue $("input[id*=hValue]").val($("input[id*=hValue]").val() + strInput+','); } } </script> </head> <body> <form id="form1" runat="server"> <asp:GridView ID="gv" runat="server" AutoGenerateColumns="False" EnableModelValidation="True" AllowPaging="True" DataSourceID="dsData" PageSize="5" onrowdatabound="gv_RowDataBound"> <Columns> <asp:TemplateField> <ItemTemplate> <asp:CheckBox ID="cb" runat="server" /> </ItemTemplate> </asp:TemplateField> <asp:BoundField DataField="id" HeaderText="ID" /> <asp:BoundField DataField="name" HeaderText="Name" /> </Columns> </asp:GridView> <input type="hidden" id="hValue" runat="server" /> <asp:XmlDataSource ID="dsData" runat="server" DataFile="~/data.xml"> </asp:XmlDataSource> <div> </div> </form> </body>.aspx.cs
protected void gv_RowDataBound(object sender, GridViewRowEventArgs e) { if (e.Row.RowType == DataControlRowType.DataRow) { CheckBox cb = (CheckBox)e.Row.FindControl("cb"); cb.InputAttributes["key"] = string.Format("{0}", DataBinder.Eval (e.Row.DataItem, "id")); } }參考:
01:利用ASP.NET的HIDDENFIELD記錄GRIDVIEW的CHECKBOX狀態,並且加入全選、取消全選、分頁保留CHECKBOX狀態,達到類似GMAIL的郵件清單功能
02:GridView 欄位 CheckBox 全選及取消全選
03:測試程式下載
謝謝~分享這麼有用的資訊
回覆刪除但,我有一個問題,
如何取得所有勾選的項目呢???
我以gridview的row掃勾選的項目,
僅能抓取到本頁中,感謝您的回答~
在掃之前先設定取消分頁,掃完後,再設定為分頁模式
刪除