Please explain why AddHandler works in Page_Load, but not Button_C

G

Guest

I need to understand why if I add a control and use AddHandler to connect its
click event, it will work in Page_Load, but not in a Button_Click.

The idea is that the user types some data, presses the button, gets a list
of results (each with a LinkButton) and can then press one of the link
buttons to get further information. The newly added link buttons appear, but
the click event added with AddHandler does not fire.

A control added in Page_Load with an event handler added with AddHandler
works fine.

I have not writted a web app previously and I guess that I do not understand
how dynamically added handlers work (or dont work).
 
B

bruce barker \(sqlwork.com\)

the key to understanding asp.net is to know that its stateless, and a form
instance is only used for one request. so if you add a link in a button
click, and the user clicks on that link, a new form is built to process that
request. your code must know to add the handler when that happens. usually
you'd store a flag in viewstate or session to known when to add the handler
(you must also re-add the link control).

-- bruce (sqlwork.com)
 
C

Cowboy \(Gregory A. Beamer\)

It has to do with the order of events in .NET. By the time you hit button
events, you have already configured controls. Under "sender", you can find
out which button was clicked and adjust appropriately in Page_Load. Be
careful, however, with overloading Page_Load with too much, as you can end
up with disaster.
 
G

Guest

Sorry, but I still don't get it. What I have in mind is a form with 3 states
that would look a bit like google. State 1, you see a textbox and button, you
type a search value and press the button, State 2, a list of search results
is displayed (a table with a LinkButton in each row), you click on the
selected row's LinkButton. State 3, The details about the selected item are
displayed along with a button to go back to state 1.

The database search after state 1 and the insertion of the results and
LinkButtons in the table must take place in the Button's Click event, the
table duly appears with the data and LinkButtons. At the end of state 2, the
LinkButtons cause a post, but no event is fired, also the entire contents of
the table no longer exist - this does not matter as I only want to know the
ID of the button.

Is the idea of linking the dynamically created LinkButtons to an event the
wrong approach? Should I be looking at the request.form(0) value? Or is my
whole approach wrong, am I thinking too much along Windows Forms lines?
 
W

Walter Wang [MSFT]

Hi John,

First, I recommend you to use [1] as your starting point to learn ASP.NET.
Though it's written for ASP.NET 1.x, it should get you started.

Since ASP.NET page is stateless, a page class instance and its controls
will need to be created at the server side every time the page is posted
back. See following steps to get an overview of the page life cycle:

1) POST Request is issued by client
2) Page-derived class is created, constructor is invoked
3) IHttpHandler.ProcessRequest is invoked (implemented by Page)
4) Page.Init()
5) Page.CreateChildControls()
6) Server-side control state is restored from POST variables and VIEWSTATE
7) Page.Load()
8) Page.Validate()
9) Server-side control events are fired
10) Page.PreRender()
11) Page.Render()
12) Page.RenderChildren()
13) HTTP Response is issued to client
14) Page.Unload()
15) Instance of Page-derived class is discarded

Each control will have a unique ID, this ID will be used to restore the
state from POST variables and VIEWSTATE (step 6) to the re-created controls
(step 5).

During load (step 7), if the current request is a postback, control
properties are loaded with information recovered from view state and
control state. (See [2] fore more information)

For your dynamically added controls to restore state correctly, you must
make sure the controls are added before the Load event. It's a best
practice to re-create them in Page.Load.

Regarding your issue, the LinkButtons are created in Button's Click event,
they are displayed in the result for the first time since Render is taken
place after that (step 11 and step 9). When the LinkButton is clicked and
caused a postback, the LinkButtons are not re-created, and the POST
variables related to them are lost. You could check the Request.Form for
the field that caused the postback, but there're better way to fulfil your
requirement.

Actually your requirement can be best handled by the Repeater [3] control
and DataBinding:


