本文介绍了创建基于视图状态在Page_ preRender动态控件导致按钮onclick事件无法工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我知道,动态控件应在的Page_Load和Page_Init为了创建为他们在控件树进行登记。

我创建需要一个按钮OnClick事件中使用的ViewState的自定义控件。然后,这个ViewState是用于动态创建控件。

由于生命周期会:页面加载 - >按钮点击 - >页面preRender。视图状态不会被更新,直到点击链接,因此我创造我的第preRender动态控件。但是,创建一个按钮,并在Page_ preRender编程分配的OnClick事件处理程序不起作用。

有谁知道我能得到这个工作?

btn_DeleteTableRow_Click不会闪光。这是CreatePartRows设置()

下面是我的例子:

 < ASP:的UpdatePanel ID =up_RMAPart=服务器的UpdateMode =条件的EnableViewState =真ChildrenAsTriggers =真>
<&的ContentTemplate GT;
    < D​​IV CLASS =button风格=宽度:54px;保证金:0像素;浮动:权利;>
        < ASP:按钮的ID =btn_AddPart=服务器文本=添加的OnClick =btn_AddPart_Click/>
    < / DIV>
    < ASP:表ID =Table_Parts=服务器的CssClass =HOR-斑马>
    < / ASP:表>
    < D​​IV CLASS =清除>< / DIV>
< /&的ContentTemplate GT;
<&触发器GT;
    < ASP:AsyncPostBackTrigger控件ID =btn_AddPart事件名称=点击/>
< /触发器>

code背后:

  [Serializable接口]
公共结构部分
{
    公共字符串零件名称;
    公众诠释数量;
    公众诠释PARTID;    公共部分(字符串sPartName,诠释iQuantity,诠释iPartID)
    {
        零件名称= sPartName;
        数量= iQuantity;
        PARTID = iPartID;
    }
}公共部分类RMAPart:System.Web.UI.UserControl
{
    私人字典<字符串,部分> m_RMAParts;
    私人INT m_RowNumber = 0;    公共字典<字符串,部分> RMAParts
    {
        得到
        {
            如果(的ViewState [m_RMAParts]!= NULL)
                回报(字典<字符串,部分>)的ViewState [m_RMAParts];
            其他
                返回null;
        }
        组
        {
            的ViewState [m_RMAParts] =值;
        }
    }    公众诠释ROWNUMBER
    {
        得到
        {
            如果(的ViewState [m_RowNumber]!= NULL)
                返回Convert.ToInt32(的ViewState [m_RowNumber]);
            其他
                返回0;
        }
        组
        {
            的ViewState [m_RowNumber] =值;
        }
    }    保护无效的Page_Load(对象发件人,EventArgs的发送)
    {
        如果(!Page.IsPostBack)
        {
            RMAParts =新词典<字符串,部分>();
            ROWNUMBER = 0;
            RMAParts.Add(PartRow_+ RowNumber.ToString(),新的部分());
            ROWNUMBER = 1;
            CreatePartRows();
        }
    }    保护无效Page_ preRender(对象发件人,EventArgs的发送)
    {
        CreatePartRows();
    }    私人无效CreatePartRows()
    {
        Table_Parts.Controls.Clear();        TableHeaderRow THR =新TableHeaderRow();        TableHeaderCell THC1 =新TableHeaderCell();
        thc1.Controls.Add(新LiteralControl(部件));
        thr.Cells.Add(THC1);        TableHeaderCell THC2 =新TableHeaderCell();
        thc2.Controls.Add(新LiteralControl(数量));
        thr.Cells.Add(THC2);        TableHeaderCell thc3 =新TableHeaderCell();
        thc3.Controls.Add(新LiteralControl());
        thr.Cells.Add(thc3);        Table_Parts.Rows.Add(THR);        的foreach(KeyValuePair<字符串,部分> KVP在RMAParts)
        {
            字符串[] = SKEY kvp.Key.Split('_');            TR的TableRow =新的TableRow();
            tr.ID = kvp.Key;            TableCell的TC1 =新的TableCell();
            文本框tb_Part =新的TextBox();
            tb_Part.ID =tb_Part_+ SKEY [1];
            tb_Part.CssClass =TextBox1的;
            tc1.Controls.Add(tb_Part);
            tr.Cells.Add(TC1);            TableCell的TC2 =新的TableCell();
            文本框tb_Quantity =新的TextBox();
            tb_Quantity.ID =tb_Quanitty_+ SKEY [1];
            tb_Quantity.CssClass =TextBox1的;
            tc2.Controls.Add(tb_Quantity);
            tr.Cells.Add(TC2);            TableCell的TC3 =新的TableCell();
            按钮btn_Delete =新按钮();
            btn_Delete.ID =btn_Delete_+ SKEY [1];
            btn_Delete.CommandArgument = tr.ID;
            btn_Delete.Click + =新的EventHandler(btn_DeleteTableRow_Click);
            btn_Delete.Text =删除;
            tc3.Controls.Add(btn_Delete);
            tr.Cells.Add(TC3);            Table_Parts.Rows.Add(TR);
        }    }    公共无效复位()
    {
        Table_Parts.Controls.Clear();
        RMAParts.Clear();
        ROWNUMBER = 0;
        RMAParts.Add(PartRow_+ RowNumber.ToString(),新的部分());
        ROWNUMBER = 1;
        CreatePartRows();
    }    保护无效btn_AddPart_Click(对象发件人,EventArgs的发送)
    {
        RMAParts.Add(PartRow_+ RowNumber.ToString(),新的部分());
        ROWNUMBER ++;
    }    保护无效btn_DeleteTableRow_Click(对象发件人,EventArgs的发送)
    {
        按钮BTN =(按钮)发送;
        TR的TableRow =(的TableRow)Table_Parts.FindControl(btn.CommandArgument);
        Table_Parts.Rows.Remove(TR);
        RMAParts.Remove(btn.CommandArgument);
    }
}


