Am using Master Pages in a Web Application and I need to find control references in my codebehind. Problem is I can't find them with FindControl no matter what I try.
Page Layout:
<asp:ContentID="Content5"ContentPlaceHolderID="PageContent"runat="server">
<div>
<asp:PlaceHolderID="PlaceHolder1"runat="server"> My controls are in here
</asp:PlaceHolder>
</div> </asp:Content>
From my codebehind, I am using XSLT to transform XML data:
sLookupResult.Append(oAPI.XSLTransform(Server.MapPath("~/XSLT/Lookup.xslt"), arguments))
sLookupResult.Replace("xmlns:asp=""remove""", String.Empty)
Dim ctrls As New Control
ctrls = Page.ParseControl(sLookupResult.ToString)
PlaceHolder1.Controls.Add(ctrls)
At this point, I can check ctrls.controls.count and see there are 67 controls. I know most of those are probably literal controls, but I also know there are a couple of dozen LinkButtons, IMGs, etc. But when I look at PlaceHolder1.Controls.Count there is
only 1. I suppose it's right since my page works perfectly.
At this point, my page loads and displays. Images appears and some are clickable. All of my LinkButtons appear and are also clickable. When I click on a LinkButton and it does a PostBack to the same page, I want to reference the LinkButton that caused the
PostBack. I can see the ID of the button in the Request.Form collection and I can see it in Request.Form("__EVENTTARGET"). The ID is:
ctl00$PageContent$ID0EABAAA_clb1
The actual ID specified during the XSLT transform was ID0EABAAA_clb1. I understand that the ctl00$PageContent$ is mangled by ASP due to the controls being in a Master Page and placed in the ContentPlaceHolder above. I also understand that FindControl only finds
controls in the current container. But I just cannot find this control anywhere. I've tried just about every stupid combination I can think of. I've written code to iterate through the controls in each container on my page. I just can't find these controls.
:S
I won't show the other 12 or so stupid combinations I've tried - but nothing is working for me. I'm suspicious it has something to do with there being 67 controls passed to ParseControl and there being only 1 control in PlaceHolder1 after doing Controls.Add.
Can anyone tell me how to find this control? Do I have to CType or DirectCast the result of FindControl? I've done that as well, but it made no difference.
Mike, I have tried finding the control with your example. It results in a null reference exception.
Possibly/probably I'm misunderstanding the life cycle of controls and I'm doing some 'assuming' that my dynamically created controls still exist, at some point, early on after a PostBack. Maybe I'm unable to find the control I'm looking for because it really
doesn't exist anymore.
The reason I'm creating controls dynamically is because I don't know how many controls (if any) that I need to create until I get user interaction.
I've tried sending my REST request in Page_Init, Page_InitComplete, Page_Preload, Page_Load and Page_LoadComplete. I have the same problem no matter where I initiate it, except that LoadComplete is just too late since there are problems dynamically creating
the controls at this point. To give you a better idea of what I'm doing I'll try to outline my page logic.
Send REST request
Receive response and transform XML using XSLTransform
Page.ParseControl(TransformedResponse)
PlaceHolder.Controls.Add(control returned by ParseControl)
There's other logic involved and this is only a general outline of what I'm doing, but at this point the page is created and it now displays.
The user views the page and clicks on a link to drill down further on more details, view larger images, etc. Every one of the links work and it seems that the above code has worked. All of the controls seem to be added to the control tree properly. A click
causes a PostBack and now I have to determine what the user is wanting to do. The page is going to change no matter what the user clicks - again, there will be varying numbers of controls with different URLs and there may be no controls needed at all - but
the only way I'm going to know is by making another REST request to the server and transforming its XML response again.
My problem is that click event handlers on any of these dynamically created controls don't fire until after the Page_Load event. Waiting this long before knowing what my next REST request is way too late. Sure, I can make the REST request at any time, the
server doesn't care about that, nor does my own code, but it is the response from the server that jurisdicts what controls/how many/if any will be created for the page to load again. There is no request to make until the user's interaction is determined. Determining
this in Page_LoadComplete is too late for me to be dynamically creating the next page's controls. This rules out the use of event handlers where I could use the CommandName and CommandArgument attributes to tell my codebehind what to do next...
Because I can't use event handlers, each of the dynamically created controls specifies PostBackURL. I am relying on the page posting back to itself. I use the Request.Form object to determine which control ID was clicked. I think Request.Form("__EVENTTARGET")
will always contain this? At least, it has been consistent in naming the clicked control... aside from the ctl00$PageContent$ being mangled into it's ID. I can strip that out - my controls have unique IDs without that.
Now I have the control ID. I can get this as early as Page_Init and so this is where I am trying to use FindControl. The control that was clicked is a custom web control I created. It inherits LinkButton, but contains a couple of extra attributes I need
to use in my code logic. These custom attributes are populated in the XSLT - I don't have that information in my program code. One of these attributes is 'Action', another is 'PartNum'. I don't really care how I get this information back. I don't care if these
controls really exist anymore, but I need to see these values in my code, not just the ID, I need to see all the attributes of the control that was clicked and that's why I'm using FindControl. I can view the source code of the page that's created and the
values are all intact. All of the attributes contain the information needed if a user clicks on them.
ViewState obviously contains the information I need. Are you saying that my dynamically created controls no longer exist IMMEDIATELY on postback? That will cause a huge problem with my code logic since I no longer have the previous REST response available
to recreate the controls. I have no idea how I could recreate them - aside from sending another REST request with 'old' data just to recreate controls. Technically, that REST response would be outdated and no longer needed - except to recreate the controls
from the 'previous' page. Oh brother, this is giving me a headache...
Does the control have to exist in order for me to have access to those custom attributes? I know ViewState contains the settings of the last page's controls. I just want that data for the specific control that was clicked - then I apply logic that forms
the new REST request, get an XML response, XSLTranform it, ParseControl the string and Controls.Add which makes the new page with all new controls. Response.Form isn't going to contain this information. I could probably use Request.QueryString and add this
information to the PostBackURL when it's formed during XSLTransform, but I've been avoiding that.
I'm sorry if I've confused you. Trying to explain this to you has confused even myself, except that I'm starting to realize that the controls I created dynamically may not be available AT ALL on PostBack. I was thinking they existed early on after PostBack
and that's where I'm running into problems?
Since I don't want to perform another REST request for all the XML I've already processed, is it adequate to save the control types and their IDs immediately after performing the initial ParseControl and Controls.Add, then save this info to a Session variable.
Then upon a PostBack, either recreate all of the controls using their ID only and of course, the correct control type? I've been reading and it seems like the most important thing is that the same control type gets created with the same ID as when it was created
initially.
If this is true, is it enough to create only the single control that triggered the PostBack as indicated by Request.Form("__EVENTTARGET")? Afterall, it's the only one I need the data from. There is some state contained in Hidden fields that I've created,
but I can retrieve all of that without any problem. I just need the values contained in the control that was clicked to cause PostBack.
And, if this is enough, when will ViewState reinitialize the control with its settings? I mean, so I recreate the dynamically created control as the same control type and with the same ID, and I do this in Page_Init. Will ViewState data for this control
be available by Page_Load? The reason I'm asking is because I've just tried doing this and I'm not seeing any ViewState being applied to the control. Just wondering what to expect! :P
Thanks Mike. I'll read the FAQs/Posts you gave me. Maybe they will answer these questions.
Mike, your threads on this topic are fantastic. Thank you for pointing me toward them!
I'm beginning to wonder though, I'm not looking at Viewstate in the way it works. My custom web controls inherit LinkButton. An example one of these controls may be:
If I recreate the control, say in Page_Init and specify only the ID, will the attributes of this control be filled in with ViewState data? Specifically, 'Action' and 'PartNum'. Or is this not the purpose of ViewState? I saw your code specifically set all of
that information in your example code.
With all the fantastic advancements in technologies made over the years, I feel I'm slowly being pushed into passing my needed data in the URL and looking at it with Request.QueryString. The method has been around for 20 years that
I know of - even before ASP, or IE for that matter! *grin*
I was able to determine why FindControl wasn't finding any of my dynamically created controls.
Dim ctrl As New Control
ctrl = Page.ParseControl(sbLookupResult.ToString)
ctrl.ID = "Test"
PlaceHolder1.Controls.Add(ctrl)
Of all the examples I've seen on using ParseControl (at least as it relates to being used after calling XSLTransform), I don't think I've seen a single one that specifies an ID on the control either before or after calling ParseControl. By not specifying
an ID there wasn't really a straightforward way of referencing it inside of PlaceHolder1.
Mike, your suggestion should have worked - to reference it with an index of (0). Not sure why that didn't work...
Still, my problem with recreating my dynamically created controls after PostBack hasn't been resolved.
Marked as answer by rwkiii on Mar 20, 2012 08:37 PM
rwkiii
Member
55 Points
43 Posts
FindControl Usage with ParseControl
Mar 18, 2012 11:24 AM|LINK
Am using Master Pages in a Web Application and I need to find control references in my codebehind. Problem is I can't find them with FindControl no matter what I try.
Page Layout:
<asp:Content ID="Content5" ContentPlaceHolderID="PageContent" runat="server">
<div>
<asp:PlaceHolder ID="PlaceHolder1" runat="server">
My controls are in here
</asp:PlaceHolder>
</div>
</asp:Content>
From my codebehind, I am using XSLT to transform XML data:
sLookupResult.Append(oAPI.XSLTransform(Server.MapPath("~/XSLT/Lookup.xslt"), arguments)) sLookupResult.Replace("xmlns:asp=""remove""", String.Empty) Dim ctrls As New Control ctrls = Page.ParseControl(sLookupResult.ToString) PlaceHolder1.Controls.Add(ctrls)At this point, I can check ctrls.controls.count and see there are 67 controls. I know most of those are probably literal controls, but I also know there are a couple of dozen LinkButtons, IMGs, etc. But when I look at PlaceHolder1.Controls.Count there is only 1. I suppose it's right since my page works perfectly.
At this point, my page loads and displays. Images appears and some are clickable. All of my LinkButtons appear and are also clickable. When I click on a LinkButton and it does a PostBack to the same page, I want to reference the LinkButton that caused the PostBack. I can see the ID of the button in the Request.Form collection and I can see it in Request.Form("__EVENTTARGET"). The ID is:
ctl00$PageContent$ID0EABAAA_clb1
The actual ID specified during the XSLT transform was ID0EABAAA_clb1. I understand that the ctl00$PageContent$ is mangled by ASP due to the controls being in a Master Page and placed in the ContentPlaceHolder above. I also understand that FindControl only finds controls in the current container. But I just cannot find this control anywhere. I've tried just about every stupid combination I can think of. I've written code to iterate through the controls in each container on my page. I just can't find these controls. :S
Page.Master.FindControl("ID0EABAAA_clb1")
Page.Master("PageContent").FindControl("ID0EABAAA_clb1")
Page.Master("PageContent").FindControl("PlaceHolder1").FindControl("ID0EABAAA_clb1")
I won't show the other 12 or so stupid combinations I've tried - but nothing is working for me. I'm suspicious it has something to do with there being 67 controls passed to ParseControl and there being only 1 control in PlaceHolder1 after doing Controls.Add.
Can anyone tell me how to find this control? Do I have to CType or DirectCast the result of FindControl? I've done that as well, but it made no difference.
Thx!
mbanavige
All-Star
134964 Points
15421 Posts
ASPInsiders
Moderator
MVP
Re: FindControl Usage with ParseControl
Mar 18, 2012 12:52 PM|LINK
Make sure you are recreating those dynamic controls on each and every postback.
and have you tried this to find your control:
PlaceHolder1.Controls(0).FindControl("ID0EABAAA_clb1")
rwkiii
Member
55 Points
43 Posts
Re: FindControl Usage with ParseControl
Mar 18, 2012 09:30 PM|LINK
Mike, I have tried finding the control with your example. It results in a null reference exception.
Possibly/probably I'm misunderstanding the life cycle of controls and I'm doing some 'assuming' that my dynamically created controls still exist, at some point, early on after a PostBack. Maybe I'm unable to find the control I'm looking for because it really doesn't exist anymore.
The reason I'm creating controls dynamically is because I don't know how many controls (if any) that I need to create until I get user interaction.
I've tried sending my REST request in Page_Init, Page_InitComplete, Page_Preload, Page_Load and Page_LoadComplete. I have the same problem no matter where I initiate it, except that LoadComplete is just too late since there are problems dynamically creating the controls at this point. To give you a better idea of what I'm doing I'll try to outline my page logic.
Init, InitComplete, PreLoad, Load (I've tried all):
Send REST request
Receive response and transform XML using XSLTransform
Page.ParseControl(TransformedResponse)
PlaceHolder.Controls.Add(control returned by ParseControl)
There's other logic involved and this is only a general outline of what I'm doing, but at this point the page is created and it now displays.
The user views the page and clicks on a link to drill down further on more details, view larger images, etc. Every one of the links work and it seems that the above code has worked. All of the controls seem to be added to the control tree properly. A click causes a PostBack and now I have to determine what the user is wanting to do. The page is going to change no matter what the user clicks - again, there will be varying numbers of controls with different URLs and there may be no controls needed at all - but the only way I'm going to know is by making another REST request to the server and transforming its XML response again.
My problem is that click event handlers on any of these dynamically created controls don't fire until after the Page_Load event. Waiting this long before knowing what my next REST request is way too late. Sure, I can make the REST request at any time, the server doesn't care about that, nor does my own code, but it is the response from the server that jurisdicts what controls/how many/if any will be created for the page to load again. There is no request to make until the user's interaction is determined. Determining this in Page_LoadComplete is too late for me to be dynamically creating the next page's controls. This rules out the use of event handlers where I could use the CommandName and CommandArgument attributes to tell my codebehind what to do next...
Because I can't use event handlers, each of the dynamically created controls specifies PostBackURL. I am relying on the page posting back to itself. I use the Request.Form object to determine which control ID was clicked. I think Request.Form("__EVENTTARGET") will always contain this? At least, it has been consistent in naming the clicked control... aside from the ctl00$PageContent$ being mangled into it's ID. I can strip that out - my controls have unique IDs without that.
Now I have the control ID. I can get this as early as Page_Init and so this is where I am trying to use FindControl. The control that was clicked is a custom web control I created. It inherits LinkButton, but contains a couple of extra attributes I need to use in my code logic. These custom attributes are populated in the XSLT - I don't have that information in my program code. One of these attributes is 'Action', another is 'PartNum'. I don't really care how I get this information back. I don't care if these controls really exist anymore, but I need to see these values in my code, not just the ID, I need to see all the attributes of the control that was clicked and that's why I'm using FindControl. I can view the source code of the page that's created and the values are all intact. All of the attributes contain the information needed if a user clicks on them.
ViewState obviously contains the information I need. Are you saying that my dynamically created controls no longer exist IMMEDIATELY on postback? That will cause a huge problem with my code logic since I no longer have the previous REST response available to recreate the controls. I have no idea how I could recreate them - aside from sending another REST request with 'old' data just to recreate controls. Technically, that REST response would be outdated and no longer needed - except to recreate the controls from the 'previous' page. Oh brother, this is giving me a headache...
Does the control have to exist in order for me to have access to those custom attributes? I know ViewState contains the settings of the last page's controls. I just want that data for the specific control that was clicked - then I apply logic that forms the new REST request, get an XML response, XSLTranform it, ParseControl the string and Controls.Add which makes the new page with all new controls. Response.Form isn't going to contain this information. I could probably use Request.QueryString and add this information to the PostBackURL when it's formed during XSLTransform, but I've been avoiding that.
I'm sorry if I've confused you. Trying to explain this to you has confused even myself, except that I'm starting to realize that the controls I created dynamically may not be available AT ALL on PostBack. I was thinking they existed early on after PostBack and that's where I'm running into problems?
Thx Mike.
mbanavige
All-Star
134964 Points
15421 Posts
ASPInsiders
Moderator
MVP
Re: FindControl Usage with ParseControl
Mar 18, 2012 09:38 PM|LINK
Before you can use FindControl, that control needs to exist in the page somewhere.
Dynamic controls do not survive postbacks so they need to be recreated each and every time you run the page.
So if you're using FindControl during Init, you'd need to have already run the ParseControl code and added those dynamic controls to your placeholder.
Here's a quick faq on the page lifecycle:
http://forums.asp.net/t/1191194.aspx/1?FAQ+Sequence+that+events+are+raised+for+Pages+UserControls+MasterPages+and+HttpModules
And here's one on adding dynamic controls:
http://forums.asp.net/t/1186195.aspx/1?FAQ+Why+do+dynamic+controls+disappear+on+postback+and+not+raise+events+
rwkiii
Member
55 Points
43 Posts
Re: FindControl Usage with ParseControl
Mar 19, 2012 12:34 AM|LINK
Since I don't want to perform another REST request for all the XML I've already processed, is it adequate to save the control types and their IDs immediately after performing the initial ParseControl and Controls.Add, then save this info to a Session variable. Then upon a PostBack, either recreate all of the controls using their ID only and of course, the correct control type? I've been reading and it seems like the most important thing is that the same control type gets created with the same ID as when it was created initially.
If this is true, is it enough to create only the single control that triggered the PostBack as indicated by Request.Form("__EVENTTARGET")? Afterall, it's the only one I need the data from. There is some state contained in Hidden fields that I've created, but I can retrieve all of that without any problem. I just need the values contained in the control that was clicked to cause PostBack.
And, if this is enough, when will ViewState reinitialize the control with its settings? I mean, so I recreate the dynamically created control as the same control type and with the same ID, and I do this in Page_Init. Will ViewState data for this control be available by Page_Load? The reason I'm asking is because I've just tried doing this and I'm not seeing any ViewState being applied to the control. Just wondering what to expect! :P
Thanks Mike. I'll read the FAQs/Posts you gave me. Maybe they will answer these questions.
rwkiii
Member
55 Points
43 Posts
Re: FindControl Usage with ParseControl
Mar 19, 2012 01:24 AM|LINK
Mike, your threads on this topic are fantastic. Thank you for pointing me toward them!
I'm beginning to wonder though, I'm not looking at Viewstate in the way it works. My custom web controls inherit LinkButton. An example one of these controls may be:
<a Action="Lookup" PartNum="84-5899DUP3G" id="ctl00_PageContent_ID0EEBAAA_customlinkbutton1" href="javascript:WebForm_DoPostBackWithOptions(new WebForm_PostBackOptions("ctl00$PageContent$ID0EEBAAA_customlinkbutton1", "", false, "", "Lookup.aspx", false, true))">Sennheiser CX300-W Earbuds (White)</a>
If I recreate the control, say in Page_Init and specify only the ID, will the attributes of this control be filled in with ViewState data? Specifically, 'Action' and 'PartNum'. Or is this not the purpose of ViewState? I saw your code specifically set all of that information in your example code.
With all the fantastic advancements in technologies made over the years, I feel I'm slowly being pushed into passing my needed data in the URL and looking at it with Request.QueryString. The method has been around for 20 years that I know of - even before ASP, or IE for that matter! *grin*
rwkiii
Member
55 Points
43 Posts
Re: FindControl Usage with ParseControl
Mar 19, 2012 02:22 AM|LINK
I was able to determine why FindControl wasn't finding any of my dynamically created controls.
Dim ctrl As New Control ctrl = Page.ParseControl(sbLookupResult.ToString) ctrl.ID = "Test" PlaceHolder1.Controls.Add(ctrl)Of all the examples I've seen on using ParseControl (at least as it relates to being used after calling XSLTransform), I don't think I've seen a single one that specifies an ID on the control either before or after calling ParseControl. By not specifying an ID there wasn't really a straightforward way of referencing it inside of PlaceHolder1.
Mike, your suggestion should have worked - to reference it with an index of (0). Not sure why that didn't work...
Still, my problem with recreating my dynamically created controls after PostBack hasn't been resolved.