<asp:Button ID="Button1" runat="server" Text="Button"
OnClick="Button1_Click" />
<br />
<asp:Repeater ID="Repeater1" runat="server"
OnItemCommand="Repeater1_ItemCommand">
<HeaderTemplate>
<ul>
</HeaderTemplate>
<ItemTemplate>
<li>
<asp:LinkButton ID="link1" runat="server" CommandName="Link1"
CommandArgument=' said:
</asp:LinkButton>
</li>
</ItemTemplate>
<FooterTemplate>
</ul>
</FooterTemplate>
</asp:Repeater>


protected void Button1_Click(object sender, EventArgs e)
{
DataTable dt = new DataTable();
dt.Columns.Add("Name");
dt.Columns.Add("Description");
dt.Rows.Add("First", "This is the first item");
dt.Rows.Add("Second", "This is the second item");
Repeater1.DataSource = dt;
Repeater1.DataBind();
}
protected void Repeater1_ItemCommand(object source,
RepeaterCommandEventArgs e)
{
if (e.CommandName == "Link1")
{
Response.Write(e.CommandArgument);
}
}

LinkButton's Click event will be bubbled up to the Repeater and fire
ItemCommand instead. Using the CommandName and CommandArgument, we can
differentiate which LinkButton is clicked.

I hope this could help you get familiar with how ASP.NET works and how to
handle such scenario using appropriate controls. Please feel free to let me
know whether or not you need further information. Thank you.

References:

[1] INFO: ASP.NET Roadmap
http://support.microsoft.com/kb/305140/

[2] ASP.NET Page Life Cycle Overview
http://msdn2.microsoft.com/en-us/library/ms178472.aspx

[3] Repeater Web Server Control Overview
http://msdn2.microsoft.com/en-us/library/x8f2zez5.aspx

Sincerely,
Walter Wang ([email protected], remove 'online.')
Microsoft Online Community Support

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications. If you are using Outlook Express, please make sure you clear the
check box "Tools/Options/Read: Get 300 headers at a time" to see your reply
promptly.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.
 
G

Guest

Thank you Walter for your excellent reply. Whilst checking request.form(0) in
Page_Load was a work around (after they click, I don't want the table
anyway), the Repeater is a better solution for me as I can use the event
driven methodology that I am used to with Windows Forms applications.

Thanks once again,
--
John Austin


Walter Wang said:
Hi John,

First, I recommend you to use [1] as your starting point to learn ASP.NET.
Though it's written for ASP.NET 1.x, it should get you started.

Since ASP.NET page is stateless, a page class instance and its controls
will need to be created at the server side every time the page is posted
back. See following steps to get an overview of the page life cycle:

1) POST Request is issued by client
2) Page-derived class is created, constructor is invoked
3) IHttpHandler.ProcessRequest is invoked (implemented by Page)
4) Page.Init()
5) Page.CreateChildControls()
6) Server-side control state is restored from POST variables and VIEWSTATE
7) Page.Load()
8) Page.Validate()
9) Server-side control events are fired
10) Page.PreRender()
11) Page.Render()
12) Page.RenderChildren()
13) HTTP Response is issued to client
14) Page.Unload()
15) Instance of Page-derived class is discarded

Each control will have a unique ID, this ID will be used to restore the
state from POST variables and VIEWSTATE (step 6) to the re-created controls
(step 5).

During load (step 7), if the current request is a postback, control
properties are loaded with information recovered from view state and
control state. (See [2] fore more information)

For your dynamically added controls to restore state correctly, you must
make sure the controls are added before the Load event. It's a best
practice to re-create them in Page.Load.

Regarding your issue, the LinkButtons are created in Button's Click event,
they are displayed in the result for the first time since Render is taken
place after that (step 11 and step 9). When the LinkButton is clicked and
caused a postback, the LinkButtons are not re-created, and the POST
variables related to them are lost. You could check the Request.Form for
the field that caused the postback, but there're better way to fulfil your
requirement.

Actually your requirement can be best handled by the Repeater [3] control
and DataBinding:


