當 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:
測試程式下載