解决方案

要确保输入字段的值持续在回传和服务器事件被触发:


  • 使用视图状态来跟踪动态创建控件。

  • LoadViewState 相同的ID(重新创建控件的加载 preRender ,因为这时输入字段的值将丢失)。

这个答案的其余部分详细介绍如何修改code到得到它的工作。

RMAPart.ascx

只是为了方便起见,可以在申报名为.ascx的标题行:

 < ASP:表ID =Table_Parts=服务器的CssClass =HOR-斑马>
    < ASP:&的TableRow GT;
        < ASP:TableHeaderCell文本=部分/>
        < ASP:TableHeaderCell文本=数量/>
        < ASP:TableHeaderCell />
    < / ASP:&的TableRow GT;
< / ASP:表>

RMAPart.ascx.cs

要跟踪动态创建的行,保持行ID的视图状态的列表:

 公共部分类RMAPart:System.Web.UI.UserControl
{
    私人列表<串GT;的rowid
    {
        {返回(列表<串GT;)的ViewState [m_RowIDs]; }
        集合{的ViewState [m_RowIDs] =值; }
    }

btn_AddPart_Click 处理,生成新的行ID和创建控​​件的新行:

 保护无效btn_AddPart_Click(对象发件人,EventArgs的发送)
    {
        字符串ID = GenerateRowID();
        RowIDs.Add(ID);
        CreatePartRow(ID);
    }    私人字符串GenerateRowID()
    {
        INT ID =(INT)的ViewState [m_NextRowID];
        的ViewState [m_NextRowID] = ID + 1;
        返回id.ToString();
    }    私人无效CreatePartRow(字符串ID)
    {
        TR的TableRow =新的TableRow();
        tr.ID = ID;        TableCell的TC1 =新的TableCell();
        文本框tb_Part =新的TextBox();
        tb_Part.ID =tb_Part_+ ID;
        tb_Part.CssClass =TextBox1的;
        tc1.Controls.Add(tb_Part);
        tr.Cells.Add(TC1);        TableCell的TC2 =新的TableCell();
        文本框tb_Quantity =新的TextBox();
        tb_Quantity.ID =tb_Quantity_+ ID;
        tb_Quantity.CssClass =TextBox1的;
        tc2.Controls.Add(tb_Quantity);
        tr.Cells.Add(TC2);        TableCell的TC3 =新的TableCell();
        按钮btn_Delete =新按钮();
        btn_Delete.ID =btn_Delete_+ ID;
        btn_Delete.CommandArgument = ID;
        btn_Delete.Click + = btn_DeleteTableRow_Click;
        btn_Delete.Text =删除;
        tc3.Controls.Add(btn_Delete);
        tr.Cells.Add(TC3);        Table_Parts.Rows.Add(TR);
    }

btn_DeleteTableRow_Click 处理程序,删除点击行和更新视图状态:

 保护无效btn_DeleteTableRow_Click(对象发件人,EventArgs的发送)
    {
        按钮BTN =(按钮)发送;
        TR的TableRow =(的TableRow)Table_Parts.FindControl(btn.CommandArgument);
        Table_Parts.Rows.Remove(TR);
        RowIDs.Remove(btn.CommandArgument);
    }

挂钩的Page_Load ,并通过创建第一行开始做事了:

 保护无效的Page_Load(对象发件人,EventArgs的发送)
    {
        如果(!的IsPostBack)
        {
            重置();
        }
    }    公共无效复位()
    {
        而(Table_Parts.Rows.Count→1)
            Table_Parts.Rows.RemoveAt(Table_Parts.Rows.Count - 1);        的ViewState [m_NextRowID] = 0;
        字符串ID = GenerateRowID();
        的rowid =新的List<串GT; { ID };
        CreatePartRow(ID);
    }

覆盖 LoadViewState 并重新创建使用存储在视图状态的ID的行:

 保护覆盖无效LoadViewState(对象savedState)
    {
        base.LoadViewState(savedState);        的foreach(在的ROWID字符串ID)
        {
            CreatePartRow(ID);
        }
    }
}

