How do I add events to nested server controls? (ASP.Net) - c#

I am building a custom master page type control i.e. sort of like a datagrid but should be easier to add custom functionality into it. It's going great but part of the desired functionality is to have a paging control that switches on and off and part of that control would be a textbox that displays the current page number and on TextChanged redirects to the new page of the dataset.
The problem I'm having is that technically the textbox which has its event fired is embedded in a control that is embedded in the control you actually put on the page sort of like
Page
|
Display Control
|
Paging Control
|
Textbox
Buried all the way down there the event is not firing. Worse the postback javascript isn't even being written onto the page (Nothing on the page posts back so far this is the only bit that really needs to).
I've been trawling around Google for quite a while now and picked up that I need to implement INamingContainer (done) and I need to add the control into the page's control tree (is Pre_Init too late for that? When's a good time to Add the Control to the page?) then the event should fire, apparently. But I've been unable to find an example of best practice on this there are quite a few near misses where people are having button angst but this isn't a button.
So can anyone point me in the direction of getting a control embedded in a control embedded in a control added to a page to behave properly?

You need INamingContainer only if you plan to add more than one instance of your custom control to the same page. What it does is enabling unique id generation so you don't end up with controls with the same ID. I recommend you inherit from CompositeControl when creating your custom control.
Pre_Init is not too late. Actually it is pretty early considering the lifecycle. You can instantiate custom controls and add them to the live controls collection in a lot of places. I would recommend you do it in Page_Init (before viewstate is loaded) or Page_Load(after view state is loaded). Even if you add it later in the page lifecycle the control will catch up in events.
To subscribe to events of child controls you can use the FindControl method:
MyControl myControl = Page.FindControl("MyControl1");
TextBox textBox = myControl.FindControl("TextBox1") as TextBox;

The answer was a combination of the above answer and the comment on the original question. The vital thing to get the event to happen is to make sure that your controls (parent and child) inherit from CompositeControl and INamingContainer e.g.
public partial myControl:CompositeControl,INamingContainer
etc...
Then you override your composite control's CreateChildControls() method and create your controls and do the wire up there. This will ensure correct bubbling. and mean that the event handling takes place within your comoposite control...

Related

Move UserControl to another parent in Page Stack

I have a page of controls that I want to be able to move specific controls by ID to a different parent control server side.
A simple example being another control loads 2 controls vertically ontop of each other. I want a module that can reference those two modules by ID and lay them out horizontally.
I assume this would have to done after the Page_Load() event so that all the controls are loaded.
I think I can accomplish this with a recursive control.FindControl() but I'm thinking there is a more elegant way.
If you plan to dynamically move controls around the page then it’s better to programmatically set them on page where it’s needed.
You should be adding controls in the OnInit method that is run before the page load.
Roughly, the OnInit method would look like
a) check the state of the page and decide where to add control
b) add control where needed

When to use PreRender over PageLoad?

Related question: Get All Web Controls of a Specific Type on a Page
In the question above I asked how I can get all controls, works like a charm but something just doesn't quite fit so I thought it might be me. I have the following code but it's not manipulating the controls on the page but in my theory it should work.
List<DropDownList> allControls = new List<DropDownList>();
ControlEnhancer.GetControlList<DropDownList>(Page.Controls, allControls);
foreach (DropDownList childControl in allControls)
{
foreach (ListItem li in childControl.Items)
{
li.Attributes.Add("title", li.Text);
}
childControl.Attributes.Add("onmouseover", "this.title=this.options[this.selectedIndex].title");
}
Thats the code, GetControlList() code you can get from the related question which shows how it gets all controls, its just my manipulation. I am trying to get all dropdownlist listitems and add a title to them so I can have a tooltip.
It's a quick fix for IE8 and below which cuts of long text in drop down boxes.
Page_Load happens often too soon; Page_PreRender is the last moment before the page's HTML is actually rendered for the browser and in many cases is the best place to set attributes on user controls.
This because during the web form (page) life cycle there are other events in the page (and in the user controls contained in the page...) which sometimes remove/replace/overwrite (really) those attributes so the only way you can get those attributes to the browser is to append them after all other life cycle events have been fired and handled, in the Page_PreRender.
Actually, even PreRender might be too early in some cases (e.g. you could have DropDownList controls added to the control tree during databinding of controls that use DataSourceID).
There are two further events that might be more appropriate:
PreRenderComplete. At this point, all controls are created and the page is ready to render.
SaveStateComplete. Occurs after view state and control state have been saved. Any changes you make here won't be persisted to view state.
In your example (adding client-side attributes), I'd use the SaveStateComplete event to avoid unnecessary view state bloat.

