Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > ASP .Net > ASP .Net Building Controls > Problem overriding render method to format literal content of nested tags in custom control

Reply
Thread Tools

Problem overriding render method to format literal content of nested tags in custom control

 
 
Stephen Miller
Guest
Posts: n/a
 
      01-19-2004
Aleddandro,

I can't override the OnBubbleEvent method in NestedChildCollection
because it inherits from CollectionBase. However, following your logic
and the MSDN sample, I have gone ahead and overridden the
OnBubbleEvent in the template NestedChild, which inherits from Control
as well as the container, Nested. I've also added a new sealed class
NestedChildCommandEventArgs inheriting from CommandEventArgs and it's
deligate NestedChildCommandEventHandler.

I've added page tracing to the OnBubbleEvent method in the Nested and
NestedChild objects, however this doesn't appear to be called. Is
event bubbling useful only when I know in advance what server controls
are being nested in my control? The primary goal of my control is to
allow the developer to place any literal or control within the
NestedChild object and have them encapsulated within the Nested
object's formatting (in this case a table).

To demonstrate, I have put a sample (which hopefully will make clear
what I'm trying to achieve) at www.3la.com .au/tabs.aspx.

If my nested container contains a Button control or a RadioButtonList
or a DataGrid then, CreateChildControls is processed in the PreRender
phase as expected.

However, If my nested container contains a Button control, TextBox and
its associated RequiredFieldValidator then CreateChildControls is
called in the ProcessPostData phase before the SelectedIndexChanged
event is handled in PostBackEvent.

As such, it appears that under some circumstances CreateChildControls
is called in the ProcessPostData phase, rather then in the PreRender
phase of the control life cycle.

The MSDN guide to the control life cycle
(ms-help://MS.VSCC/MS.MSDNVS/cpguide/html/cpconcontrolexecutionlifecycle.htm)
does not list the CreateChildControls method "because it is called
whenever the ASP.NET page framework needs to create the controls tree
and this method call is not limited to a specific phase in a control's
lifecycle. For example, CreateChildControls can be invoked when
loading a page, during data binding, or during rendering".

Of course this is very unhelpful if CreateChildControls depends on a
PostBackEvent.

I have found another reference to this problem (without solution) at
http://support.softartisans.com/kbview.aspx?ID=671 (see under the
apply named section, 'take heed')

So how do I control when CreateChildControls is called?

I had thought that I could call CreateChildControls (twice if
necessary) from onPreRender (after we have handled
RaisePostBackEvent), but this results in "Multiple controls with the
same ID" error. I don't understand this as CreateChildControls
includes a Controls.Clear() statement, which I assumed would reset the
control's naming hierarchy).

I then tried moving CreateChildControls to
IPostBackEventHandler.RaisePostBackEvent and bizarrely this fixed the
problem with CreateChildControls being called once, in the
PostBackEvent phase. Again I don't understand why this actually works,
given that the ProcessPostData phase (where CreateChildControls was
being called during some PostBack events) happens before the
PostBackEvent phase (Note the sample referred to above doesn't have
this workaround).

Moving right along, I've now encountered a problem with a DataGrid
nested in my template container. When I click away, I'm now getting
the error:

"Failed to load viewstate. The control tree into which viewstate is
being loaded must match the control tree that was used to save
viewstate during the previous request. For example, when adding
controls dynamically, the controls added during a post-back must match
the type and position of the controls added during the initial
request."

I'll start on this problem tomorrow

Stephen


"Alessandro Zifiglio" <(E-Mail Removed)> wrote in message news:<EEIOb.4937$(E-Mail Removed)>...
> Stephen, when I made that last post, I was in a hurry and missed out a few
> things. I have given the code another look and here are some corrections.
> However note that I have not taken the time to test this with your code, but
> that i have just copied and pasted the essential parts that you need to
> implement, and I have tried to adapt it to your code.
>
> Also note that the example on MSDN if you tried to compile it along with its
> designer class --you will end up with errors, this is because there are
> numerous bugs in the code and i had to make many corrections before i was
> able to run that code myself --however it demonstrates how to code and puts
> you in the right direction on many techniques like with using
> templates --databinding --associating a designer to your control, event
> bubbling etc
>
> Here is a much better step by step --the one i did last I missed out a few
> things like declaring the static event EventItemCommand as object and i
> removed some un-necessary code --Still did not test --the rest is up to you
> to get this working
>
> //First bubble events in NestedChildCollection
> //Note how it looks only for a CommandEventArgs
> //and passes that information to the
> //class TemplatedListCommandEventArgs which will in turn delegate
> //the event.
> public class NestedChildCollection : CollectionBase {
>
> public NestedChild this[int nIndex]{
> get { return (NestedChild) base.List[nIndex]; }
> }
> public void Add(NestedChild child){
> base.List.Add(child);
> }
>
> public int IndexOf(NestedChild child){
> return base.List.IndexOf(child);
> }
> }
>
> protected override bool OnBubbleEvent(object source, EventArgs e) {
> if (e is CommandEventArgs) {
> // Add the information about Item to CommandEvent.
>
> TemplatedListCommandEventArgs args =
> new TemplatedListCommandEventArgs(this, source,
> (CommandEventArgs)e);
>
> RaiseBubbleEvent(this, args);
> return true;
> }
> return false;
> }
>
> }
>
>
> //Now the class TemplatedListCommandEventArgs --this is where we passed the
> captured event, and now we delegate it.
>
> public sealed class TemplatedListCommandEventArgs : CommandEventArgs {
> private NestedChildCollection item;
> private object commandSource;
>
> public TemplatedListCommandEventArgs(NestedChildCollectio n item,
> object
> commandSource, CommandEventArgs originalArgs) :
> base(originalArgs) {
> this.item = item;
> this.commandSource = commandSource;
> }
>
>
> public object CommandSource {
> get {
> return commandSource;
> }
> }
> }
>
> public delegate void TemplatedListCommandEventHandler(object source,
> TemplatedListCommandEventArgs e);
>
>
> //now in your class(nested) first declare the static event variable
> EventItemCommand as object
> //Next Override OnBubbleEvent method and
> //bubble the events to the container(that is the page the control sits)
>
> private static readonly object EventItemCommand = new object();
>
>
>
> //note how OnItemCommand is called when the TemplatedListCommandEventHandler
> //is the one that fired --the one that we bubbled
> //in the Template
>
> protected override bool OnBubbleEvent(object source, EventArgs e) {
> // Handle events raised by children by overriding OnBubbleEvent.
>
> bool handled = false;
>
> if (e is TemplatedListCommandEventArgs) {
> TemplatedListCommandEventArgs ce =
> (TemplatedListCommandEventArgs)e;
>
> OnItemCommand(ce);
> handled = true;
> }
>
> return handled;
> }
>
> //now the OnItemCommand --the one that we called in the above method :
> //this will raise the bubbled Event to the client with
> //the handler and all
>
> protected virtual void OnItemCommand(TemplatedListCommandEventArgs e) {
> TemplatedListCommandEventHandler onItemCommandHandler =
> (TemplatedListCommandEventHandler)Events[EventItemCommand];
> if (onItemCommandHandler != null) onItemCommandHandler(this, e);
> }
>
>
> //expose the handler TemplatedListCommandEventHandler to the client(the
> page)
>
> [
> Category("Action"),
> Description("Raised when a CommandEvent occurs within the
> Template.")
> ]
> public event TemplatedListCommandEventHandler ItemCommand {
> add {
> Events.AddHandler(EventItemCommand, value);
> }
> remove {
> Events.RemoveHandler(EventItemCommand, value);
> }
> }
>
>
>
> ------------------------------------------------------Finish----------------
> --------------------------------------
>
>
> protected void MyList_ItemCreated(object sender,
> TemplatedListCommandEventArgs e) {
> if (e.CommandName == "save"){
> response.write("save button was clicked, lets do something")
> }
>
> }
>
>
> "Alessandro Zifiglio" <(E-Mail Removed)> wrote in
> message news:l_bOb.4409$(E-Mail Removed)...
> > Now that the naming issue has been resolved by PeterBlum --your second
> > problem is the events being fired in your nestedchild controls. For any
> > events that fire in your template(nestedchild) you need to bubble them up

> to
> > the container(nested), you can then either take action in nested if this

> is
> > the last destination or bubble them futher up to the page, that is the
> > container of nested.
> >
> > to handle or to raise the bubbled event, you must override the

> OnBubbleEvent
> > method for your control.
> >
> > Now always building up on the same code you posted --the first one that

> is,
> > you have a button in your template --the clickevent for the button will

> fire
> > in your template --override the OnBubbleEvent method there and have it

> fire
> > in the container "nested" --in nested override the OnBUbbleEvent again and
> > send it up to its container(the page where the control sits)
> > You do not need to implement the IPostBackEventHandler for this. There is

> a
> > very nice example on msdn which goes into all the details. However its

> very
> > vast so i'm coping out the steps you need to take. For more look at the
> > example to which i'm including the link after the code below :
> > //First bubble events in NestedChildCollection
> > //Note how it looks only for a CommandEventArgs
> > //and passes that information to the
> > //class TemplatedListCommandEventArgs which will in turn delegate
> > //the event.
> > public class NestedChildCollection : CollectionBase {
> >
> > public NestedChild this[int nIndex]{
> > get { return (NestedChild) base.List[nIndex]; }
> > }
> > public void Add(NestedChild child){
> > base.List.Add(child);
> > }
> >
> > public int IndexOf(NestedChild child){
> > return base.List.IndexOf(child);
> > }
> > }
> >
> > protected override bool OnBubbleEvent(object source, EventArgs e) {
> > if (e is CommandEventArgs) {
> > // Add the information about Item to CommandEvent.
> >
> > TemplatedListCommandEventArgs args =
> > new TemplatedListCommandEventArgs(this, source,
> > (CommandEventArgs)e);
> >
> > RaiseBubbleEvent(this, args);
> > return true;
> > }
> > return false;
> > }
> >
> > }
> >
> >
> > //Now the class TemplatedListCommandEventArgs --this is where we passed

