2011年2月2日星期三

Dynamic CheckBox Control 取值技巧

正所謂"人類不斷犯重覆的錯誤",今天我工作上在搞一些Dynamic Control就剛好可以套用給自己。
自問對如何把玩Dynamic Web Control,Page Life Cycle是怎樣,如何重構Controls和ViewState都很了解。
但奈何今天敗在一個CheckBox Control下,花了我半天才搞定,所以在Blog上做一個記錄和分享。

而遇到的問題就是,
在同一個Form內,PostBack後,所有動態Control都存取都正常,唯獨是任何CheckBox Controls的Checked屬性一直都是False。

搞了一段時間後,我決定由最原始的方式 Request.Form 的方式,去找出問題在那裡。
或者這樣說太抽象,先看看Source Code和Screen Shot。

Markup 方面很簡單,一個Button,一個PlaceHolder。
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="test.aspx.cs" Inherits="test" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title></title>
</head>
<body style="font-family: Tahoma; font-size: 11px;">
    <form id="form1" runat="server">
    <div>
        <asp:Button ID="btnAdd" runat="server" Text="Add CheckBox" OnClick="btnAdd_Click" />
        <asp:PlaceHolder ID="PlaceHolder1" runat="server" />
    </div>
    </form>
</body>
</html>
再來是Source部份,留意Line:62 & 63 :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

public partial class test : System.Web.UI.Page
{
    protected int NumberOfControls
    {
        get { return (int)ViewState["ControlCount"]; }
        set { ViewState["ControlCount"] = value; }
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!Page.IsPostBack)
            NumberOfControls = 0;
        else
            RecreateControls();
    }

    protected void btnAdd_Click(object sender, EventArgs e)
    {
        CreateControls(this.NumberOfControls);
        this.NumberOfControls++;
    }

    private void CreateControls(int i)
    {
        CheckBox chk = new CheckBox();
        chk.ID = "chk" + i.ToString();

        Label lbl = new Label { ID = "lbl" + i.ToString(), Text = "
Index : " + i.ToString() };

        PlaceHolder1.Controls.Add(lbl);
        PlaceHolder1.Controls.Add(chk);
        PlaceHolder1.Controls.Add(new Literal { Text = "
" });
    }

    private void RecreateControls()
    {
        int count = this.NumberOfControls;
        for (int i = 0; i < count; i++)
            CreateControls(i);

        int idx = 0;
        for (int x = 0; x < PlaceHolder1.Controls.Count; x++)
        {
            Control ctl = PlaceHolder1.Controls[x];

            if (ctl is Label)
            {
                Response.Write(((Label)ctl).Text);
            }
            else if (ctl is CheckBox)
            {
                Response.Write(" Checked : "
                + ((CheckBox)ctl).Checked.ToString() + " | "
                + Request.Form["chk" + idx.ToString()] + "
");
                idx++;
            }
        }
    }
}

得出來的結果如下圖 :



使用((CheckBox)ctl).Checked.ToString()輸出是False,但Request.Form找取Value卻是"on",最奇怪就是用IE的Deveper Tool看一看文件,明明真的有CHECKED="checked",換句話說,ASP.NET的CheckBox Control在勾選或取消勾選雖然會變更Client-Side Value和Checked屬性,但不會更改Control States的Checked的屬性,這就是問題的根源,於是我立即想起沒有PostBack的原故。

那如何解決呢? 很簡單,就是把CheckBox的AutoPostBack設成True,再在CheckedChanged的Event加上Event Handler就可以了。



整理後的Source :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

public partial class test : System.Web.UI.Page
{
    protected int NumberOfControls
    {
        get { return (int)ViewState["ControlCount"]; }
        set { ViewState["ControlCount"] = value; }
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!Page.IsPostBack)
            NumberOfControls = 0;
        else
            RecreateControls();
    }

    protected void btnAdd_Click(object sender, EventArgs e)
    {
        CreateControls(this.NumberOfControls);
        this.NumberOfControls++;
    }

    private void CreateControls(int i)
    {
        CheckBox chk = new CheckBox();
        chk.ID = "chk" + i.ToString();
        chk.AutoPostBack = true;
        chk.CheckedChanged += CheckedChanged;

        Label lbl = new Label { ID = "lbl" + i.ToString(), Text = "
Index : " + i.ToString() };

        PlaceHolder1.Controls.Add(lbl);
        PlaceHolder1.Controls.Add(chk);
        PlaceHolder1.Controls.Add(new Literal { Text = "
" });
    }

    private void CheckedChanged(Object sender, EventArgs e)
    {

    }

    private void RecreateControls()
    {
        int count = this.NumberOfControls;
        for (int i = 0; i < count; i++)
            CreateControls(i);

        int idx = 0;
        for (int x = 0; x < PlaceHolder1.Controls.Count; x++)
        {
            Control ctl = PlaceHolder1.Controls[x];

            if (ctl is Label)
            {
                Response.Write(((Label)ctl).Text);
            }
            else if (ctl is CheckBox)
            {
                Response.Write(" Checked : "
                + ((CheckBox)ctl).Checked.ToString() + " | "
                + Request.Form["chk" + idx.ToString()] + "
");
                idx++;
            }
        }
    }
}

沒有留言:

發佈留言