Delegates, Events and the Page Lifecycle

S

studio60podcast

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
 
G

Guest

Try changing the declaration of the LinkButton to protected:

protected LinkButton _lnkRemove;



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
 
G

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.
 
T

Teemu Keiski

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



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
 
S

studio60podcast

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
 

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. After that, you can post your question and our members will help you out.

Ask a Question

Top