> the
> > captured event, and now we delegate it.
> >
> > public sealed class TemplatedListCommandEventArgs : CommandEventArgs {
> >
> > private TemplatedListItem item;
> > private object commandSource;
> >
> > public TemplatedListCommandEventArgs(TemplatedListItem item,

> object
> > commandSource, CommandEventArgs originalArgs) :
> > base(originalArgs) {
> > this.item = item;
> > this.commandSource = commandSource;
> > }
> >
> > public TemplatedListItem Item {
> > get {
> > return item;
> > }
> > }
> >
> > public object CommandSource {
> > get {
> > return commandSource;
> > }
> > }
> > }
> >
> > public delegate void TemplatedListCommandEventHandler(object source,
> > TemplatedListCommandEventArgs e);
> >
> >
> > //now in your class(nested) first Override OnBubbleEvent method and
> > //bubble the events to the container(that is the page the control sits)
> >
> >
> >
> >
> > //note how OnItemCommand is called when the

> TemplatedListCommandEventHandler
> > //is the one that fired --the one that we bubbled
> > //in the Template
> >
> > protected override bool OnBubbleEvent(object source, EventArgs e) {
> > // Handle events raised by children by overriding

> OnBubbleEvent.
> >
> > bool handled = false;
> >
> > if (e is TemplatedListCommandEventArgs) {
> > TemplatedListCommandEventArgs ce =
> > (TemplatedListCommandEventArgs)e;
> >
> > OnItemCommand(ce);
> > handled = true;
> > }
> >
> > return handled;
> > }
> >
> > //now the OnItemCommand --the one that we called in the above method :
> > //this will raise the bubbled Event to the client with
> > //the handler and all
> >
> > protected virtual void OnItemCommand(TemplatedListCommandEventArgs e) {
> > TemplatedListCommandEventHandler onItemCommandHandler =
> > (TemplatedListCommandEventHandler)Events[EventItemCommand];
> > if (onItemCommandHandler != null) onItemCommandHandler(this,

> e);
> > }
> >
> >
> > //expose the handler TemplatedListCommandEventHandler to the client(the
> > page)
> >
> > [
> > Category("Action"),
> > Description("Raised when a CommandEvent occurs within the
> > Template.")
> > ]
> > public event TemplatedListCommandEventHandler ItemCommand {
> > add {
> > Events.AddHandler(EventItemCommand, value);
> > }
> > remove {
> > Events.RemoveHandler(EventItemCommand, value);
> > }
> > }
> >
> >
> >
> > ------------------------------------------------------Finish--------------

> --
> > --------------------------------------
> >
> > now in your page where the control sits you have the event handler use it,
> > all events fired in your templates can be captured here --you now have one
> > unique place to search for events as it is with templated controls, you

> can
> > have many controls in there that fire events, and you do not want an event
> > handler for every control in there, instead you want ONE handler that will
> > handle all --the trick it is to use the commandName for your buttons and
> > then look for this command name here --that way you can differentiate
> > between buttons and take action accordingly. So on the button that you

> have
> > used in your template pass use the CommandName property to like say if you
> > set the CommandName="save" then you can check if the save button was the

> one
> > clicked, see code below :
> >
> > protected void MyList_ItemCreated(object sender,

> TemplatedListItemEventArgs
> > e) {
> >
> > if (e.CommandName == "save"){
> > response.write("save button was clicked, lets do something")
> > }

>
> > }
> >
> > This is the templated databound sample, surely you will learn a lot

> besides
> > the event bubbling ;P
> > Ok --please look at the sample code on MSDN.
> >
> > If you have the docs then the link is :
> >

> ms-help://MS.VSCC/MS.MSDNVS/cpguide/html/cpcontemplateddataboundcontrolsampl
> > e.htm
> >
> >

> http://msdn.microsoft.com/library/de...trolsample.asp
> >
> > Not very hard to follow once you get a hang of it
> >
> >
> >
> >
> >
> > "Peter Blum" <(E-Mail Removed)> wrote in message
> > news:(E-Mail Removed)...
> > > Right now you're dealing with the ID property, as the error message

> tells
> > > you.
> > >
> > > If you do not assign an ID property, ASP.NET will automatically assign

> one
> > > for your. However, if you need to refer to that ID in some other

> control,
> > > like in Validator.ControlToValidator, that doesn't help.
> > >
> > > I don't see your code that creates the validators and textboxes. So I

> don't
> > > know how you are assigning Ids there. But I can see how you are

> assigning
> > > the ID to the table. Perhaps we can learn from that:
> > > HtmlTable table = new HtmlTable();
> > > table.ID = this.UniqueID;
> > >
> > >
> > > Don't assign an ID to either UniqueID or ClientID properties. The ID

> should
> > > be a unique name within the naming container. ASP.NET will convert it

> into
> a
> > > unique ID in ClientID and UniqueID. Additionally, UniqueID introduces a
> > > format that doesn't work well in the client ID side attribute. (When you

> see
> > > the HTML <input type='text' id=[from ClientID] name=[from UniqueID] />)
> > >
> > > Here's what I like to do for IDs of child controls within a custom

> control.
> > > Use the ID of custom control + "_" + some name.
> > > For example:
> > > HtmlTable table = new HtmlTable();
> > > table.ID = this.ID + "_Table";
> > >
> > > Because your custom control's ID will already be unique within the