<asp:Button ID="Button1" runat="server" Text="Button"
OnClick="Button1_Click" />
<br />
<asp:Repeater ID="Repeater1" runat="server"
OnItemCommand="Repeater1_ItemCommand">
<HeaderTemplate>
<ul>
</HeaderTemplate>
<ItemTemplate>
<li>
<asp:LinkButton ID="link1" runat="server" CommandName="Link1"
CommandArgument=' said:
</asp:LinkButton>
</li>
</ItemTemplate>
<FooterTemplate>
</ul>
</FooterTemplate>
</asp:Repeater>


protected void Button1_Click(object sender, EventArgs e)
{
DataTable dt = new DataTable();
dt.Columns.Add("Name");
dt.Columns.Add("Description");
dt.Rows.Add("First", "This is the first item");
dt.Rows.Add("Second", "This is the second item");
Repeater1.DataSource = dt;
Repeater1.DataBind();
}
protected void Repeater1_ItemCommand(object source,
RepeaterCommandEventArgs e)
{
if (e.CommandName == "Link1")
{
Response.Write(e.CommandArgument);
}
}

LinkButton's Click event will be bubbled up to the Repeater and fire
ItemCommand instead. Using the CommandName and CommandArgument, we can
differentiate which LinkButton is clicked.

I hope this could help you get familiar with how ASP.NET works and how to
handle such scenario using appropriate controls. Please feel free to let me
know whether or not you need further information. Thank you.

References:

[1] INFO: ASP.NET Roadmap
http://support.microsoft.com/kb/305140/

[2] ASP.NET Page Life Cycle Overview
http://msdn2.microsoft.com/en-us/library/ms178472.aspx

[3] Repeater Web Server Control Overview
http://msdn2.microsoft.com/en-us/library/x8f2zez5.aspx

Sincerely,
Walter Wang ([email protected], remove 'online.')
Microsoft Online Community Support

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications. If you are using Outlook Express, please make sure you clear the
check box "Tools/Options/Read: Get 300 headers at a time" to see your reply
promptly.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.
 
G

Guest

Hello Walter,
The Data Repeater works fine, but thinking further about the issue, it would
make sense for state 1 to build a query string from input criteria and store
the select statement in viewstate so that the Page_Load in state 2 can query
the database and generate results (this means that pressing Back in the
browser from state 3 would re-query the database and include any changes).
This would also avoid my original problem; but when in state 1, the button
press would need to build the query string, change the state to 2 and then
force a reload of the current page. How could I force the page to be re-build
from the Button_Click event?

--
John Austin


Walter Wang said:
Hi John,

First, I recommend you to use [1] as your starting point to learn ASP.NET.
Though it's written for ASP.NET 1.x, it should get you started.

Since ASP.NET page is stateless, a page class instance and its controls
will need to be created at the server side every time the page is posted
back. See following steps to get an overview of the page life cycle:

1) POST Request is issued by client
2) Page-derived class is created, constructor is invoked
3) IHttpHandler.ProcessRequest is invoked (implemented by Page)
4) Page.Init()
5) Page.CreateChildControls()
6) Server-side control state is restored from POST variables and VIEWSTATE
7) Page.Load()
8) Page.Validate()
9) Server-side control events are fired
10) Page.PreRender()
11) Page.Render()
12) Page.RenderChildren()
13) HTTP Response is issued to client
14) Page.Unload()
15) Instance of Page-derived class is discarded

Each control will have a unique ID, this ID will be used to restore the
state from POST variables and VIEWSTATE (step 6) to the re-created controls
(step 5).

During load (step 7), if the current request is a postback, control
properties are loaded with information recovered from view state and
control state. (See [2] fore more information)

For your dynamically added controls to restore state correctly, you must
make sure the controls are added before the Load event. It's a best
practice to re-create them in Page.Load.

