Scott on Writing

Musings on technical writing...

A Stumper of an ASP.NET Question: SOLVED!

In my last blog entry I posed an ASP.NET / Repeater question that had me and a student of mine utterly stumped.  I have been communicating with this student today, bouncing ideas back and forth, and he noted that if the Repeater's EnableViewState property was set to False, then the Button column raised the ItemCommand event, as expected.  Now, all that the Repeater stores in its ViewState StateBag is the number of items in the Repeater (stored in a view state variable named _!ItemCount).

Basically, when the Repeater's DataBind() method is called, the following steps happen:

  1. The Repeater's base's Controls collection has its Clear() method called, cleaning out any controls in the Repeater's hierarchy.
  2. The child view state is cleared out.
  3. The CreateControlHierarchy() method is called, passing in True (indicating that the data should be bound from the DataSource).
  4. The ChildControlsCreated property is set to True.  This property is a flag to indicate that the Repeater's control hierarchy has been built.  Whenever the Repeater's Controls property is accessed, it first checks to make sure that the control hierarchy has been built by calling EnsureChildControls()EnsureChildControls() checks to see if ChildControlsCreated is false; if it is, it calls CreateChildControls().

When a Button submits a Form, the Button's name and value attributes are sent along in the POST data to the Web server.  After the LoadViewState stage in the page's life-cycle (which comes before the Load stage), the ProcessPostData stage transpires.  This stage loads post data back into the appropriate Web controls, such as reloading the text from a textbox back into the Text property of a TextBox Web control.  Specifically, the Page class's ProcessPostData() method enumerates all the POST names passed in and searches the control hierarchy for these controls to determine if they participate in this stage of the life-cycle.  In accessing the control via FindControl(), the EnsureChildControls() method is called and the Repeater's CreateChildControls() method is invoked.

Now, what does the Repeater's CreateChildControls() method do?  Well, it checks to see if the view state variable _!ItemCount is null or not.  If it is not null, then it calls CreateControlHierarchy(), passing in False.  This builds up the control hierarchy, creating the Button.  This created Button is then the Button the Page class's ProcessPostData() method marks to have its Command event raised during the RaisePostBackEvent stage later on.

Hopefully the problem is becoming clearer now.  After this ProcessPostData stage, the Load stage transpires, and the Repeater's DataBind() method is called.  This clears out the control hierarchy and rebuilds it.  Hence, the reference to the Button we had earlier has had it's RepeaterItem “detached” from the Repeater, replaced by a new RepeaterItem isntance from the call to DataBind().  Since it's this orphaned Button's Command event that is raised, the event cannot percolate up to the Repeater, and hence the Repeater's ItemCommand event does not fire.

The LinkButton works because clicking a LinkButton does not send the LinkButton's ID through the POST data.  Rather, it passes this information along in the __EVENTARG hidden form field.  This information, then, is not queried by the Page class until the RaisePostBackEvents stage, which happens after the Load event, after the Repeater's control hierarchy has been constructed.

So, one workaround is as follows: don't call a data Web control's DataBind() method each and every page load if you are using view state.  If you are not using view state, then you'll need to call DataBind() on each and every page load. 