> naming
> > > container, so will its child controls.
> > >
> > > --- Peter Blum
> > > www.PeterBlum.com
> > > Email: http://www.velocityreviews.com/forums/(E-Mail Removed)
> > >
> > > "Stephen Miller" <(E-Mail Removed)> wrote in message
> > > news:(E-Mail Removed) m...
> > > > Peter,
> > > >
> > > > Again, thanks for your on going help.
> > > >
> > > > I have been struggling with this problem for 3+ months and on someone
> > > > else's advice purchased "Developing Microsoft ASP.NET Server Controls
> > > > and Components". Following the book religiously, I started again from
> > > > the "Hello World" example and worked forward to recreate my problem.
> > > > Kothari & Datye's touches on controls "whose nested content does not
> > > > correspond to properties" on pages 332-7, but the example provided is
> > > > very trivial and doesn't address any of the issues I've encountered.
> > > > If I've missed something, please point me to the section.
> > > >
> > > > My control uses the ParseChildrenAttribute attribute with the
> > > > declaration '[ParseChildren(true, "NestedChild")', which defines a
> > > > public property named 'NestedChild', with nested (child) elements
> > > > corresponding to child elements of the 'NestedChild' property. The
> > > > idea with the '_NestedChildren' property is to add each nested child
> > > > element to a collection, which I can iterate through and hide or show
> > > > based on additional conditions. I have extra logic here, which I have
> > > > omitted for clarity.
> > > >
> > > > As you suggested, I have move my code to CreateChildControls. The
> > > > control renders ok in design time, but fails in runtime at
> > > > FillNamedControlsTable with the error message "Multiple controls with
> > > > the same ID 'myControl1' were found. FindControl requires that
> > > > controls have unique IDs". With the RequiredFieldValidator removed,
> > > > the control renders but generates the same error with the buttons
> > > > onClick event. Looking at the source code I notice that the command
> > > > button has now rendered as:
> > > >
> > > > <input type="submit" name="myControl1:cmdTest1" value="Test"
> > > > id="myControl1_cmdTest1" />
> > > >
> > > > I can't compile my code behind with an event handling
> > > > myControl1_cmdTest1.Click, because the compiler is expecting
> > > > cmdTest1.click.
> > > >
> > > > My code for CreateChildControls now looks like:
> > > >
> > > > protected override void CreateChildControls() {
> > > >
> > > > this.Controls.Clear();
> > > > this.Controls.Add(new LiteralControl("<b>before</b><br /><hr><br
> > > > />"));
> > > >
> > > > foreach (NestedChild child in _NestedChildren) {
> > > > //this.Controls.Add(child);
> > > >
> > > > this.Controls.Add(new LiteralControl("* " + child.Caption +
> > > > "<BR>"));
> > > >
> > > > // Display the contents of child in a single cell table
> > > > HtmlTableCell td = new HtmlTableCell();
> > > > td.Style.Add("width", "250px");
> > > > td.Style.Add("height", "100px");
> > > > td.Controls.Add(child);
> > > >
> > > > // Add the table cell to a new table row
> > > > HtmlTableRow tr = new HtmlTableRow();
> > > > tr.Controls.Add(td);
> > > >
> > > > // Add the table row to a new table
> > > > HtmlTable table = new HtmlTable();
> > > > table.ID = this.UniqueID;
> > > > table.Width = this.Width.ToString();
> > > > table.Height = this.Height.ToString();
> > > > table.CellSpacing = 2;
> > > > table.CellPadding = 2;
> > > > table.Border = 1;
> > > > table.Style.Add("background-color", "#C0C0C0");
> > > > table.Controls.Add(tr);
> > > >
> > > > this.Controls.Add(table);
> > > >
> > > > }
> > > > this.Controls.Add(new LiteralControl("<br /><hr><br
> > > > /><b>after</b>"));
> > > > }
> > > >
> > > > Any other suggestions?
> > > >
> > > > Regards,
> > > >
> > > > Stephen
> > > >
> > >
> > >

> >
> >

 
Reply With Quote
 
 
 
 
Alessandro Zifiglio
Guest
Posts: n/a
 
      01-19-2004
Stephen, Event bubbling is what you want. That is bubble any events fired by
any control within the template and take it up to the container. Because any
control can be within your template --you will require one handler, where
you can check who fired the event and take action. The events are not firing
in your case coz as i said that code on MSDN is buggy. My coding language of
preference is VB.NET so all corrections i have made are in vb.net ;P

However one possible reason why the event is not firing is because of the
following line, or atleast i think --
protected virtual void OnItemCommand(NestedChildCommandEventArgs e) {
NestedChildCommandEventHandler onItemCommandHandler =
(NestedChildCommandEventHandler)Events[EventItemCommand];
if (onItemCommandHandler != null) onItemCommandHandler(this,
e);
}

Change it to :

protected virtual void OnItemCommand(NestedChildCommandEventArgs e) {
if (EventItemCommand != null)
{
EventItemCommand(this,e);
}

Events should be bubbled up now and it should fire --hopefully ;P

Nice demo by the way --very clever of you to use tracing to showcase what is
happening --however I do not know what your code looks like and if i saw it
then i'd have to run tests --and that will be time consuming. Looks like you
are almost there. Try checking some old posts on the google GROUPS section.
PeterBlum has brought up this topic regarding the CreateChildControls method
a long time ago--see if any of the questions he brough up answers your
questions. In one of the old posts ofcourse. I'm sure its all in there.

That error you are getting with the viewstate could be because you are not
rebuilding your controls exactly how it were before postback
Make sure all controls are added exactly the way they were on the initial
request, just like how the error states ;P
I'm not much help there

Glad I could help.
"Stephen Miller" <(E-Mail Removed)> wrote in message
news:(E-Mail Removed) om...
> Aleddandro,
>
> I can't override the OnBubbleEvent method in NestedChildCollection
> because it inherits from CollectionBase. However, following your logic
> and the MSDN sample, I have gone ahead and overridden the
> OnBubbleEvent in the template NestedChild, which inherits from Control
> as well as the container, Nested. I've also added a new sealed class
> NestedChildCommandEventArgs inheriting from CommandEventArgs and it's
> deligate NestedChildCommandEventHandler.
>
> I've added page tracing to the OnBubbleEvent method in the Nested and
> NestedChild objects, however this doesn't appear to be called. Is
> event bubbling useful only when I know in advance what server controls
> are being nested in my control? The primary goal of my control is to
> allow the developer to place any literal or control within the
> NestedChild object and have them encapsulated within the Nested
> object's formatting (in this case a table).
>
> To demonstrate, I have put a sample (which hopefully will make clear
> what I'm trying to achieve) at www.3la.com .au/tabs.aspx.
>
> If my nested container contains a Button control or a RadioButtonList
> or a DataGrid then, CreateChildControls is processed in the PreRender
> phase as expected.
>
> However, If my nested container contains a Button control, TextBox and
> its associated RequiredFieldValidator then CreateChildControls is
> called in the ProcessPostData phase before the SelectedIndexChanged
> event is handled in PostBackEvent.
>
> As such, it appears that under some circumstances CreateChildControls
> is called in the ProcessPostData phase, rather then in the PreRender
> phase of the control life cycle.
>
> The MSDN guide to the control life cycle
>

(ms-help://MS.VSCC/MS.MSDNVS/cpguide/html/cpconcontrolexecutionlifecycle.htm
)
> does not list the CreateChildControls method "because it is called
> whenever the ASP.NET page framework needs to create the controls tree
> and this method call is not limited to a specific phase in a control's
> lifecycle. For example, CreateChildControls can be invoked when
> loading a page, during data binding, or during rendering".
>
> Of course this is very unhelpful if CreateChildControls depends on a
> PostBackEvent.
>
> I have found another reference to this problem (without solution) at
> http://support.softartisans.com/kbview.aspx?ID=671 (see under the
> apply named section, 'take heed')
>
> So how do I control when CreateChildControls is called?
>
> I had thought that I could call CreateChildControls (twice if
> necessary) from onPreRender (after we have handled
> RaisePostBackEvent), but this results in "Multiple controls with the
> same ID" error. I don't understand this as CreateChildControls
> includes a Controls.Clear() statement, which I assumed would reset the
> control's naming hierarchy).
>
> I then tried moving CreateChildControls to
> IPostBackEventHandler.RaisePostBackEvent and bizarrely this fixed the
> problem with CreateChildControls being called once, in the
> PostBackEvent phase. Again I don't understand why this actually works,
> given that the ProcessPostData phase (where CreateChildControls was
> being called during some PostBack events) happens before the
> PostBackEvent phase (Note the sample referred to above doesn't have
> this workaround).
>
> Moving right along, I've now encountered a problem with a DataGrid
> nested in my template container. When I click away, I'm now getting
> the error:
>
> "Failed to load viewstate. The control tree into which viewstate is
> being loaded must match the control tree that was used to save
> viewstate during the previous request. For example, when adding
> controls dynamically, the controls added during a post-back must match
> the type and position of the controls added during the initial
> request."
>
> I'll start on this problem tomorrow
>
> Stephen
>
>
> "Alessandro Zifiglio" <(E-Mail Removed)> wrote in

message news:<EEIOb.4937$(E-Mail Removed)>...
> > Stephen, when I made that last post, I was in a hurry and missed out a

few
> > things. I have given the code another look and here are some

corrections.
> > However note that I have not taken the time to test this with your code,

but
> > that i have just copied and pasted the essential parts that you need to
> > implement, and I have tried to adapt it to your code.
> >
> > Also note that the example on MSDN if you tried to compile it along with

its
> > designer class --you will end up with errors, this is because there are
> > numerous bugs in the code and i had to make many corrections before i

was
> > able to run that code myself --however it demonstrates how to code and

puts
> > you in the right direction on many techniques like with using
> > templates --databinding --associating a designer to your control, event
> > bubbling etc
> >
> > Here is a much better step by step --the one i did last I missed out a

few
> > things like declaring the static event EventItemCommand as object and i
> > removed some un-necessary code --Still did not test --the rest is up to

you
> > to get this working
> >
> > //First bubble events in NestedChildCollection
> > //Note how it looks only for a CommandEventArgs
> > //and passes that information to the
> > //class TemplatedListCommandEventArgs which will in turn delegate
> > //the event.
> > public class NestedChildCollection : CollectionBase {
> >
> > public NestedChild this[int nIndex]{
> > get { return (NestedChild) base.List[nIndex]; }
> > }
> > public void Add(NestedChild child){
> > base.List.Add(child);
> > }
> >
> > public int IndexOf(NestedChild child){
> > return base.List.IndexOf(child);
> > }
> > }
> >
> > protected override bool OnBubbleEvent(object source, EventArgs e) {
> > if (e is CommandEventArgs) {
> > // Add the information about Item to CommandEvent.
> >
> > TemplatedListCommandEventArgs args =
> > new TemplatedListCommandEventArgs(this, source,
> > (CommandEventArgs)e);
> >
> > RaiseBubbleEvent(this, args);
> > return true;
> > }
> > return false;
> > }
> >
> > }
> >
> >
> > //Now the class TemplatedListCommandEventArgs --this is where we passed

the
> > captured event, and now we delegate it.
> >
> > public sealed class TemplatedListCommandEventArgs : CommandEventArgs {
> > private NestedChildCollection item;
> > private object commandSource;
> >
> > public TemplatedListCommandEventArgs(NestedChildCollectio n item,
> > object
> > commandSource, CommandEventArgs originalArgs) :
> > base(originalArgs) {
> > this.item = item;
> > this.commandSource = commandSource;
> > }
> >
> >
> > public object CommandSource {
> > get {
> > return commandSource;
> > }
> > }
> > }
> >
> > public delegate void TemplatedListCommandEventHandler(object source,
> > TemplatedListCommandEventArgs e);
> >
> >
> > //now in your class(nested) first declare the static event variable
> > EventItemCommand as object
> > //Next Override OnBubbleEvent method and
> > //bubble the events to the container(that is the page the control sits)
> >
> > private static readonly object EventItemCommand = new object();
> >
> >
> >
> > //note how OnItemCommand is called when the

TemplatedListCommandEventHandler
> > //is the one that fired --the one that we bubbled
> > //in the Template
> >
> > protected override bool OnBubbleEvent(object source, EventArgs e) {
> > // Handle events raised by children by overriding

OnBubbleEvent.
> >
> > bool handled = false;
> >
> > if (e is TemplatedListCommandEventArgs) {
> > TemplatedListCommandEventArgs ce =
> > (TemplatedListCommandEventArgs)e;
> >
> > OnItemCommand(ce);
> > handled = true;
> > }
> >
> > return handled;
> > }
> >
> > //now the OnItemCommand --the one that we called in the above method :
> > //this will raise the bubbled Event to the client with
> > //the handler and all
> >
> > protected virtual void OnItemCommand(TemplatedListCommandEventArgs e) {
> > TemplatedListCommandEventHandler onItemCommandHandler =
> > (TemplatedListCommandEventHandler)Events[EventItemCommand];
> > if (onItemCommandHandler != null) onItemCommandHandler(this,

e);
> > }
> >
> >
> > //expose the handler TemplatedListCommandEventHandler to the client(the
> > page)
> >
> > [
> > Category("Action"),
> > Description("Raised when a CommandEvent occurs within the
> > Template.")
> > ]
> > public event TemplatedListCommandEventHandler ItemCommand {
> > add {
> > Events.AddHandler(EventItemCommand, value);
> > }
> > remove {
> > Events.RemoveHandler(EventItemCommand, value);
> > }
> > }
> >
> >
> >

>
> ------------------------------------------------------Finish--------------

--
> > --------------------------------------
> >
> >
> > protected void MyList_ItemCreated(object sender,
> > TemplatedListCommandEventArgs e) {
> > if (e.CommandName == "save"){
> > response.write("save button was clicked, lets do something")
> > }
> >
> > }
> >
> >
> > "Alessandro Zifiglio" <(E-Mail Removed)> wrote in
> > message news:l_bOb.4409$(E-Mail Removed)...
> > > Now that the naming issue has been resolved by PeterBlum --your

second
> > > problem is the events being fired in your nestedchild controls. For

any
> > > events that fire in your template(nestedchild) you need to bubble them

up
> > to
> > > the container(nested), you can then either take action in nested if

this
> > is
> > > the last destination or bubble them futher up to the page, that is the
> > > container of nested.
> > >
> > > to handle or to raise the bubbled event, you must override the

> > OnBubbleEvent
> > > method for your control.
> > >
> > > Now always building up on the same code you posted --the first one

that
> > is,
> > > you have a button in your template --the clickevent for the button

will
> > fire
> > > in your template --override the OnBubbleEvent method there and have it

> > fire
> > > in the container "nested" --in nested override the OnBUbbleEvent again

and
> > > send it up to its container(the page where the control sits)
> > > You do not need to implement the IPostBackEventHandler for this. There

is
> > a
> > > very nice example on msdn which goes into all the details. However its

> > very
> > > vast so i'm coping out the steps you need to take. For more look at

the
> > > example to which i'm including the link after the code below :
> > > //First bubble events in NestedChildCollection
> > > //Note how it looks only for a CommandEventArgs
> > > //and passes that information to the
> > > //class TemplatedListCommandEventArgs which will in turn delegate
> > > //the event.
> > > public class NestedChildCollection : CollectionBase {
> > >
> > > public NestedChild this[int nIndex]{
> > > get { return (NestedChild) base.List[nIndex]; }
> > > }
> > > public void Add(NestedChild child){
> > > base.List.Add(child);
> > > }
> > >
> > > public int IndexOf(NestedChild child){
> > > return base.List.IndexOf(child);
> > > }
> > > }
> > >
> > > protected override bool OnBubbleEvent(object source, EventArgs e) {
> > > if (e is CommandEventArgs) {
> > > // Add the information about Item to CommandEvent.
> > >
> > > TemplatedListCommandEventArgs args =
> > > new TemplatedListCommandEventArgs(this, source,
> > > (CommandEventArgs)e);
> > >
> > > RaiseBubbleEvent(this, args);
> > > return true;
> > > }
> > > return false;
> > > }
> > >
> > > }
> > >
> > >
> > > //Now the class TemplatedListCommandEventArgs --this is where we

passed
> > the
> > > captured event, and now we delegate it.
> > >
> > > public sealed class TemplatedListCommandEventArgs : CommandEventArgs {
> > >
> > > private TemplatedListItem item;
> > > private object commandSource;
> > >
> > > public TemplatedListCommandEventArgs(TemplatedListItem item,

> > object
> > > commandSource, CommandEventArgs originalArgs) :
> > > base(originalArgs) {
> > > this.item = item;
> > > this.commandSource = commandSource;
> > > }
> > >
> > > public TemplatedListItem Item {
> > > get {
> > > return item;
> > > }
> > > }
> > >
> > > public object CommandSource {
> > > get {
> > > return commandSource;
> > > }
> > > }
> > > }
> > >
> > > public delegate void TemplatedListCommandEventHandler(object

source,
> > > TemplatedListCommandEventArgs e);
> > >
> > >
> > > //now in your class(nested) first Override OnBubbleEvent method and
> > > //bubble the events to the container(that is the page the control

sits)
> > >
> > >
> > >
> > >
> > > //note how OnItemCommand is called when the

> > TemplatedListCommandEventHandler
> > > //is the one that fired --the one that we bubbled
> > > //in the Template
> > >
> > > protected override bool OnBubbleEvent(object source, EventArgs e) {
> > > // Handle events raised by children by overriding

> > OnBubbleEvent.
> > >
> > > bool handled = false;
> > >
> > > if (e is TemplatedListCommandEventArgs) {
> > > TemplatedListCommandEventArgs ce =
> > > (TemplatedListCommandEventArgs)e;
> > >
> > > OnItemCommand(ce);
> > > handled = true;
> > > }
> > >
> > > return handled;
> > > }
> > >
> > > //now the OnItemCommand --the one that we called in the above method :
> > > //this will raise the bubbled Event to the client with
> > > //the handler and all
> > >
> > > protected virtual void OnItemCommand(TemplatedListCommandEventArgs e)

{
> > > TemplatedListCommandEventHandler onItemCommandHandler =
> > > (TemplatedListCommandEventHandler)Events[EventItemCommand];
> > > if (onItemCommandHandler != null)

onItemCommandHandler(this,
> > e);
> > > }
> > >
> > >
> > > //expose the handler TemplatedListCommandEventHandler to the

client(the
> > > page)
> > >
> > > [
> > > Category("Action"),
> > > Description("Raised when a CommandEvent occurs within the
> > > Template.")
> > > ]
> > > public event TemplatedListCommandEventHandler ItemCommand {
> > > add {
> > > Events.AddHandler(EventItemCommand, value);
> > > }
> > > remove {
> > > Events.RemoveHandler(EventItemCommand, value);
> > > }
> > > }
> > >
> > >
> > >

> >

> ------------------------------------------------------Finish--------------
> > --
> > > --------------------------------------
> > >
> > > now in your page where the control sits you have the event handler use

it,
> > > all events fired in your templates can be captured here --you now have

one
> > > unique place to search for events as it is with templated controls,

you
> > can
> > > have many controls in there that fire events, and you do not want an

event
> > > handler for every control in there, instead you want ONE handler that

will
> > > handle all --the trick it is to use the commandName for your buttons

and
> > > then look for this command name here --that way you can differentiate
> > > between buttons and take action accordingly. So on the button that you

> > have
> > > used in your template pass use the CommandName property to like say if

you
> > > set the CommandName="save" then you can check if the save button was

the
> > one
> > > clicked, see code below :
> > >
> > > protected void MyList_ItemCreated(object sender,

> > TemplatedListItemEventArgs
> > > e) {
> > >
> > > if (e.CommandName == "save"){
> > > response.write("save button was clicked, lets do something")
> > > }

> >
> > > }
> > >
> > > This is the templated databound sample, surely you will learn a lot

> > besides
> > > the event bubbling ;P
> > > Ok --please look at the sample code on MSDN.
> > >
> > > If you have the docs then the link is :
> > >

> >

ms-help://MS.VSCC/MS.MSDNVS/cpguide/html/cpcontemplateddataboundcontrolsampl
> > > e.htm
> > >
> > >

> >

http://msdn.microsoft.com/library/de...trolsample.asp
> > >
> > > Not very hard to follow once you get a hang of it
> > >
> > >
> > >
> > >
> > >
> > > "Peter Blum" <(E-Mail Removed)> wrote in message
> > > news:(E-Mail Removed)...
> > > > Right now you're dealing with the ID property, as the error message

> > tells
> > > > you.
> > > >
> > > > If you do not assign an ID property, ASP.NET will automatically

assign
> > one
> > > > for your. However, if you need to refer to that ID in some other

> > control,
> > > > like in Validator.ControlToValidator, that doesn't help.
> > > >
> > > > I don't see your code that creates the validators and textboxes. So

I
> > don't
> > > > know how you are assigning Ids there. But I can see how you are

> > assigning
> > > > the ID to the table. Perhaps we can learn from that:
> > > > HtmlTable table = new HtmlTable();
> > > > table.ID = this.UniqueID;
> > > >
> > > >
> > > > Don't assign an ID to either UniqueID or ClientID properties. The ID

> > should
> > > > be a unique name within the naming container. ASP.NET will convert

it
> > into
> > a
> > > > unique ID in ClientID and UniqueID. Additionally, UniqueID

introduces a
> > > > format that doesn't work well in the client ID side attribute. (When

you
> > see
> > > > the HTML <input type='text' id=[from ClientID] name=[from UniqueID]

/>)
> > > >
> > > > Here's what I like to do for IDs of child controls within a custom

> > control.
> > > > Use the ID of custom control + "_" + some name.
> > > > For example:
> > > > HtmlTable table = new HtmlTable();
> > > > table.ID = this.ID + "_Table";
> > > >
> > > > Because your custom control's ID will already be unique within the

> > naming
> > > > container, so will its child controls.
> > > >
> > > > --- Peter Blum
> > > > www.PeterBlum.com
> > > > Email: (E-Mail Removed)
> > > >
> > > > "Stephen Miller" <(E-Mail Removed)> wrote in message
> > > > news:(E-Mail Removed) m...
> > > > > Peter,
> > > > >
> > > > > Again, thanks for your on going help.
> > > > >
> > > > > I have been struggling with this problem for 3+ months and on

someone
> > > > > else's advice purchased "Developing Microsoft ASP.NET Server

Controls
> > > > > and Components". Following the book religiously, I started again

from
> > > > > the "Hello World" example and worked forward to recreate my

problem.
> > > > > Kothari & Datye's touches on controls "whose nested content does

not
> > > > > correspond to properties" on pages 332-7, but the example provided

is
> > > > > very trivial and doesn't address any of the issues I've

encountered.
> > > > > If I've missed something, please point me to the section.
> > > > >
> > > > > My control uses the ParseChildrenAttribute attribute with the
> > > > > declaration '[ParseChildren(true, "NestedChild")', which defines a
> > > > > public property named 'NestedChild', with nested (child) elements
> > > > > corresponding to child elements of the 'NestedChild' property. The
> > > > > idea with the '_NestedChildren' property is to add each nested

child
> > > > > element to a collection, which I can iterate through and hide or

show
> > > > > based on additional conditions. I have extra logic here, which I

have
> > > > > omitted for clarity.
> > > > >
> > > > > As you suggested, I have move my code to CreateChildControls. The
> > > > > control renders ok in design time, but fails in runtime at
> > > > > FillNamedControlsTable with the error message "Multiple controls

with
> > > > > the same ID 'myControl1' were found. FindControl requires that
> > > > > controls have unique IDs". With the RequiredFieldValidator

removed,
> > > > > the control renders but generates the same error with the buttons
> > > > > onClick event. Looking at the source code I notice that the

command
> > > > > button has now rendered as:
> > > > >
> > > > > <input type="submit" name="myControl1:cmdTest1" value="Test"
> > > > > id="myControl1_cmdTest1" />
> > > > >
> > > > > I can't compile my code behind with an event handling
> > > > > myControl1_cmdTest1.Click, because the compiler is expecting
> > > > > cmdTest1.click.
> > > > >
> > > > > My code for CreateChildControls now looks like:
> > > > >
> > > > > protected override void CreateChildControls() {
> > > > >
> > > > > this.Controls.Clear();
> > > > > this.Controls.Add(new LiteralControl("<b>before</b><br /><hr><br
> > > > > />"));
> > > > >
> > > > > foreach (NestedChild child in _NestedChildren) {
> > > > > //this.Controls.Add(child);
> > > > >
> > > > > this.Controls.Add(new LiteralControl("* " + child.Caption +
> > > > > "<BR>"));
> > > > >
> > > > > // Display the contents of child in a single cell table
> > > > > HtmlTableCell td = new HtmlTableCell();
> > > > > td.Style.Add("width", "250px");
> > > > > td.Style.Add("height", "100px");
> > > > > td.Controls.Add(child);
> > > > >
> > > > > // Add the table cell to a new table row
> > > > > HtmlTableRow tr = new HtmlTableRow();
> > > > > tr.Controls.Add(td);
> > > > >
> > > > > // Add the table row to a new table
> > > > > HtmlTable table = new HtmlTable();
> > > > > table.ID = this.UniqueID;
> > > > > table.Width = this.Width.ToString();
> > > > > table.Height = this.Height.ToString();
> > > > > table.CellSpacing = 2;
> > > > > table.CellPadding = 2;
> > > > > table.Border = 1;
> > > > > table.Style.Add("background-color", "#C0C0C0");
> > > > > table.Controls.Add(tr);
> > > > >
> > > > > this.Controls.Add(table);
> > > > >
> > > > > }
> > > > > this.Controls.Add(new LiteralControl("<br /><hr><br
> > > > > /><b>after</b>"));
> > > > > }
> > > > >
> > > > > Any other suggestions?
> > > > >
> > > > > Regards,
> > > > >
> > > > > Stephen
> > > > >
> > > >
> > > >
> > >
> > >



 
Reply With Quote
 
 
 
 
Stephen Miller
Guest
Posts: n/a
 
      01-20-2004
Alessandro,

Thanks for your help, I'm now getting EventBubbling.

I also solved my viewstate problem (see
http://www.denisbauer.com/ASPNETCont...aceholder.aspx
for a how-too) and everything works well now.

This has certainly been a challenging introduction to controls. At
some stage I'll blog a summary of the process.

Regards,

Stephen




"Alessandro Zifiglio" <(E-Mail Removed)> wrote in message news:<42ZOb.5350$(E-Mail Removed)>...
> Stephen, Event bubbling is what you want. That is bubble any events fired by
> any control within the template and take it up to the container. Because any
> control can be within your template --you will require one handler, where
> you can check who fired the event and take action. The events are not firing
> in your case coz as i said that code on MSDN is buggy. My coding language of
> preference is VB.NET so all corrections i have made are in vb.net ;P
>
> However one possible reason why the event is not firing is because of the
> following line, or atleast i think --
> protected virtual void OnItemCommand(NestedChildCommandEventArgs e) {
> NestedChildCommandEventHandler onItemCommandHandler =
> (NestedChildCommandEventHandler)Events[EventItemCommand];
> if (onItemCommandHandler != null) onItemCommandHandler(this,
> e);
> }
>
> Change it to :
>
> protected virtual void OnItemCommand(NestedChildCommandEventArgs e) {
> if (EventItemCommand != null)
> {
> EventItemCommand(this,e);
> }
>
> Events should be bubbled up now and it should fire --hopefully ;P
>
> Nice demo by the way --very clever of you to use tracing to showcase what is
> happening --however I do not know what your code looks like and if i saw it
> then i'd have to run tests --and that will be time consuming. Looks like you
> are almost there. Try checking some old posts on the google GROUPS section.
> PeterBlum has brought up this topic regarding the CreateChildControls method
> a long time ago--see if any of the questions he brough up answers your
> questions. In one of the old posts ofcourse. I'm sure its all in there.
>
> That error you are getting with the viewstate could be because you are not
> rebuilding your controls exactly how it were before postback
> Make sure all controls are added exactly the way they were on the initial
> request, just like how the error states ;P
> I'm not much help there
>
> Glad I could help.
> "Stephen Miller" <(E-Mail Removed)> wrote in message
> news:(E-Mail Removed) om...
> > Aleddandro,
> >
> > I can't override the OnBubbleEvent method in NestedChildCollection
> > because it inherits from CollectionBase. However, following your logic
> > and the MSDN sample, I have gone ahead and overridden the
> > OnBubbleEvent in the template NestedChild, which inherits from Control
> > as well as the container, Nested. I've also added a new sealed class
> > NestedChildCommandEventArgs inheriting from CommandEventArgs and it's
> > deligate NestedChildCommandEventHandler.
> >
> > I've added page tracing to the OnBubbleEvent method in the Nested and
> > NestedChild objects, however this doesn't appear to be called. Is
> > event bubbling useful only when I know in advance what server controls
> > are being nested in my control? The primary goal of my control is to
> > allow the developer to place any literal or control within the
> > NestedChild object and have them encapsulated within the Nested
> > object's formatting (in this case a table).
> >
> > To demonstrate, I have put a sample (which hopefully will make clear
> > what I'm trying to achieve) at www.3la.com .au/tabs.aspx.
> >
> > If my nested container contains a Button control or a RadioButtonList
> > or a DataGrid then, CreateChildControls is processed in the PreRender
> > phase as expected.
> >
> > However, If my nested container contains a Button control, TextBox and
> > its associated RequiredFieldValidator then CreateChildControls is
> > called in the ProcessPostData phase before the SelectedIndexChanged
> > event is handled in PostBackEvent.
> >
> > As such, it appears that under some circumstances CreateChildControls
> > is called in the ProcessPostData phase, rather then in the PreRender
> > phase of the control life cycle.
> >
> > The MSDN guide to the control life cycle
> >

> (ms-help://MS.VSCC/MS.MSDNVS/cpguide/html/cpconcontrolexecutionlifecycle.htm
> )
> > does not list the CreateChildControls method "because it is called
> > whenever the ASP.NET page framework needs to create the controls tree
> > and this method call is not limited to a specific phase in a control's
> > lifecycle. For example, CreateChildControls can be invoked when
> > loading a page, during data binding, or during rendering".
> >
> > Of course this is very unhelpful if CreateChildControls depends on a
> > PostBackEvent.
> >
> > I have found another reference to this problem (without solution) at
> > http://support.softartisans.com/kbview.aspx?ID=671 (see under the
> > apply named section, 'take heed')
> >
> > So how do I control when CreateChildControls is called?
> >
> > I had thought that I could call CreateChildControls (twice if
> > necessary) from onPreRender (after we have handled
> > RaisePostBackEvent), but this results in "Multiple controls with the
> > same ID" error. I don't understand this as CreateChildControls
> > includes a Controls.Clear() statement, which I assumed would reset the
> > control's naming hierarchy).
> >
> > I then tried moving CreateChildControls to
> > IPostBackEventHandler.RaisePostBackEvent and bizarrely this fixed the
> > problem with CreateChildControls being called once, in the
> > PostBackEvent phase. Again I don't understand why this actually works,
> > given that the ProcessPostData phase (where CreateChildControls was
> > being called during some PostBack events) happens before the
> > PostBackEvent phase (Note the sample referred to above doesn't have
> > this workaround).
> >
> > Moving right along, I've now encountered a problem with a DataGrid
> > nested in my template container. When I click away, I'm now getting
> > the error:
> >
> > "Failed to load viewstate. The control tree into which viewstate is
> > being loaded must match the control tree that was used to save
> > viewstate during the previous request. For example, when adding
> > controls dynamically, the controls added during a post-back must match
> > the type and position of the controls added during the initial
> > request."
> >
> > I'll start on this problem tomorrow
> >
> > Stephen
> >
> >
> > "Alessandro Zifiglio" <(E-Mail Removed)> wrote in

> message news:<EEIOb.4937$(E-Mail Removed)>...
> > > Stephen, when I made that last post, I was in a hurry and missed out a

> few
> > > things. I have given the code another look and here are some

> corrections.
> > > However note that I have not taken the time to test this with your code,

> but
> > > that i have just copied and pasted the essential parts that you need to
> > > implement, and I have tried to adapt it to your code.
> > >
> > > Also note that the example on MSDN if you tried to compile it along with

> its
> > > designer class --you will end up with errors, this is because there are
> > > numerous bugs in the code and i had to make many corrections before i

> was
> > > able to run that code myself --however it demonstrates how to code and

> puts
> > > you in the right direction on many techniques like with using
> > > templates --databinding --associating a designer to your control, event
> > > bubbling etc
> > >
> > > Here is a much better step by step --the one i did last I missed out a

> few
> > > things like declaring the static event EventItemCommand as object and i
> > > removed some un-necessary code --Still did not test --the rest is up to

> you
> > > to get this working
> > >
> > > //First bubble events in NestedChildCollection
> > > //Note how it looks only for a CommandEventArgs
> > > //and passes that information to the
> > > //class TemplatedListCommandEventArgs which will in turn delegate
> > > //the event.
> > > public class NestedChildCollection : CollectionBase {
> > >
> > > public NestedChild this[int nIndex]{
> > > get { return (NestedChild) base.List[nIndex]; }
> > > }
> > > public void Add(NestedChild child){
> > > base.List.Add(child);
> > > }
> > >
> > > public int IndexOf(NestedChild child){
> > > return base.List.IndexOf(child);
> > > }
> > > }
> > >
> > > protected override bool OnBubbleEvent(object source, EventArgs e) {
> > > if (e is CommandEventArgs) {
> > > // Add the information about Item to CommandEvent.
> > >
> > > TemplatedListCommandEventArgs args =
> > > new TemplatedListCommandEventArgs(this, source,
> > > (CommandEventArgs)e);
> > >
> > > RaiseBubbleEvent(this, args);
> > > return true;
> > > }
> > > return false;
> > > }
> > >
> > > }
> > >
> > >
> > > //Now the class TemplatedListCommandEventArgs --this is where we passed

> the
> > > captured event, and now we delegate it.
> > >
> > > public sealed class TemplatedListCommandEventArgs : CommandEventArgs {
> > > private NestedChildCollection item;
> > > private object commandSource;
> > >
> > > public TemplatedListCommandEventArgs(NestedChildCollectio n item,
> > > object
> > > commandSource, CommandEventArgs originalArgs) :
> > > base(originalArgs) {
> > > this.item = item;
> > > this.commandSource = commandSource;
> > > }
> > >
> > >
> > > public object CommandSource {
> > > get {
> > > return commandSource;
> > > }
> > > }
> > > }
> > >
> > > public delegate void TemplatedListCommandEventHandler(object source,
> > > TemplatedListCommandEventArgs e);
> > >
> > >
> > > //now in your class(nested) first declare the static event variable
> > > EventItemCommand as object
> > > //Next Override OnBubbleEvent method and
> > > //bubble the events to the container(that is the page the control sits)
> > >
> > > private static readonly object EventItemCommand = new object();
> > >
> > >
> > >
> > > //note how OnItemCommand is called when the

> TemplatedListCommandEventHandler
> > > //is the one that fired --the one that we bubbled
> > > //in the Template
> > >
> > > protected override bool OnBubbleEvent(object source, EventArgs e) {
> > > // Handle events raised by children by overriding

> OnBubbleEvent.
> > >
> > > bool handled = false;
> > >
> > > if (e is TemplatedListCommandEventArgs) {
> > > TemplatedListCommandEventArgs ce =
> > > (TemplatedListCommandEventArgs)e;
> > >
> > > OnItemCommand(ce);
> > > handled = true;
> > > }
> > >
> > > return handled;
> > > }
> > >
> > > //now the OnItemCommand --the one that we called in the above method :
> > > //this will raise the bubbled Event to the client with
> > > //the handler and all
> > >
> > > protected virtual void OnItemCommand(TemplatedListCommandEventArgs e) {
> > > TemplatedListCommandEventHandler onItemCommandHandler =
> > > (TemplatedListCommandEventHandler)Events[EventItemCommand];
> > > if (onItemCommandHandler != null) onItemCommandHandler(this,

> e);
> > > }
> > >
> > >
> > > //expose the handler TemplatedListCommandEventHandler to the client(the
> > > page)
> > >
> > > [
> > > Category("Action"),
> > > Description("Raised when a CommandEvent occurs within the
> > > Template.")
> > > ]
> > > public event TemplatedListCommandEventHandler ItemCommand {
> > > add {
> > > Events.AddHandler(EventItemCommand, value);
> > > }
> > > remove {
> > > Events.RemoveHandler(EventItemCommand, value);
> > > }
> > > }
> > >
> > >
> > >

> >
> > ------------------------------------------------------Finish--------------

> --
> > > --------------------------------------
> > >
> > >
> > > protected void MyList_ItemCreated(object sender,
> > > TemplatedListCommandEventArgs e) {
> > > if (e.CommandName == "save"){
> > > response.write("save button was clicked, lets do something")
> > > }

>
> > > }
> > >
> > >
> > > "Alessandro Zifiglio" <(E-Mail Removed)> wrote in
> > > message news:l_bOb.4409$(E-Mail Removed)...
> > > > Now that the naming issue has been resolved by PeterBlum --your

> second
> > > > problem is the events being fired in your nestedchild controls. For

> any
> > > > events that fire in your template(nestedchild) you need to bubble them

> up
> to
> > > > the container(nested), you can then either take action in nested if

> this
> is
> > > > the last destination or bubble them futher up to the page, that is the
> > > > container of nested.
> > > >
> > > > to handle or to raise the bubbled event, you must override the

> OnBubbleEvent
> > > > method for your control.
> > > >
> > > > Now always building up on the same code you posted --the first one

> that
> is,
> > > > you have a button in your template --the clickevent for the button

> will
> fire
> > > > in your template --override the OnBubbleEvent method there and have it

> fire
> > > > in the container "nested" --in nested override the OnBUbbleEvent again

> and
> > > > send it up to its container(the page where the control sits)
> > > > You do not need to implement the IPostBackEventHandler for this. There

> is
> a
> > > > very nice example on msdn which goes into all the details. However its

> very
> > > > vast so i'm coping out the steps you need to take. For more look at

> the
> > > > example to which i'm including the link after the code below :
> > > > //First bubble events in NestedChildCollection
> > > > //Note how it looks only for a CommandEventArgs
> > > > //and passes that information to the
> > > > //class TemplatedListCommandEventArgs which will in turn delegate
> > > > //the event.
> > > > public class NestedChildCollection : CollectionBase {
> > > >
> > > > public NestedChild this[int nIndex]{
> > > > get { return (NestedChild) base.List[nIndex]; }
> > > > }
> > > > public void Add(NestedChild child){
> > > > base.List.Add(child);
> > > > }
> > > >
> > > > public int IndexOf(NestedChild child){
> > > > return base.List.IndexOf(child);
> > > > }
> > > > }
> > > >
> > > > protected override bool OnBubbleEvent(object source, EventArgs e) {
> > > > if (e is CommandEventArgs) {
> > > > // Add the information about Item to CommandEvent.
> > > >
> > > > TemplatedListCommandEventArgs args =
> > > > new TemplatedListCommandEventArgs(this, source,
> > > > (CommandEventArgs)e);
> > > >
> > > > RaiseBubbleEvent(this, args);
> > > > return true;
> > > > }
> > > > return false;
> > > > }
> > > >
> > > > }
> > > >
> > > >
> > > > //Now the class TemplatedListCommandEventArgs --this is where we

> passed
> the
> > > > captured event, and now we delegate it.
> > > >
> > > > public sealed class TemplatedListCommandEventArgs : CommandEventArgs {
> > > >
> > > > private TemplatedListItem item;
> > > > private object commandSource;
> > > >
> > > > public TemplatedListCommandEventArgs(TemplatedListItem item,

> object
> > > > commandSource, CommandEventArgs originalArgs) :
> > > > base(originalArgs) {
> > > > this.item = item;
> > > > this.commandSource = commandSource;
> > > > }
> > > >
> > > > public TemplatedListItem Item {
> > > > get {
> > > > return item;
> > > > }
> > > > }
> > > >
> > > > public object CommandSource {
> > > > get {
> > > > return commandSource;
> > > > }
> > > > }
> > > > }
> > > >
> > > > public delegate void TemplatedListCommandEventHandler(object

> source,
> > > > TemplatedListCommandEventArgs e);
> > > >
> > > >
> > > > //now in your class(nested) first Override OnBubbleEvent method and
> > > > //bubble the events to the container(that is the page the control

> sits)
> > > >
> > > >
> > > >
> > > >
> > > > //note how OnItemCommand is called when the

> TemplatedListCommandEventHandler
> > > > //is the one that fired --the one that we bubbled
> > > > //in the Template
> > > >
> > > > protected override bool OnBubbleEvent(object source, EventArgs e) {
> > > > // Handle events raised by children by overriding

> OnBubbleEvent.
> > > >
> > > > bool handled = false;
> > > >
> > > > if (e is TemplatedListCommandEventArgs) {
> > > > TemplatedListCommandEventArgs ce =
> > > > (TemplatedListCommandEventArgs)e;
> > > >
> > > > OnItemCommand(ce);
> > > > handled = true;
> > > > }
> > > >
> > > > return handled;
> > > > }
> > > >
> > > > //now the OnItemCommand --the one that we called in the above method :
> > > > //this will raise the bubbled Event to the client with
> > > > //the handler and all
> > > >
> > > > protected virtual void OnItemCommand(TemplatedListCommandEventArgs e)

> {
> > > > TemplatedListCommandEventHandler onItemCommandHandler =
> > > > (TemplatedListCommandEventHandler)Events[EventItemCommand];
> > > > if (onItemCommandHandler != null)

> onItemCommandHandler(this,
> e);
> > > > }
> > > >
> > > >
> > > > //expose the handler TemplatedListCommandEventHandler to the

> client(the
> > > > page)
> > > >
> > > > [
> > > > Category("Action"),
> > > > Description("Raised when a CommandEvent occurs within the
> > > > Template.")
> > > > ]
> > > > public event TemplatedListCommandEventHandler ItemCommand {
> > > > add {
> > > > Events.AddHandler(EventItemCommand, value);
> > > > }
> > > > remove {
> > > > Events.RemoveHandler(EventItemCommand, value);
> > > > }
> > > > }
> > > >
> > > >
> > > >
> > >

> ------------------------------------------------------Finish--------------
> > > --
> > > > --------------------------------------
> > > >
> > > > now in your page where the control sits you have the event handler use

> it,
> > > > all events fired in your templates can be captured here --you now have

> one
> > > > unique place to search for events as it is with templated controls,

> you
> can
> > > > have many controls in there that fire events, and you do not want an

> event
> > > > handler for every control in there, instead you want ONE handler that

> will
> > > > handle all --the trick it is to use the commandName for your buttons

> and
> > > > then look for this command name here --that way you can differentiate
> > > > between buttons and take action accordingly. So on the button that you

> have
> > > > used in your template pass use the CommandName property to like say if

> you
> > > > set the CommandName="save" then you can check if the save button was

> the
> one
> > > > clicked, see code below :
> > > >
> > > > protected void MyList_ItemCreated(object sender,

> TemplatedListItemEventArgs
> > > > e) {
> > > >
> > > > if (e.CommandName == "save"){
> > > > response.write("save button was clicked, lets do something")
> > > > }

>
> > > > }
> > > >
> > > > This is the templated databound sample, surely you will learn a lot

> besides
> > > > the event bubbling ;P
> > > > Ok --please look at the sample code on MSDN.
> > > >
> > > > If you have the docs then the link is :
> > > >
> > >

> ms-help://MS.VSCC/MS.MSDNVS/cpguide/html/cpcontemplateddataboundcontrolsampl
> > > > e.htm
> > > >
> > > >
> > >

> http://msdn.microsoft.com/library/de...trolsample.asp
> > > >
> > > > Not very hard to follow once you get a hang of it
> > > >
> > > >
> > > >
> > > >
> > > >
> > > > "Peter Blum" <(E-Mail Removed)> wrote in message
> > > > news:(E-Mail Removed)...
> > > > > Right now you're dealing with the ID property, as the error message

> tells
> > > > > you.
> > > > >
> > > > > If you do not assign an ID property, ASP.NET will automatically

> assign
> one
> > > > > for your. However, if you need to refer to that ID in some other

> control,
> > > > > like in Validator.ControlToValidator, that doesn't help.
> > > > >
> > > > > I don't see your code that creates the validators and textboxes. So

> I
> don't
> > > > > know how you are assigning Ids there. But I can see how you are

> assigning
> > > > > the ID to the table. Perhaps we can learn from that:
> > > > > HtmlTable table = new HtmlTable();
> > > > > table.ID = this.UniqueID;
> > > > >
> > > > >
> > > > > Don't assign an ID to either UniqueID or ClientID properties. The ID

> should
> > > > > be a unique name within the naming container. ASP.NET will convert

> it
> > > into
> > > a
> > > > > unique ID in ClientID and UniqueID. Additionally, UniqueID

> introduces a
> > > > > format that doesn't work well in the client ID side attribute. (When

> you
> see
> > > > > the HTML <input type='text' id=[from ClientID] name=[from UniqueID]

> />)
> > > > >
> > > > > Here's what I like to do for IDs of child controls within a custom

> control.
> > > > > Use the ID of custom control + "_" + some name.
> > > > > For example:
> > > > > HtmlTable table = new HtmlTable();
> > > > > table.ID = this.ID + "_Table";
> > > > >
> > > > > Because your custom control's ID will already be unique within the

> naming
> > > > > container, so will its child controls.
> > > > >
> > > > > --- Peter Blum
> > > > > www.PeterBlum.com
> > > > > Email: (E-Mail Removed)
> > > > >
> > > > > "Stephen Miller" <(E-Mail Removed)> wrote in message
> > > > > news:(E-Mail Removed) m...
> > > > > > Peter,
> > > > > >
> > > > > > Again, thanks for your on going help.
> > > > > >
> > > > > > I have been struggling with this problem for 3+ months and on

> someone
> > > > > > else's advice purchased "Developing Microsoft ASP.NET Server

> Controls
> > > > > > and Components". Following the book religiously, I started again

> from
> > > > > > the "Hello World" example and worked forward to recreate my

> problem.
> > > > > > Kothari & Datye's touches on controls "whose nested content does

> not
> > > > > > correspond to properties" on pages 332-7, but the example provided

> is
> > > > > > very trivial and doesn't address any of the issues I've

> encountered.
> > > > > > If I've missed something, please point me to the section.
> > > > > >
> > > > > > My control uses the ParseChildrenAttribute attribute with the
> > > > > > declaration '[ParseChildren(true, "NestedChild")', which defines a
> > > > > > public property named 'NestedChild', with nested (child) elements
> > > > > > corresponding to child elements of the 'NestedChild' property. The
> > > > > > idea with the '_NestedChildren' property is to add each nested

> child
> > > > > > element to a collection, which I can iterate through and hide or

> show
> > > > > > based on additional conditions. I have extra logic here, which I

> have
> > > > > > omitted for clarity.
> > > > > >
> > > > > > As you suggested, I have move my code to CreateChildControls. The
> > > > > > control renders ok in design time, but fails in runtime at
> > > > > > FillNamedControlsTable with the error message "Multiple controls

> with
> > > > > > the same ID 'myControl1' were found. FindControl requires that
> > > > > > controls have unique IDs". With the RequiredFieldValidator

> removed,
> > > > > > the control renders but generates the same error with the buttons
> > > > > > onClick event. Looking at the source code I notice that the

> command
> > > > > > button has now rendered as:
> > > > > >
> > > > > > <input type="submit" name="myControl1:cmdTest1" value="Test"
> > > > > > id="myControl1_cmdTest1" />
> > > > > >
> > > > > > I can't compile my code behind with an event handling
> > > > > > myControl1_cmdTest1.Click, because the compiler is expecting
> > > > > > cmdTest1.click.
> > > > > >
> > > > > > My code for CreateChildControls now looks like:
> > > > > >
> > > > > > protected override void CreateChildControls() {
> > > > > >
> > > > > > this.Controls.Clear();
> > > > > > this.Controls.Add(new LiteralControl("<b>before</b><br /><hr><br
> > > > > > />"));
> > > > > >
> > > > > > foreach (NestedChild child in _NestedChildren) {
> > > > > > //this.Controls.Add(child);
> > > > > >
> > > > > > this.Controls.Add(new LiteralControl("* " + child.Caption +
> > > > > > "<BR>"));
> > > > > >
> > > > > > // Display the contents of child in a single cell table
> > > > > > HtmlTableCell td = new HtmlTableCell();
> > > > > > td.Style.Add("width", "250px");
> > > > > > td.Style.Add("height", "100px");
> > > > > > td.Controls.Add(child);
> > > > > >
> > > > > > // Add the table cell to a new table row
> > > > > > HtmlTableRow tr = new HtmlTableRow();
> > > > > > tr.Controls.Add(td);
> > > > > >
> > > > > > // Add the table row to a new table
> > > > > > HtmlTable table = new HtmlTable();
> > > > > > table.ID = this.UniqueID;
> > > > > > table.Width = this.Width.ToString();
> > > > > > table.Height = this.Height.ToString();
> > > > > > table.CellSpacing = 2;
> > > > > > table.CellPadding = 2;
> > > > > > table.Border = 1;
> > > > > > table.Style.Add("background-color", "#C0C0C0");
> > > > > > table.Controls.Add(tr);
> > > > > >
> > > > > > this.Controls.Add(table);
> > > > > >
> > > > > > }
> > > > > > this.Controls.Add(new LiteralControl("<br /><hr><br
> > > > > > /><b>after</b>"));
> > > > > > }
> > > > > >
> > > > > > Any other suggestions?
> > > > > >
> > > > > > Regards,
> > > > > >
> > > > > > Stephen
> > > > > >
> > > > >
> > > > >
> > > >
> > > >

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Skins disappear when overriding Render method John ASP .Net 0 08-28-2006 02:59 PM
JSP Custom Tags as attribute values for other custom tags Dave Java 0 08-14-2006 02:21 PM
Ignore literal content when parsing custom control children? Donal McWeeney ASP .Net Building Controls 2 09-06-2004 08:10 AM
Overriding ImageButton Render in a custom control Peter Colson ASP .Net Web Controls 3 11-18-2003 11:33 PM
Custom Tags within Custom Tags. Ranganath Java 2 10-21-2003 06:14 AM



Advertisments