Regarding your issue, the LinkButtons are created in Button's Click event,
they are displayed in the result for the first time since Render is taken
place after that (step 11 and step 9). When the LinkButton is clicked and
caused a postback, the LinkButtons are not re-created, and the POST
variables related to them are lost. You could check the Request.Form for
the field that caused the postback, but there're better way to fulfil your
requirement.

Actually your requirement can be best handled by the Repeater [3] control
and DataBinding:


<asp:Button ID="Button1" runat="server" Text="Button"
OnClick="Button1_Click" />
<br />
<asp:Repeater ID="Repeater1" runat="server"
OnItemCommand="Repeater1_ItemCommand">
<HeaderTemplate>
<ul>
</HeaderTemplate>
<ItemTemplate>
<li>
<asp:LinkButton ID="link1" runat="server" CommandName="Link1"
CommandArgument=' said:
</asp:LinkButton>
</li>
</ItemTemplate>
<FooterTemplate>
</ul>
</FooterTemplate>
</asp:Repeater>


protected void Button1_Click(object sender, EventArgs e)
{
DataTable dt = new DataTable();
dt.Columns.Add("Name");
dt.Columns.Add("Description");
dt.Rows.Add("First", "This is the first item");
dt.Rows.Add("Second", "This is the second item");
Repeater1.DataSource = dt;
Repeater1.DataBind();
}
protected void Repeater1_ItemCommand(object source,
RepeaterCommandEventArgs e)
{
if (e.CommandName == "Link1")
{
Response.Write(e.CommandArgument);
}
}

LinkButton's Click event will be bubbled up to the Repeater and fire
ItemCommand instead. Using the CommandName and CommandArgument, we can
differentiate which LinkButton is clicked.

I hope this could help you get familiar with how ASP.NET works and how to
handle such scenario using appropriate controls. Please feel free to let me
know whether or not you need further information. Thank you.

References:

[1] INFO: ASP.NET Roadmap
http://support.microsoft.com/kb/305140/

[2] ASP.NET Page Life Cycle Overview
http://msdn2.microsoft.com/en-us/library/ms178472.aspx

[3] Repeater Web Server Control Overview
http://msdn2.microsoft.com/en-us/library/x8f2zez5.aspx

Sincerely,
Walter Wang ([email protected], remove 'online.')
Microsoft Online Community Support

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications. If you are using Outlook Express, please make sure you clear the
check box "Tools/Options/Read: Get 300 headers at a time" to see your reply
promptly.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.
 
W

Walter Wang [MSFT]

Hi John,

See if following code answers your question:

protected void Page_Load(object sender, EventArgs e)
{
RebindData();
}
protected void Button1_Click(object sender, EventArgs e)
{
QueryStatement = "foo sql statement";
RebindData();
}

void RebindData()
{
string sql = QueryStatement;
if (string.IsNullOrEmpty(sql))
{
Repeater1.DataSource = null;
}
else
{
// you need to do sql query in real app; here I'm just
returning a DataTable for test purpose
DataTable dt = new DataTable();
dt.Columns.Add("Name");
dt.Columns.Add("Description");
dt.Rows.Add("First", "This is the first item");
dt.Rows.Add("Second", "This is the second item");
Repeater1.DataSource = dt;
}
Repeater1.DataBind();
}

string QueryStatement
{
get { reutrn ViewState["QueryStatement"] as string; }
set { ViewState["QueryStatement"] = value; }
}

protected void Repeater1_ItemCommand(object source,
RepeaterCommandEventArgs e)
{
if (e.CommandName == "Link1")
{
Response.Write(e.CommandArgument);
}
}

Regards,
Walter Wang ([email protected], remove 'online.')
Microsoft Online Community Support

==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.
 
G

Guest

Sorry Walter, putting the creation of the results table in a subroutine is a
far simpler solution - I guess that getting my head round the way web app
work clouded the brain!

Many thanks,
 

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

Members online

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,050
Latest member
AngelS122

Latest Threads

Top