My student, Matt, wrote in to share the following workaround as well (I've not tested it; might not work in all browsers):

My colleague Borys has proposed a fix.  I don't think I can explain it
very well, but we think it has to do with a conflict between post data
and viewstate.  I'll let the code speak for itself.

Private Sub DataList1_ItemDataBound(ByVal sender As Object, ByVal e As
System.Web.UI.WebControls.DataListItemEventArgs) Handles
DataList1.ItemDataBound

            Dim b As Button = DirectCast(e.Item.Controls(1), Button)

            Dim evTarget As String = b.UniqueID.Replace(":", "$")

            Dim script As String = "__doPostBack('" + evTarget +
"','');return(false)"

            b.Attributes.Add("onclick", script)

End Sub

Hope this helps someone, it was rather fun to examine and unearth the solution.

posted on Thursday, May 20, 2004 3:05 PM

Feedback

# re: A Stumper of an ASP.NET Question: SOLVED! 5/20/2004 3:49 PM Paul D. Murphy

IIRC, In Whidbey they have moved away from the : character because of the way certain browsers deal with the character in certain situations. In Whidbey the Control base class picks up the IdSeperator property. This is used to abstract away the seperation character and most likely corrects the problem that you found and fixed.

I thought I read something about IdSeperator on Rob Howards blog, but I can't seem to find it and I can't get google to spit out an answer.

# re: A Stumper of an ASP.NET Question: SOLVED! 5/20/2004 4:33 PM AndrewSeven

I'm home now and I'm going to have a look at this.

I'd like to know which controls behavior you consider wrong?
From Borys code, I guess its the button.

# re: A Stumper of an ASP.NET Question: SOLVED! 5/20/2004 5:02 PM Scott Mitchell

Ryan Trudelle-Schwarz [http://authors.aspalliance.com/mamanze/] mentioned over IM to me that this whole problem can be solved by moving the DataBind() call to the Init event handler. This works b/c the Initialization event of the page's control life-cycle happens *before* the ProcessPostData stage.

# re: A Stumper of an ASP.NET Question: SOLVED! 5/20/2004 5:04 PM Scott Mitchell

Paul, my comment about browser support was not about the : character, but about how basically the code added an onclick event to a button with type=submit. Certainly browsers that have JavaScript enabled will submit the form, and NOT obey the onclick directive, thereby returning us to square one. I am concerned that other browsers might prioritize the submit button over onclick somehow.

# re: A Stumper of an ASP.NET Question: SOLVED! 5/20/2004 6:00 PM AndrewSeven

I think this may be a bug.
I tried using some mono code, but that was just leading to frustration; maybe somone could try it with mono or Rotor.

When the viewstate is enabled and you re-bind, both the events should be cleared and not fire.
(One be rebinding on every postback, that is why viewstate is there)
I may very well be binding different data for which the event is no longer relevant.

If I had a penny for every time I have solved some event issue on aspmessageboard by suggesting that the person not Bind on every postback, I would have at least ten bucks.


I have almost no experience with the viewstate off, but when it is off,re-Binding is not an option and both events fire which seems to make sense.
(They even fire if I bind to different data)




# re: A Stumper of an ASP.NET Question: SOLVED! 5/20/2004 6:01 PM AndrewSeven

Oopsy:
(One should not be rebinding on every postback, that is why viewstate is there)

# re: A Stumper of an ASP.NET Question: SOLVED! 5/20/2004 6:23 PM Scott Mitchell

I agree the DataBind() method shouldn't be called every page view if view state is enabled. Here's a bit from my student, explaining his situation:

"I found this interesting because I am working on a project with similar circumstances. However, in my project I have nested repeaters. In the ItemDataBound handler for the outer repeater, I bind the inner repeater to some datasource. In the ItemCreated handler for the outer repeater, I use FindControl("InnerRepeaterID") and AddHandler to add the handler for the ItemCommand for the inner repeater. Now, when the button in the inner repeater is clicked, the InnerRepeater ItemCommand handler does fire, contrary to what happens when the inner repeater is not nested in an outer repeater. I think that's why I took notice when the non-nested repeater wasn't working for me."

So I think he is needing to rebind the selected inner Repeater on postbacks, and I think what is throwing it is the FindControl() call, as that's causing the Repeater's CreateChildControls() method to run. On postback, with view state enabled, this bumps into the problem I discussed in this entry...

# re: A Stumper of an ASP.NET Question: SOLVED! 5/20/2004 8:34 PM AndrewSeven

Interesting.
I have had issues before with events on nested repeaters/grids.
It was some time ago but the events on the inner repeaters (might have been CheckboxLists) did not function properly when I bound them in the code, but functioned "as expected" when I specified them in the html attributes.

I notice that both item databound and item created are used. Does it make a difference if only the databound event is used to do both the bind and the event atachment?

I'm really curious, do you have some minmal code I can try fiddling with?

# Databinding bits... 3/24/2005 12:21 PM Scott Galloway's Personal Blog

# Databinding issues 4/4/2005 3:59 PM Andrew Hilary

# re: A Stumper of an ASP.NET Question: SOLVED! 6/20/2005 6:16 AM Hansen

Just wanted to say that I solved this issue with specifying the events in the html-attributes as AndrewSeven suggested!

Great! :-)

# OnItemCommand not firing on repeater control 9/14/2005 12:27 AM dehran ph - NDriven Life

Just this afternoon I am creating the main transaction module for my project, it was my first project...

# re: A Stumper of an ASP.NET Question: SOLVED! 9/16/2005 1:41 PM efrank

I had this same problem. I have to rebind my repeater list because the data changes as a user makes their selections. However I don't need the viewstate of the the repeater list saved so I set EnableViewState=False and it now works (per recommendation of http://tempurisnacks.blogspot.com/2005/01/waste-of-few-days.html)

A co-worker suggests that if the problem is truncation of the transmission and you can't decrease the size of the viewstate, then try including encType="multipart/form-data" in your form declaration.

# Thanks! 11/7/2005 11:13 AM Oleg

Event handlers for nested binding controls works in aspx files.
Thanks!

# re: A Stumper of an ASP.NET Question: SOLVED! 2/7/2006 4:34 AM Anthony Feeney

I had this same problem - the ItemCommand wasn't firing when I clicked on a link button template column. It was down to me calling DataBind() on every page refresh with ViewState on.

# re: A Stumper of an ASP.NET Question: SOLVED! 2/9/2006 6:27 AM sbyard

Very useful.

In my case I have custom repeaters in nested Web User Controls, and although I bind to a generic list, I actually search the controls and load the data myself manually.

I have an issue that a repeater in a WUC load event will attempt to call it's child controls in the nested WUC, but it wil not have completed it's page load yet (.Net 2.0 has a different process model to 1.1). Unlike a web page, a post load event does not exist for WUC's

After having similar issues to other postee's, I have used the above posted info, and put a check for the ChildControlsCreated in order to force this if not yet happened on postback. I still have some issues, but it's working as close as I've got it so far!

Many thanks

# IdSeperator property 2/22/2006 9:29 PM Jayesh

Hi,

I have used : in my server control which is written in 2003. Now when i use it in Whidbey, i am finding issue with getting value. If Whidbey the Control base class picks up the IdSeperator property, what change should i make in my control?

# re: A Stumper of an ASP.NET Question: SOLVED! 3/15/2006 2:22 PM Tab

argh, I'd hoped this thread would lead to the pot of gold, but it doesn't seem to help.

In my case, I've got a repeater with LinkButtons that are created programmatically in the Repeater's ItemDataBound event.

I've found that if I don't DataBind the repeater on any given trip, the ItemCommand event doesn't fire when the LinkButtons are clicked.

I've tried creating a sub that runs when .isPostBack = true that finds the repeater and tries to create instances of all of the repeater's controls, but it can't find the LinkButtons. The code for the sub is:

Private Sub InstantiateRepeaterControls()
'rptQueryList = Page.FindControl("rptQueryList")
rptResultColumns = Page.FindControl("rptResultColumns")

Dim rsItem As RepeaterItem
For Each rsItem In rptResultColumns.Items
Dim tdResultColumnHeader As New System.Web.UI.HtmlControls.HtmlTableCell
tdResultColumnHeader = rsItem.FindControl("tdResultColumnHeader")
Dim hrefOrderBy As New System.Web.UI.WebControls.LinkButton
hrefOrderBy = tdResultColumnHeader.FindControl("hrefOrderBy")
Next

End Sub

As I step through the code and QuickWatch it, each repeater item does have the tdResultColumnHeader control in it, but neither the repeater nor the tablecell has the linkbutton control in it.

Any idea why not? I do have ViewState enabled on the repeater.

# re: A Stumper of an ASP.NET Question: SOLVED! 4/13/2006 8:41 AM PonyRyd

Same as Hansen:

The issue was solved by specifying the events in the html-attributes as AndrewSeven suggested! This means not using the handles in the Vb.net code behind.

# re: A Stumper of an ASP.NET Question: SOLVED! 11/29/2006 1:04 AM Ruben Cordoba

Given a LinkButton inside a dynamic user control loaded inside a GridView row template, I see LinkButton_Command is never fired. Why?

Title:  
Name:  
Url:
Protected by Clearscreen.SharpHIPEnter the code you see:
Comments   

My Links

Ads Via DevMavens

Archives

Post Categories

 

I am a Microsoft MVP for ASP.NET.
I am an ASPInsider.
<March 2010>
SMTWTFS
28123456
78910111213
14151617181920
21222324252627
28293031123
45678910

Comment Stats

DayTotal% of Total
Sunday 2056.8%
Monday 42514.1%
Tuesday 51917.2%
Wednesday 55618.4%
Thursday 58019.2%
Friday 54718.1%
Saturday 1886.2%
Total 3020100.0%

Hour1Total% of Total
12:00 AM 782.6%
1:00 AM 812.7%
2:00 AM 682.3%
3:00 AM 822.7%
4:00 AM 692.3%
5:00 AM 1264.2%
6:00 AM 1193.9%
7:00 AM 1816.0%
8:00 AM 1926.4%
9:00 AM 1585.2%
10:00 AM 1886.2%
11:00 AM 1936.4%
12:00 PM 2016.7%
1:00 PM 1846.1%
2:00 PM 1695.6%
3:00 PM 1354.5%
4:00 PM 1153.8%
5:00 PM 1073.5%
6:00 PM 1013.3%
7:00 PM 1073.5%
8:00 PM 923.0%
9:00 PM 882.9%
10:00 PM 913.0%
11:00 PM 953.1%
Total 3020100.0%

Comments by Blog Entry Date/Time

Day Entry MadeAvg.Total
Sunday 5.00160
Monday 4.80384
Tuesday 4.04477
Wednesday 7.39680
Thursday 6.26676
Friday 5.07466
Saturday 4.78177
Total 5.403020

Hour1 Entry MadeAvg.Total
12:00 AM 5.2937
1:00 AM 1.002
5:00 AM 0.000
7:00 AM 3.8550
8:00 AM 3.72134
9:00 AM 6.06297
10:00 AM 5.63276
11:00 AM 4.22194
12:00 PM 6.16351
1:00 PM 3.09133
2:00 PM 4.89230
3:00 PM 7.67322
4:00 PM 4.00108
5:00 PM 6.07170
6:00 PM 4.64116
7:00 PM 8.95188
8:00 PM 8.63164
9:00 PM 5.00115
10:00 PM 6.31101
11:00 PM 4.5732
Total 5.403020

Learn More About Comment Stats
1 - All times GMT -8...


Blog Stats

Favorite Web Sites

My Books

My MSDN Articles