MasterPages Inherit another MasterPage

I am working on a website that has many MasterPages and one of them inherits from another master page and I am trying to add a control to the parent masterpage like this
myMasterPage.Page.Controls.Add(new LiteralControl(theSuperStoreString.ToString()));
But I get the following error
The control collection cannot be modified during DataBind, Init, Load, PreRender or Unload phases.
How can i do this?
Thank you
First of all you should never add any control to your parent control. It violates separation of concerns principle, breaks integrity and is the first sign of bad design (and troubles in the project).
If you have to modify Controls of your parent or another control use event, state property or interface to delegate this request to the object which is really responsible for it (parent MasterPage in that case).
Regarding to your question place a control in your parent MasterPage and show it only when someone request for it.
I think you are trying to add the control too late in the cycle, try the preinit event
added as I read your comment
here is a link to the lifecycle
http://msdn.microsoft.com/en-us/library/ms178472.aspx
so essentially what we are saying is you need to overrid ethe preinit event and add/create new controls there rather than the later events (where it seems you are currently trying to add)

accessing data from a dynamically added custom control

I am writing a survey generating system in asp.net. i asked an ealier question about the best way to create controls that can be passed about as variables. This was problematic with user controls so i was advised to use custom controls and a quick way to do this was to inherit from the panel control and just add a bunch of standard controls the the controls collection by overriding the CreateChildControls method. This way i could create my "survey" controls,which are basically Questions in the survey. THe question controls are then dynamically added to the page. This all works well but know i have come to the point that i want to try and retrieve the values from these controls and i seem to be lost in a nether world of of viewstates and page lifecycles. I can ensure that the dynamically added text boxes have a known ID, however even if i add the parent control in the page init handler the CreateChildControls method does not run until after until after the viewstate is loaded. I cannot work out how to retreive the values from these text boxes.
You can call the EnsureChildControls method on the init handler of your control to ensure CreateChildControls is called before the ViewState is loaded.
You certainly seem to be doing this the hard way. TextBox values are not saved in the Viewstate, they are posted in the request.
Why aren't you using a UserControl here? So you can "pass" it somewhere? what exactly are you trying to do?

Adding a list of UserControls with buttons to a PlaceHolder - no event?

I want to make use of "complex" usercontrols with more than one control element within. It's the same control I will reuse in the list, and I have a PlaceHolder control for it already.
I can add the control with LoadControl(path to .ascx) - no problem.
I can through my custom properties get/set access the embedded Labels, too, so I can initialize each control perfectly.
But when adding LinkButtons, I get into trouble/problems.
When I click the button, I do get a "submit" of the page rendering the controls; but the control's own button event does not seem to fire (or at least PageLoad on the parent page seems to fire first?) - I can't figure out where my event goes or where to look for a name/ID or parameter for this button.
How come or what am I doing wrong here?
I've made a "fake button" now by using a label more with a "hardcoded A HREF" with an ID in the URL, but I would like to learn what event I need to catch and where or how to init the button, because I want to be able to use "default ASP.NET" controls for these usercontrols (hopefully without too much patchwork-coding)...
The only reason that events get "lost" is because your controls are not being recreated in such a manner that ASP.Net can associate the event with the control after the postback. It does so through the use of the ID property.
In other words, you're doing one of three things wrong:
1) You're assigning the ID's of your linkbuttons differently during the creating phase in Init after the postback
2) You're creating your linkbuttons dynamically using code, but you're doing it after the Init phase of the page lifecycle, so that your controls do not participate in ViewState.
3) You're re-binding the datasource of the parent control containing the linkbuttons on every postback. Use if (!IsPostBack) to prevent rebinding it every time.
Without seeing your code I can't give anything more specific than that unfortunately.

Categories