与零件处理

在code以上不使用你的部分结构可言。要真正的业务对象和用户控件之间移动数据,可以添加需要部分收集和使用它来创建行和填充文本框,然后一个公共方法补充一点,读出的文本框中的值到部分收集另一种公共方法。

I realize that dynamic controls should be created within Page_Load and Page_Init in order for them to be registered in the control tree.

I have created a custom control that requires the use of ViewState in a button OnClick event. This ViewState is then used to dynamically create controls.

Since the life-cycle will go: Page Load -> Button Click -> Page PreRender. The view-state will not be updated until "Button Click", thus I am creating my dynamic controls in Page PreRender. However, creating a button and programatically assigning the OnClick EventHandler in Page_PreRender does not work.

Does anyone know how I can get this to work?

btn_DeleteTableRow_Click will not fire. This is setup in CreatePartRows()

Here is my example:

<asp:UpdatePanel ID="up_RMAPart" runat="server" UpdateMode="Conditional" EnableViewState="true" ChildrenAsTriggers="true">
<ContentTemplate>
    <div class="button" style="width: 54px; margin: 0px; float: right;">
        <asp:Button ID="btn_AddPart" runat="server" Text="Add" OnClick="btn_AddPart_Click" />
    </div>
    <asp:Table ID="Table_Parts" runat="server" CssClass="hor-zebra">
    </asp:Table>
    <div class="clear"></div>
</ContentTemplate>
<Triggers>
    <asp:AsyncPostBackTrigger ControlID="btn_AddPart" EventName="Click" />
</Triggers>

Code Behind:

[Serializable]
public struct Part
{
    public string PartName;
    public int Quantity;
    public int PartID;

    public Part(string sPartName, int iQuantity, int iPartID)
    {
        PartName = sPartName;
        Quantity = iQuantity;
        PartID = iPartID;
    }
}

