Delegates, Events and the Page Lifecycle

Discussion in 'ASP .Net' started by studio60podcast, Apr 13, 2007.

  1. I have been fighting with this for almost two days and I can't figure
    it out. I'm hoping someone can shed some light on my problem.

    I have a web user control (NewAccountHolders) that contains a generic
    list of another web user control (NewAccountHolder). I place
    NewAccountHolders in a page and all renders properly, but the
    _lnkRemove_Click never fires when I click "Remove". If I create
    instances of NewAccountHolder outside of NewAccountHolders and add
    them directly to the page, all works properly. But from within the
    NewAccountHolders "container", they don't fire properly.

    Can anyone see where I may have missed a step or wired something up
    out of order?

    test.aspx
    <div>
    <asp:TextBox ID="txtTest" runat="Server" />
    <asp:placeHolder ID="phTest" runat="server" />
    </div>

    test.aspx.cs
    namespace MyTest
    {
    public delegate void AccountHolderRemoveEventHandler(int index);
    public delegate void SomethingHappenedEventHandler(string value);

    public partial class test2 : System.Web.UI.Page
    {
    protected NewAccountHolders _accountHolders;

    protected void Page_Load(object sender, EventArgs e)
    {
    _accountHolders = new NewAccountHolders();
    phTest.Controls.Add(_accountHolders);

    _accountHolders.SomethingHappened += new
    SomethingHappenedEventHandler(_accountHolders_SomethingHappened);
    }

    protected void _accountHolders_SomethingHappened(string value)
    {
    txtTest.Text = value;
    }
    }

    public class NewAccountHolders : WebControl
    {
    public event SomethingHappenedEventHandler SomethingHappened;

    public List<NewAccountHolder> AccountHolderList
    {
    get { return (HttpContext.Current.Session["account_holder_list"] ==
    null) ? new List<NewAccountHolder>() :
    (List<NewAccountHolder>)HttpContext.Current.Session["account_holder_list"]; }
    set { HttpContext.Current.Session["account_holder_list"] = value; }
    }

    protected override void OnInit(EventArgs e)
    {
    base.OnInit(e);
    AddAccountHolder("User 1", "123-45-6579", "1/2/1903");
    AddAccountHolder("User 2", "123-45-6579", "1/2/1903");
    AddAccountHolder("User 3", "123-45-6579", "1/2/1903");
    foreach (NewAccountHolder item in AccountHolderList)
    {
    item.AccountHolderRemoveClicked += new
    AccountHolderRemoveEventHandler(NewRegistrationAccountHolders_AccountHolderRemoveClicked);
    this.Controls.Add(item);
    }
    }

    protected override void OnPreRender(EventArgs e)
    {
    base.OnPreRender(e);
    }

    public void AddAccountHolder(string fullName, string id, string
    dateOfBirth)
    {
    List<NewAccountHolder> list = AccountHolderList;
    list.Add(new NewAccountHolder(fullName, id, dateOfBirth,
    AccountHolderList.Count));
    AccountHolderList = list;
    }

    protected override void RenderContents(HtmlTextWriter output)
    {
    output.Write("<table border=\"0\" cellpadding=\"0\" cellspacing=
    \"0\" class=\"accountHolderContainer\">");
    for (int i = 0; i < 3; i++)
    {
    AccountHolderList.RenderControl(output);
    }
    output.Write("</table>");
    }

    protected void
    NewRegistrationAccountHolders_AccountHolderRemoveClicked(int index)
    {
    SomethingHappened(index.ToString());
    }
    }

    public class NewAccountHolder : WebControl
    {
    private int _index;
    private string _name, _id, _dob;
    private LinkButton _lnkRemove;
    public event AccountHolderRemoveEventHandler
    AccountHolderRemoveClicked;

    public NewAccountHolder(string name, string id, string dob, int
    index)
    {
    _name = name;
    _id = id;
    _dob = dob;
    _index = index;

    _lnkRemove = new LinkButton();
    _lnkRemove.Text = "Remove";
    _lnkRemove.CssClass = "greysmall";
    this.Controls.Add(_lnkRemove);
    _lnkRemove.Click += new EventHandler(_lnkRemove_Click);
    }

    public int Index
    {
    get { return _index; }
    set { _index = value; }
    }

    protected void _lnkRemove_Click(object sender, EventArgs e)
    {
    AccountHolderRemoveClicked(Index);
    }

    protected override void RenderContents(HtmlTextWriter output)
    {
    output.Write("<tr>");
    output.Write("<td colspan=\"2\" class=\"accountHolderItem\">");
    output.Write(_name + "<br>");
    output.Write(_id + "<br>");
    output.Write(_dob + "<br>");
    output.Write("</td>");
    output.Write("</tr>");

    output.Write("<tr>");
    output.Write("<td class=\"accountHolderRemove\">");
    _lnkRemove.RenderControl(output);
    output.Write("</td>");
    output.Write("</tr>");
    }
    }
    }


    Thanks!
    Jason
     
    studio60podcast, Apr 13, 2007
    #1
    1. Advertisements

  2. studio60podcast

    Guest Guest

    Try changing the declaration of the LinkButton to protected:

    protected LinkButton _lnkRemove;



     
    Guest, Apr 13, 2007
    #2
    1. Advertisements

  3. Nope, didn't help.
     
    studio60podcast, Apr 13, 2007
    #3
  4. studio60podcast

    Guest Guest

    I apologize for any incorrect guesses - I'm trying to guess at some of what
    your code is doing since I program in VB.

    Anyway, the next thing I'd try is to narrow it down. Try creating a single
    class-level object (instead of a list), change your various NewAccountHolders
    methods to use the single instance, and then see if you're able to get the
    event to fire - hopefully that will help you narrow down the possibilities.
     
    Guest, Apr 13, 2007
    #4
  5. studio60podcast

    Teemu Keiski Guest

    Hi,

    controls containing child controls need to implement INamingContainer
    interface (or derive from CompositeControl).

    And it's not really smart to keep control instances on Session as you have
    the list of AccountHolders. Having the list itself is OK, but you shouldn't
    put control instances there directly but items like ListItems are (e.g just
    data containers), However, I removed it entiorely since the list wasn't used
    for anything else (and as child controls were instantiated directly in code,
    no need to store them into session etc)

    Here's the modified code:

    public class NewAccountHolders : WebControl,INamingContainer
    {

    public event SomethingHappenedEventHandler SomethingHappened;


    protected override void CreateChildControls()
    {


    NewAccountHolder h = new NewAccountHolder("User 1",
    "123-45-6579", "1/2/1903", 0);
    h.AccountHolderRemoveClicked += new
    AccountHolderRemoveEventHandler(NewRegistrationAccountHolders_AccountHolderRemoveClicked);
    Controls.Add(h);

    h = new NewAccountHolder("User 2", "123-45-6579", "1/2/1903",
    1);
    h.AccountHolderRemoveClicked += new
    AccountHolderRemoveEventHandler(NewRegistrationAccountHolders_AccountHolderRemoveClicked);
    Controls.Add(h);

    h = new NewAccountHolder("User 3", "123-45-6579", "1/2/1903",
    2);
    h.AccountHolderRemoveClicked += new
    AccountHolderRemoveEventHandler(NewRegistrationAccountHolders_AccountHolderRemoveClicked);
    Controls.Add(h);

    }

    protected override void RenderChildren(HtmlTextWriter output)
    {
    output.Write("<table border=\"0\" cellpadding=\"0\"
    cellspacing=\"0\" class=\"accountHolderContainer\">");
    for (int i = 0; i < Controls.Count; i++)
    {
    Controls.RenderControl(output);
    }
    output.Write("</table>");

    }


    protected void
    NewRegistrationAccountHolders_AccountHolderRemoveClicked(int index)
    {
    SomethingHappened(index.ToString());
    }
    }

    public class NewAccountHolder : WebControl,INamingContainer
    {
    private int _index;
    private string _name, _id, _dob;
    private LinkButton _lnkRemove;
    public event AccountHolderRemoveEventHandler
    AccountHolderRemoveClicked;

    public NewAccountHolder(string name, string id, string dob, int
    index)
    {
    _name = name;
    _id = id;
    _dob = dob;
    _index = index;


    }

    protected override void CreateChildControls()
    {
    _lnkRemove = new LinkButton();
    _lnkRemove.ID = "remove";
    _lnkRemove.Text = "Remove";
    _lnkRemove.CssClass = "greysmall";
    _lnkRemove.Click += new EventHandler(_lnkRemove_Click);
    this.Controls.Add(_lnkRemove);

    }
    public int Index
    {
    get { return _index; }
    set { _index = value; }
    }

    protected void _lnkRemove_Click(object sender, EventArgs e)
    {
    if (AccountHolderRemoveClicked != null)
    AccountHolderRemoveClicked(Index);
    }

    protected override void RenderContents(HtmlTextWriter output)
    {
    output.Write("<tr>");
    output.Write("<td colspan=\"2\" class=\"accountHolderItem\">");
    output.Write(_name + "<br>");
    output.Write(_id + "<br>");
    output.Write(_dob + "<br>");
    output.Write("</td>");
    output.Write("</tr>");

    output.Write("<tr>");
    output.Write("<td class=\"accountHolderRemove\">");
    _lnkRemove.RenderControl(output);
    output.Write("</td>");
    output.Write("</tr>");
    }
    }


    --
    Teemu Keiski
    AspInsider, ASP.NET MVP
    http://blogs.aspadvice.com/joteke
    http://teemukeiski.net



     
    Teemu Keiski, Apr 15, 2007
    #5
  6. Teemu,

    Thanks for the reply! Your suggestions fixed my problem. When
    attempting to store the NewAccountHolder in ViewState, I hadn't
    thought of it as storing the actualy webcontrol, but rather just the
    data. So, I created a separate class for the data, store that in
    ViewState, then use it to bind to new instances of NewAccountHolder.

    I also had never implemented INamingContainer either, so this was a
    beneficial problem all around!

    Thanks again!
    Jason
     
    studio60podcast, Apr 19, 2007
    #6
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.