public partial class RMAPart : System.Web.UI.UserControl
{
    private Dictionary<string,Part> m_RMAParts;
    private int m_RowNumber = 0;

    public Dictionary<string, Part> RMAParts
    {
        get
        {
            if (ViewState["m_RMAParts"] != null)
                return (Dictionary<string, Part>)ViewState["m_RMAParts"];
            else
                return null;
        }
        set
        {
            ViewState["m_RMAParts"] = value;
        }
    }

    public int RowNumber
    {
        get
        {
            if (ViewState["m_RowNumber"] != null)
                return Convert.ToInt32(ViewState["m_RowNumber"]);
            else
                return 0;
        }
        set
        {
            ViewState["m_RowNumber"] = value;
        }
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!Page.IsPostBack)
        {
            RMAParts = new Dictionary<string, Part>();
            RowNumber = 0;
            RMAParts.Add("PartRow_" + RowNumber.ToString(), new Part());
            RowNumber = 1;
            CreatePartRows();
        }
    }



    protected void Page_PreRender(object sender, EventArgs e)
    {
        CreatePartRows();
    }

    private void CreatePartRows()
    {
        Table_Parts.Controls.Clear();

        TableHeaderRow thr = new TableHeaderRow();

        TableHeaderCell thc1 = new TableHeaderCell();
        thc1.Controls.Add(new LiteralControl("Part"));
        thr.Cells.Add(thc1);

        TableHeaderCell thc2 = new TableHeaderCell();
        thc2.Controls.Add(new LiteralControl("Quantity"));
        thr.Cells.Add(thc2);

        TableHeaderCell thc3 = new TableHeaderCell();
        thc3.Controls.Add(new LiteralControl(""));
        thr.Cells.Add(thc3);

        Table_Parts.Rows.Add(thr);

        foreach (KeyValuePair<string, Part> kvp in RMAParts)
        {
            string[] sKey = kvp.Key.Split('_');

            TableRow tr = new TableRow();
            tr.ID = kvp.Key;

            TableCell tc1 = new TableCell();
            TextBox tb_Part = new TextBox();
            tb_Part.ID = "tb_Part_" + sKey[1];
            tb_Part.CssClass = "textbox1";
            tc1.Controls.Add(tb_Part);
            tr.Cells.Add(tc1);

            TableCell tc2 = new TableCell();
            TextBox tb_Quantity = new TextBox();
            tb_Quantity.ID = "tb_Quanitty_" + sKey[1];
            tb_Quantity.CssClass = "textbox1";
            tc2.Controls.Add(tb_Quantity);
            tr.Cells.Add(tc2);

            TableCell tc3 = new TableCell();
            Button btn_Delete = new Button();
            btn_Delete.ID = "btn_Delete_" + sKey[1];
            btn_Delete.CommandArgument = tr.ID;
            btn_Delete.Click += new EventHandler(btn_DeleteTableRow_Click);                
            btn_Delete.Text = "Remove";
            tc3.Controls.Add(btn_Delete);
            tr.Cells.Add(tc3);

            Table_Parts.Rows.Add(tr);               
        }

    }

    public void Reset()
    {
        Table_Parts.Controls.Clear();
        RMAParts.Clear();
        RowNumber = 0;
        RMAParts.Add("PartRow_" + RowNumber.ToString(), new Part());
        RowNumber = 1;
        CreatePartRows();
    }

    protected void btn_AddPart_Click(object sender, EventArgs e)
    {
        RMAParts.Add("PartRow_" + RowNumber.ToString(), new Part());
        RowNumber++;
    }

    protected void btn_DeleteTableRow_Click(object sender, EventArgs e)
    {
        Button btn = (Button)sender;
        TableRow tr = (TableRow)Table_Parts.FindControl(btn.CommandArgument);
        Table_Parts.Rows.Remove(tr);
        RMAParts.Remove(btn.CommandArgument);
    }        
}
解决方案

To ensure that the values of input fields persist across postbacks and that server events are raised:

  • Use view state to keep track of dynamically created controls.
  • Re-create the controls with the same IDs in LoadViewState (not Load or PreRender, because then the values of input fields will be lost).

The rest of this answer details how I modified your code to get it to work.

RMAPart.ascx

Just for convenience, you can declare the header row in the .ascx:

<asp:Table ID="Table_Parts" runat="server" CssClass="hor-zebra">
    <asp:TableRow>
        <asp:TableHeaderCell Text="Part" />
        <asp:TableHeaderCell Text="Quantity" />
        <asp:TableHeaderCell />
    </asp:TableRow>
</asp:Table>

RMAPart.ascx.cs

To keep track of dynamically created rows, maintain a list of row IDs in view state:

public partial class RMAPart : System.Web.UI.UserControl
{
    private List<string> RowIDs
    {
        get { return (List<string>)ViewState["m_RowIDs"]; }
        set { ViewState["m_RowIDs"] = value; }
    }

In the btn_AddPart_Click handler, generate a new row ID and create the controls for the new row:

    protected void btn_AddPart_Click(object sender, EventArgs e)
    {
        string id = GenerateRowID();
        RowIDs.Add(id);
        CreatePartRow(id);
    }

    private string GenerateRowID()
    {
        int id = (int)ViewState["m_NextRowID"];
        ViewState["m_NextRowID"] = id + 1;
        return id.ToString();
    }

    private void CreatePartRow(string id)
    {
        TableRow tr = new TableRow();
        tr.ID = id;

        TableCell tc1 = new TableCell();
        TextBox tb_Part = new TextBox();
        tb_Part.ID = "tb_Part_" + id;
        tb_Part.CssClass = "textbox1";
        tc1.Controls.Add(tb_Part);
        tr.Cells.Add(tc1);

        TableCell tc2 = new TableCell();
        TextBox tb_Quantity = new TextBox();
        tb_Quantity.ID = "tb_Quantity_" + id;
        tb_Quantity.CssClass = "textbox1";
        tc2.Controls.Add(tb_Quantity);
        tr.Cells.Add(tc2);

        TableCell tc3 = new TableCell();
        Button btn_Delete = new Button();
        btn_Delete.ID = "btn_Delete_" + id;
        btn_Delete.CommandArgument = id;
        btn_Delete.Click += btn_DeleteTableRow_Click;
        btn_Delete.Text = "Remove";
        tc3.Controls.Add(btn_Delete);
        tr.Cells.Add(tc3);

        Table_Parts.Rows.Add(tr);
    }

In the btn_DeleteTableRow_Click handler, delete the clicked row and update view state:

    protected void btn_DeleteTableRow_Click(object sender, EventArgs e)
    {
        Button btn = (Button)sender;
        TableRow tr = (TableRow)Table_Parts.FindControl(btn.CommandArgument);
        Table_Parts.Rows.Remove(tr);
        RowIDs.Remove(btn.CommandArgument);
    }

Hook Page_Load and start things off by creating the first row:

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            Reset();
        }
    }

    public void Reset()
    {
        while (Table_Parts.Rows.Count > 1)
            Table_Parts.Rows.RemoveAt(Table_Parts.Rows.Count - 1);

        ViewState["m_NextRowID"] = 0;
        string id = GenerateRowID();
        RowIDs = new List<string> { id };
        CreatePartRow(id);
    }

Override LoadViewState and re-create the rows using the IDs stored in view state:

    protected override void LoadViewState(object savedState)
    {
        base.LoadViewState(savedState);

        foreach (string id in RowIDs)
        {
            CreatePartRow(id);
        }
    }
}

Dealing with Parts

The code above doesn't use your Part structure at all. To actually move data between your business objects and the user control, you can add a public method that takes a Part collection and uses it to create rows and populate text boxes, and then add another public method that reads out the values of the text boxes into a Part collection.

这篇关于创建基于视图状态在Page_ preRender动态控件导致按钮onclick事件无法工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

11-03 09:07