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

Discussion in 'ASP .Net Building Controls' started by Stephen Miller, Jan 3, 2004.

  1. Gurus,

    I have a custom web control that in turn has nested child controls. I
    want to be able to encapsulated and render any literal HTML and or
    server controls placed between the child control's tags. This works
    fine, unless I add a RequiredFieldValidator control at which point my
    aspx page fails at 'WebControls.BaseValidator.CheckControlValidationProperty'
    with the error message 'Unable to find control id 'TextBox1'
    referenced by the 'ControlToValidate' property of
    'RequiredFieldValidator1'.

    My control 'Nested' contains a collection of 'NestedChild' controls
    with a simple 'Caption' property. I override the 'Render' method on
    the 'Nested' control and iterate though each NestedChild' in the
    collection, outputting the child's literal content to a formatted
    table.

    The full code for my control is (please cut-n-paste to replicate my
    problem):

    using System;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.ComponentModel;
    using System.Collections;
    using System.Collections.Specialized;
    using System.Web.UI.HtmlControls;

    namespace Custom.Web.UI {

    [ToolboxData("<{0}:Nested runat=server></{0}:Nested2>")]
    [ParseChildren(true, "NestedChild"), PersistChildren(false)]
    public class Nested : WebControl, INamingContainer,
    IPostBackDataHandler {

    private NestedChildCollection _NestedChildren = new
    NestedChildCollection();

    #region Properties
    [
    DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
    PersistenceMode(PersistenceMode.InnerDefaultProperty),
    Description("A collection of nested children"),
    ]
    public NestedChildCollection NestedChild {
    get{ return _NestedChildren; }
    }
    #endregion

    #region Overrides

    protected override void CreateChildControls() {
    this.Controls.Clear();
    foreach (NestedChild child in _NestedChildren) {
    this.Controls.Add(child);
    }
    }

    protected override void Render(HtmlTextWriter output) {
    output.Write("<b>before</b><br /><hr><br />");
    //base.Render(output);
    foreach(NestedChild child in _NestedChildren) {
    output.Write("* " + child.Caption + "<BR>");

    // Display the literal contents of NestedChild
    // in a single table cell
    HtmlTableCell td = new HtmlTableCell();
    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.CellSpacing = 2;
    table.CellPadding = 2;
    table.Border = 1;
    table.Style.Add("background-color", "#C0C0C0");
    table.Controls.Add(tr);

    /* FAILS NEXT LINE
    Error: "Unable to find control id 'txtTest1' referenced
    by the 'ControlToValidate' property of 'valTest1'"
    /*
    table.RenderControl(output);
    }
    output.Write("<br /><hr><br /><b>after</b>");
    }
    #endregion

    #region Implements IPostBackDataHandler (postback only)
    bool IPostBackDataHandler.LoadPostData(string strPostDataKey,
    NameValueCollection postDataCollection) {
    return true;
    }
    void IPostBackDataHandler.RaisePostDataChangedEvent() {}
    #endregion
    }

    [ToolboxItem(false), DefaultProperty("Caption")]
    public class NestedChild : Control{
    private String m_strCaption;
    public String Caption {
    get { return m_strCaption; }
    set { m_strCaption = value; }
    }
    }

    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);
    }
    }
    }

    When deployed the aspx page looks like:

    <form id="Form1" method="post" runat="server">
    <P>Nested Control</P>
    <P>
    <cc1:Nested id="Nested1" runat="server">
    <cc1:NestedChild Caption="NestedChild">
    <asp:Button id="cmdTest" runat="server" Text="Generate
    Event"></asp:Button>
    <asp:TextBox id="txtTest" runat="server"></asp:TextBox>
    <asp:RequiredFieldValidator id="valTest" runat="server"
    ControlToValidate="txtTest"
    ErrorMessage="This field is
    Required.">*</asp:RequiredFieldValidator>
    </cc1:NestedChild>
    </cc1:Nested2></P>
    <P>&nbsp;</P>
    <P>
    <asp:label id="lblResult" runat="server">lblResult</asp:label></P>
    </form>

    If I remove 'RequiredFieldValidator' my control render as expected.

    I have noticed, that If I replace the entire formatting logic in the
    'Render' method with a call to 'base.Render(output);', the
    RequiredFieldValidator works as expected.

    I think this is quite a technical problem and I assume that
    'table.RenderControl(output)' is not preforming the necessary
    server-side pre-validation to pass a 'RequiredFieldValidator'. How can
    I do this?

    Regards,

    Stephen
     
    Stephen Miller, Jan 3, 2004
    #1
    1. Advertising

  2. Peter,

    Thanks for the link. I have enjoyed looking at your site, but its not
    going to give me the answer I need. My control needs to be able to
    render any nested literal content the developer throws at it, so I
    have to resolve this problem with MS's validators.

    I have designed my control in such a way that it only needs to
    validate controls in the same naming container but as you noted, the
    error message seems to indicate that the control thinks the textbox
    and validator are in different containers.

    What I find particularly puzzling is that if I simple override the
    render method with 'base.Render(output)' the validator has not problem
    locating the textbox in the container. The problem only seems to arise
    when I attempt to encapsulate the controls in my own formatting with
    'table.RenderControl(output)'.

    Should I be looking at overriding the 'table.RenderControl' method to
    make this work? If so what should I be doing?

    I recently found another commercial product that is able to do what
    I'm trying to achive
    (http://www.infragistics.com/products/navigation.asp?sec=0&cat=3) so I
    know it must be possible (... and I don't want to pay USD $500)

    Thanks,

    Stephen



    "Peter Blum" <> wrote in message news:<O#>...
    > The ControlToValidate property only accepts the ID of another control in the
    > same naming container. I believe that this error is due to naming container
    > problems. While your code seems to enclose both the textbox and validator
    > within the same naming container, the error message says otherwise.
    >
    > FYI: If the naming container is indeed the problem, I have a commercial
    > product that replaces Microsoft's validators to overcome its many
    > limitations. My validators support controls in any naming container. The
    > product is "Professional Validation And More". Details are at
    > http://www.peterblum.com/vam/home.aspx.
    >
    > --- Peter Blum
    > www.PeterBlum.com
    > Email:
    >
    > "Stephen Miller" <> wrote in message
    > news:...
    > > Gurus,
    > >
    > > I have a custom web control that in turn has nested child controls. I
    > > want to be able to encapsulated and render any literal HTML and or
    > > server controls placed between the child control's tags. This works
    > > fine, unless I add a RequiredFieldValidator control at which point my
    > > aspx page fails at

    > 'WebControls.BaseValidator.CheckControlValidationProperty'
    > > with the error message 'Unable to find control id 'TextBox1'
    > > referenced by the 'ControlToValidate' property of
    > > 'RequiredFieldValidator1'.
    > >
    > > My control 'Nested' contains a collection of 'NestedChild' controls
    > > with a simple 'Caption' property. I override the 'Render' method on
    > > the 'Nested' control and iterate though each NestedChild' in the
    > > collection, outputting the child's literal content to a formatted
    > > table.
    > >
    > > The full code for my control is (please cut-n-paste to replicate my
    > > problem):
    > >
    > > using System;
    > > using System.Web.UI;
    > > using System.Web.UI.WebControls;
    > > using System.ComponentModel;
    > > using System.Collections;
    > > using System.Collections.Specialized;
    > > using System.Web.UI.HtmlControls;
    > >
    > > namespace Custom.Web.UI {
    > >
    > > [ToolboxData("<{0}:Nested runat=server></{0}:Nested2>")]
    > > [ParseChildren(true, "NestedChild"), PersistChildren(false)]
    > > public class Nested : WebControl, INamingContainer,
    > > IPostBackDataHandler {
    > >
    > > private NestedChildCollection _NestedChildren = new
    > > NestedChildCollection();
    > >
    > > #region Properties
    > > [
    > >

    > DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
    > > PersistenceMode(PersistenceMode.InnerDefaultProperty),
    > > Description("A collection of nested children"),
    > > ]
    > > public NestedChildCollection NestedChild {
    > > get{ return _NestedChildren; }
    > > }
    > > #endregion
    > >
    > > #region Overrides
    > >
    > > protected override void CreateChildControls() {
    > > this.Controls.Clear();
    > > foreach (NestedChild child in _NestedChildren) {
    > > this.Controls.Add(child);
    > > }
    > > }
    > >
    > > protected override void Render(HtmlTextWriter output) {
    > > output.Write("<b>before</b><br /><hr><br />");
    > > //base.Render(output);
    > > foreach(NestedChild child in _NestedChildren) {
    > > output.Write("* " + child.Caption + "<BR>");
    > >
    > > // Display the literal contents of NestedChild
    > > // in a single table cell
    > > HtmlTableCell td = new HtmlTableCell();
    > > 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.CellSpacing = 2;
    > > table.CellPadding = 2;
    > > table.Border = 1;
    > > table.Style.Add("background-color", "#C0C0C0");
    > > table.Controls.Add(tr);
    > >
    > > /* FAILS NEXT LINE
    > > Error: "Unable to find control id 'txtTest1' referenced
    > > by the 'ControlToValidate' property of 'valTest1'"
    > > /*
    > > table.RenderControl(output);
    > > }
    > > output.Write("<br /><hr><br /><b>after</b>");
    > > }
    > > #endregion
    > >
    > > #region Implements IPostBackDataHandler (postback only)
    > > bool IPostBackDataHandler.LoadPostData(string strPostDataKey,
    > > NameValueCollection postDataCollection) {
    > > return true;
    > > }
    > > void IPostBackDataHandler.RaisePostDataChangedEvent() {}
    > > #endregion
    > > }
    > >
    > > [ToolboxItem(false), DefaultProperty("Caption")]
    > > public class NestedChild : Control{
    > > private String m_strCaption;
    > > public String Caption {
    > > get { return m_strCaption; }
    > > set { m_strCaption = value; }
    > > }
    > > }
    > >
    > > 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);
    > > }
    > > }
    > > }
    > >
    > > When deployed the aspx page looks like:
    > >
    > > <form id="Form1" method="post" runat="server">
    > > <P>Nested Control</P>
    > > <P>
    > > <cc1:Nested id="Nested1" runat="server">
    > > <cc1:NestedChild Caption="NestedChild">
    > > <asp:Button id="cmdTest" runat="server" Text="Generate
    > > Event"></asp:Button>
    > > <asp:TextBox id="txtTest" runat="server"></asp:TextBox>
    > > <asp:RequiredFieldValidator id="valTest" runat="server"
    > > ControlToValidate="txtTest"
    > > ErrorMessage="This field is
    > > Required.">*</asp:RequiredFieldValidator>
    > > </cc1:NestedChild>
    > > </cc1:Nested2></P>
    > > <P>&nbsp;</P>
    > > <P>
    > > <asp:label id="lblResult" runat="server">lblResult</asp:label></P>
    > > </form>
    > >
    > > If I remove 'RequiredFieldValidator' my control render as expected.
    > >
    > > I have noticed, that If I replace the entire formatting logic in the
    > > 'Render' method with a call to 'base.Render(output);', the
    > > RequiredFieldValidator works as expected.
    > >
    > > I think this is quite a technical problem and I assume that
    > > 'table.RenderControl(output)' is not preforming the necessary
    > > server-side pre-validation to pass a 'RequiredFieldValidator'. How can
    > > I do this?
    > >
    > > Regards,
    > >
    > > Stephen
     
    Stephen Miller, Jan 4, 2004
    #2
    1. Advertising

  3. Peter,

    I was under the mistaken impression that I was already overriding
    CreateChildControls to create the controls with the code:

    protected override void CreateChildControls() {
    this.Controls.Clear();
    foreach (NestedChild child in _NestedChildren) {
    this.Controls.Add(child);
    }
    }

    If I leave this override out then the control renders fine (with the
    RequiredFieldValidators) but fails to generate any server side events.
    Moving this code block to OnPreRender (with base.OnPreRend) after
    adding the controls doesn't seem to make any difference.

    I have also tried moving the code in the overridden Render method to
    the overridden RenderContents method. This doesn't appear to have made
    any difference.

    Have I missed the point? Could I beg you for some sample code?

    Thanks,

    Stephen

    "Peter Blum" <> wrote in message news:<>...
    > I think I see the problem. You are creating your control objects in the
    > Render method. Leave the Render method alone. Generally, only use it if you
    > wanted to output explicit HTML (although there are some cases where you can
    > do what you did.) Instead, create the controls in either the
    > CreateChildControls() or OnPreRender method.
    >
    > Here's the actual problem you are having. Validators must have their
    > OnPreRender method run. Because your validators aren't in this control's
    > Controls property, they don't get OnPreRender run. So move your code to
    > OnPreRender and call base.OnPreRender() after those controls are added to
    > the Controls property.
    >
    > --- Peter Blum
    > www.PeterBlum.com
    > Email:
    >
    > "Stephen Miller" <> wrote in message
    > news:...
    > > Peter,
    > >
    > > Thanks for the link. I have enjoyed looking at your site, but its not
    > > going to give me the answer I need. My control needs to be able to
    > > render any nested literal content the developer throws at it, so I
    > > have to resolve this problem with MS's validators.
    > >
    > > I have designed my control in such a way that it only needs to
    > > validate controls in the same naming container but as you noted, the
    > > error message seems to indicate that the control thinks the textbox
    > > and validator are in different containers.
    > >
    > > What I find particularly puzzling is that if I simple override the
    > > render method with 'base.Render(output)' the validator has not problem
    > > locating the textbox in the container. The problem only seems to arise
    > > when I attempt to encapsulate the controls in my own formatting with
    > > 'table.RenderControl(output)'.
    > >
    > > Should I be looking at overriding the 'table.RenderControl' method to
    > > make this work? If so what should I be doing?
    > >
    > > I recently found another commercial product that is able to do what
    > > I'm trying to achive
    > > (http://www.infragistics.com/products/navigation.asp?sec=0&cat=3) so I
    > > know it must be possible (... and I don't want to pay USD $500)
    > >
    > > Thanks,
    > >
    > > Stephen
    > >
    > >
    > >
    > > "Peter Blum" <> wrote in message

    > news:<O#>...
    > > > The ControlToValidate property only accepts the ID of another control in

    > the
    > > > same naming container. I believe that this error is due to naming

    > container
    > > > problems. While your code seems to enclose both the textbox and

    > validator
    > > > within the same naming container, the error message says otherwise.
    > > >
    > > > FYI: If the naming container is indeed the problem, I have a commercial
    > > > product that replaces Microsoft's validators to overcome its many
    > > > limitations. My validators support controls in any naming container. The
    > > > product is "Professional Validation And More". Details are at
    > > > http://www.peterblum.com/vam/home.aspx.
    > > >
    > > > --- Peter Blum
    > > > www.PeterBlum.com
    > > > Email:
    > > >
    > > > "Stephen Miller" <> wrote in message
    > > > news:...
    > > > > Gurus,
    > > > >
    > > > > I have a custom web control that in turn has nested child controls. I
    > > > > want to be able to encapsulated and render any literal HTML and or
    > > > > server controls placed between the child control's tags. This works
    > > > > fine, unless I add a RequiredFieldValidator control at which point my
    > > > > aspx page fails at

    > 'WebControls.BaseValidator.CheckControlValidationProperty'
    > > > > with the error message 'Unable to find control id 'TextBox1'
    > > > > referenced by the 'ControlToValidate' property of
    > > > > 'RequiredFieldValidator1'.
    > > > >
    > > > > My control 'Nested' contains a collection of 'NestedChild' controls
    > > > > with a simple 'Caption' property. I override the 'Render' method on
    > > > > the 'Nested' control and iterate though each NestedChild' in the
    > > > > collection, outputting the child's literal content to a formatted
    > > > > table.
    > > > >
    > > > > The full code for my control is (please cut-n-paste to replicate my
    > > > > problem):
    > > > >
    > > > > using System;
    > > > > using System.Web.UI;
    > > > > using System.Web.UI.WebControls;
    > > > > using System.ComponentModel;
    > > > > using System.Collections;
    > > > > using System.Collections.Specialized;
    > > > > using System.Web.UI.HtmlControls;
    > > > >
    > > > > namespace Custom.Web.UI {
    > > > >
    > > > > [ToolboxData("<{0}:Nested runat=server></{0}:Nested2>")]
    > > > > [ParseChildren(true, "NestedChild"), PersistChildren(false)]
    > > > > public class Nested : WebControl, INamingContainer,
    > > > > IPostBackDataHandler {
    > > > >
    > > > > private NestedChildCollection _NestedChildren = new
    > > > > NestedChildCollection();
    > > > >
    > > > > #region Properties
    > > > > [
    > > > >
    > > >

    > DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
    > > > > PersistenceMode(PersistenceMode.InnerDefaultProperty),
    > > > > Description("A collection of nested children"),
    > > > > ]
    > > > > public NestedChildCollection NestedChild {
    > > > > get{ return _NestedChildren; }
    > > > > }
    > > > > #endregion
    > > > >
    > > > > #region Overrides
    > > > >
    > > > > protected override void CreateChildControls() {
    > > > > this.Controls.Clear();
    > > > > foreach (NestedChild child in _NestedChildren) {
    > > > > this.Controls.Add(child);
    > > > > }
    > > > > }
    > > > >
    > > > > protected override void Render(HtmlTextWriter output) {
    > > > > output.Write("<b>before</b><br /><hr><br />");
    > > > > //base.Render(output);
    > > > > foreach(NestedChild child in _NestedChildren) {
    > > > > output.Write("* " + child.Caption + "<BR>");
    > > > >
    > > > > // Display the literal contents of NestedChild
    > > > > // in a single table cell
    > > > > HtmlTableCell td = new HtmlTableCell();
    > > > > 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.CellSpacing = 2;
    > > > > table.CellPadding = 2;
    > > > > table.Border = 1;
    > > > > table.Style.Add("background-color", "#C0C0C0");
    > > > > table.Controls.Add(tr);
    > > > >
    > > > > /* FAILS NEXT LINE
    > > > > Error: "Unable to find control id 'txtTest1' referenced
    > > > > by the 'ControlToValidate' property of 'valTest1'"
    > > > > /*
    > > > > table.RenderControl(output);
    > > > > }
    > > > > output.Write("<br /><hr><br /><b>after</b>");
    > > > > }
    > > > > #endregion
    > > > >
    > > > > #region Implements IPostBackDataHandler (postback only)
    > > > > bool IPostBackDataHandler.LoadPostData(string strPostDataKey,
    > > > > NameValueCollection postDataCollection) {
    > > > > return true;
    > > > > }
    > > > > void IPostBackDataHandler.RaisePostDataChangedEvent() {}
    > > > > #endregion
    > > > > }
    > > > >
    > > > > [ToolboxItem(false), DefaultProperty("Caption")]
    > > > > public class NestedChild : Control{
    > > > > private String m_strCaption;
    > > > > public String Caption {
    > > > > get { return m_strCaption; }
    > > > > set { m_strCaption = value; }
    > > > > }
    > > > > }
    > > > >
    > > > > 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);
    > > > > }
    > > > > }
    > > > > }
    > > > >
    > > > > When deployed the aspx page looks like:
    > > > >
    > > > > <form id="Form1" method="post" runat="server">
    > > > > <P>Nested Control</P>
    > > > > <P>
    > > > > <cc1:Nested id="Nested1" runat="server">
    > > > > <cc1:NestedChild Caption="NestedChild">
    > > > > <asp:Button id="cmdTest" runat="server" Text="Generate
    > > > > Event"></asp:Button>
    > > > > <asp:TextBox id="txtTest" runat="server"></asp:TextBox>
    > > > > <asp:RequiredFieldValidator id="valTest" runat="server"
    > > > > ControlToValidate="txtTest"
    > > > > ErrorMessage="This field is
    > > > > Required.">*</asp:RequiredFieldValidator>
    > > > > </cc1:NestedChild>
    > > > > </cc1:Nested2></P>
    > > > > <P>&nbsp;</P>
    > > > > <P>
    > > > > <asp:label id="lblResult" runat="server">lblResult</asp:label></P>
    > > > > </form>
    > > > >
    > > > > If I remove 'RequiredFieldValidator' my control render as expected.
    > > > >
    > > > > I have noticed, that If I replace the entire formatting logic in the
    > > > > 'Render' method with a call to 'base.Render(output);', the
    > > > > RequiredFieldValidator works as expected.
    > > > >
    > > > > I think this is quite a technical problem and I assume that
    > > > > 'table.RenderControl(output)' is not preforming the necessary
    > > > > server-side pre-validation to pass a 'RequiredFieldValidator'. How can
    > > > > I do this?
    > > > >
    > > > > Regards,
    > > > >
    > > > > Stephen
     
    Stephen Miller, Jan 7, 2004
    #3
  4. 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


    "Peter Blum" <> wrote in message news:<>...
    > I strongly encourage you to step back and learn the right way to build
    > custom controls. Otherwise, you will be developing code that may require
    > plenty more debugging and maintenance because it doesn't confirm to how
    > Microsoft designed web controls. I recommend the book "Developing ASP.NET
    > Server Controls and Components" from Microsoft Press.
    >
    > In your code, I do not understand why you have the _NestedChildren property.
    > The webcontrol framework automatically supports child controls using the
    > ParseChildrenAttribute. (See the .net docs on this attribute.) I do
    > understand what you are doing in CreateChildControls and that looks OK.
    >
    > Here are my recommendations.
    > - Move the code you have creating the table and its contents into
    > CreateChildControls without removing the existing code.
    > - Any "output.Write()" calls can become LiteralControl objects. (new
    > LiteralControl("<br>"))
    > - Add objects to the Controls collection on your control.
    >
    > Now your validators will be created before OnPreRender occurs. That allows
    > the webcontrol system that Microsoft designed to call each of your
    > validators own OnPreRender method properly, setting up many things that are
    > required for validation.
    >
    > --- Peter Blum
    > www.PeterBlum.com
    > Email:
    > "Stephen Miller" <> wrote in message
    > news:...
    > > Peter,
    > >
    > > I was under the mistaken impression that I was already overriding
    > > CreateChildControls to create the controls with the code:
    > >
    > > protected override void CreateChildControls() {
    > > this.Controls.Clear();
    > > foreach (NestedChild child in _NestedChildren) {
    > > this.Controls.Add(child);
    > > }
    > > }
    > >
    > > If I leave this override out then the control renders fine (with the
    > > RequiredFieldValidators) but fails to generate any server side events.
    > > Moving this code block to OnPreRender (with base.OnPreRend) after
    > > adding the controls doesn't seem to make any difference.
    > >
    > > I have also tried moving the code in the overridden Render method to
    > > the overridden RenderContents method. This doesn't appear to have made
    > > any difference.
    > >
    > > Have I missed the point? Could I beg you for some sample code?
    > >
    > > Thanks,
    > >
    > > Stephen
    > >
    > > "Peter Blum" <> wrote in message

    > news:<>...
    > > > I think I see the problem. You are creating your control objects in the
    > > > Render method. Leave the Render method alone. Generally, only use it if

    > you
    > > > wanted to output explicit HTML (although there are some cases where you

    > can
    > > > do what you did.) Instead, create the controls in either the
    > > > CreateChildControls() or OnPreRender method.
    > > >
    > > > Here's the actual problem you are having. Validators must have their
    > > > OnPreRender method run. Because your validators aren't in this control's
    > > > Controls property, they don't get OnPreRender run. So move your code to
    > > > OnPreRender and call base.OnPreRender() after those controls are added

    > to
    > > > the Controls property.
    > > >
    > > > --- Peter Blum
    > > > www.PeterBlum.com
    > > > Email:
    > > >
    > > > "Stephen Miller" <> wrote in message
    > > > news:...
    > > > > Peter,
    > > > >
    > > > > Thanks for the link. I have enjoyed looking at your site, but its not
    > > > > going to give me the answer I need. My control needs to be able to
    > > > > render any nested literal content the developer throws at it, so I
    > > > > have to resolve this problem with MS's validators.
    > > > >
    > > > > I have designed my control in such a way that it only needs to
    > > > > validate controls in the same naming container but as you noted, the
    > > > > error message seems to indicate that the control thinks the textbox
    > > > > and validator are in different containers.
    > > > >
    > > > > What I find particularly puzzling is that if I simple override the
    > > > > render method with 'base.Render(output)' the validator has not problem
    > > > > locating the textbox in the container. The problem only seems to arise
    > > > > when I attempt to encapsulate the controls in my own formatting with
    > > > > 'table.RenderControl(output)'.
    > > > >
    > > > > Should I be looking at overriding the 'table.RenderControl' method to
    > > > > make this work? If so what should I be doing?
    > > > >
    > > > > I recently found another commercial product that is able to do what
    > > > > I'm trying to achive
    > > > > (http://www.infragistics.com/products/navigation.asp?sec=0&cat=3) so I
    > > > > know it must be possible (... and I don't want to pay USD $500)
    > > > >
    > > > > Thanks,
    > > > >
    > > > > Stephen
    > > > >
    > > > >
    > > > >
    > > > > "Peter Blum" <> wrote in message

    > news:<O#>...
    > > > > > The ControlToValidate property only accepts the ID of another

    > control in
    > the
    > > > > > same naming container. I believe that this error is due to naming

    > container
    > > > > > problems. While your code seems to enclose both the textbox and

    > validator
    > > > > > within the same naming container, the error message says otherwise.
    > > > > >
    > > > > > FYI: If the naming container is indeed the problem, I have a

    > commercial
    > > > > > product that replaces Microsoft's validators to overcome its many
    > > > > > limitations. My validators support controls in any naming container.

    > The
    > > > > > product is "Professional Validation And More". Details are at
    > > > > > http://www.peterblum.com/vam/home.aspx.
    > > > > >
    > > > > > --- Peter Blum
    > > > > > www.PeterBlum.com
    > > > > > Email:
    > > > > >
    > > > > > "Stephen Miller" <> wrote in message
    > > > > > news:...
    > > > > > > Gurus,
    > > > > > >
    > > > > > > I have a custom web control that in turn has nested child

    > controls. I
    > > > > > > want to be able to encapsulated and render any literal HTML and or
    > > > > > > server controls placed between the child control's tags. This

    > works
    > > > > > > fine, unless I add a RequiredFieldValidator control at which point

    > my
    > > > > > > aspx page fails at

    > 'WebControls.BaseValidator.CheckControlValidationProperty'
    > > > > > > with the error message 'Unable to find control id 'TextBox1'
    > > > > > > referenced by the 'ControlToValidate' property of
    > > > > > > 'RequiredFieldValidator1'.
    > > > > > >
    > > > > > > My control 'Nested' contains a collection of 'NestedChild'

    > controls
    > > > > > > with a simple 'Caption' property. I override the 'Render' method

    > on
    > > > > > > the 'Nested' control and iterate though each NestedChild' in the
    > > > > > > collection, outputting the child's literal content to a formatted
    > > > > > > table.
    > > > > > >
    > > > > > > The full code for my control is (please cut-n-paste to replicate

    > my
    > > > > > > problem):
    > > > > > >
    > > > > > > using System;
    > > > > > > using System.Web.UI;
    > > > > > > using System.Web.UI.WebControls;
    > > > > > > using System.ComponentModel;
    > > > > > > using System.Collections;
    > > > > > > using System.Collections.Specialized;
    > > > > > > using System.Web.UI.HtmlControls;
    > > > > > >
    > > > > > > namespace Custom.Web.UI {
    > > > > > >
    > > > > > > [ToolboxData("<{0}:Nested runat=server></{0}:Nested2>")]
    > > > > > > [ParseChildren(true, "NestedChild"), PersistChildren(false)]
    > > > > > > public class Nested : WebControl, INamingContainer,
    > > > > > > IPostBackDataHandler {
    > > > > > >
    > > > > > > private NestedChildCollection _NestedChildren = new
    > > > > > > NestedChildCollection();
    > > > > > >
    > > > > > > #region Properties
    > > > > > > [
    > > > > > >
    > > > > >
    > > >

    > DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
    > > > > > > PersistenceMode(PersistenceMode.InnerDefaultProperty),
    > > > > > > Description("A collection of nested children"),
    > > > > > > ]
    > > > > > > public NestedChildCollection NestedChild {
    > > > > > > get{ return _NestedChildren; }
    > > > > > > }
    > > > > > > #endregion
    > > > > > >
    > > > > > > #region Overrides
    > > > > > >
    > > > > > > protected override void CreateChildControls() {
    > > > > > > this.Controls.Clear();
    > > > > > > foreach (NestedChild child in _NestedChildren) {
    > > > > > > this.Controls.Add(child);
    > > > > > > }
    > > > > > > }
    > > > > > >
    > > > > > > protected override void Render(HtmlTextWriter output) {
    > > > > > > output.Write("<b>before</b><br /><hr><br />");
    > > > > > > //base.Render(output);
    > > > > > > foreach(NestedChild child in _NestedChildren) {
    > > > > > > output.Write("* " + child.Caption + "<BR>");
    > > > > > >
    > > > > > > // Display the literal contents of NestedChild
    > > > > > > // in a single table cell
    > > > > > > HtmlTableCell td = new HtmlTableCell();
    > > > > > > 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.CellSpacing = 2;
    > > > > > > table.CellPadding = 2;
    > > > > > > table.Border = 1;
    > > > > > > table.Style.Add("background-color", "#C0C0C0");
    > > > > > > table.Controls.Add(tr);
    > > > > > >
    > > > > > > /* FAILS NEXT LINE
    > > > > > > Error: "Unable to find control id 'txtTest1' referenced
    > > > > > > by the 'ControlToValidate' property of 'valTest1'"
    > > > > > > /*
    > > > > > > table.RenderControl(output);
    > > > > > > }
    > > > > > > output.Write("<br /><hr><br /><b>after</b>");
    > > > > > > }
    > > > > > > #endregion
    > > > > > >
    > > > > > > #region Implements IPostBackDataHandler (postback only)
    > > > > > > bool IPostBackDataHandler.LoadPostData(string strPostDataKey,
    > > > > > > NameValueCollection postDataCollection) {
    > > > > > > return true;
    > > > > > > }
    > > > > > > void IPostBackDataHandler.RaisePostDataChangedEvent() {}
    > > > > > > #endregion
    > > > > > > }
    > > > > > >
    > > > > > > [ToolboxItem(false), DefaultProperty("Caption")]
    > > > > > > public class NestedChild : Control{
    > > > > > > private String m_strCaption;
    > > > > > > public String Caption {
    > > > > > > get { return m_strCaption; }
    > > > > > > set { m_strCaption = value; }
    > > > > > > }
    > > > > > > }
    > > > > > >
    > > > > > > 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);
    > > > > > > }
    > > > > > > }
    > > > > > > }
    > > > > > >
    > > > > > > When deployed the aspx page looks like:
    > > > > > >
    > > > > > > <form id="Form1" method="post" runat="server">
    > > > > > > <P>Nested Control</P>
    > > > > > > <P>
    > > > > > > <cc1:Nested id="Nested1" runat="server">
    > > > > > > <cc1:NestedChild Caption="NestedChild">
    > > > > > > <asp:Button id="cmdTest" runat="server" Text="Generate
    > > > > > > Event"></asp:Button>
    > > > > > > <asp:TextBox id="txtTest" runat="server"></asp:TextBox>
    > > > > > > <asp:RequiredFieldValidator id="valTest" runat="server"
    > > > > > > ControlToValidate="txtTest"
    > > > > > > ErrorMessage="This field is
    > > > > > > Required.">*</asp:RequiredFieldValidator>
    > > > > > > </cc1:NestedChild>
    > > > > > > </cc1:Nested2></P>
    > > > > > > <P>&nbsp;</P>
    > > > > > > <P>
    > > > > > > <asp:label id="lblResult"

    > runat="server">lblResult</asp:label></P>
    > > > > > > </form>
    > > > > > >
    > > > > > > If I remove 'RequiredFieldValidator' my control render as

    > expected.
    > > > > > >
    > > > > > > I have noticed, that If I replace the entire formatting logic in

    > the
    > > > > > > 'Render' method with a call to 'base.Render(output);', the
    > > > > > > RequiredFieldValidator works as expected.
    > > > > > >
    > > > > > > I think this is quite a technical problem and I assume that
    > > > > > > 'table.RenderControl(output)' is not preforming the necessary
    > > > > > > server-side pre-validation to pass a 'RequiredFieldValidator'. How

    > can
    > > > > > > I do this?
    > > > > > >
    > > > > > > Regards,
    > > > > > >
    > > > > > > Stephen
     
    Stephen Miller, Jan 11, 2004
    #4
  5. have your control implement INamingContainer. This will generate Unique Ids
    "Stephen Miller" <> wrote in message
    news:...
    > 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
    >
    >
    > "Peter Blum" <> wrote in message

    news:<>...
    > > I strongly encourage you to step back and learn the right way to build
    > > custom controls. Otherwise, you will be developing code that may require
    > > plenty more debugging and maintenance because it doesn't confirm to how
    > > Microsoft designed web controls. I recommend the book "Developing

    ASP.NET
    > > Server Controls and Components" from Microsoft Press.
    > >
    > > In your code, I do not understand why you have the _NestedChildren

    property.
    > > The webcontrol framework automatically supports child controls using the
    > > ParseChildrenAttribute. (See the .net docs on this attribute.) I do
    > > understand what you are doing in CreateChildControls and that looks OK.
    > >
    > > Here are my recommendations.
    > > - Move the code you have creating the table and its contents into
    > > CreateChildControls without removing the existing code.
    > > - Any "output.Write()" calls can become LiteralControl objects. (new
    > > LiteralControl("<br>"))
    > > - Add objects to the Controls collection on your control.
    > >
    > > Now your validators will be created before OnPreRender occurs. That

    allows
    > > the webcontrol system that Microsoft designed to call each of your
    > > validators own OnPreRender method properly, setting up many things that

    are
    > > required for validation.
    > >
    > > --- Peter Blum
    > > www.PeterBlum.com
    > > Email:
    > > "Stephen Miller" <> wrote in message
    > > news:...
    > > > Peter,
    > > >
    > > > I was under the mistaken impression that I was already overriding
    > > > CreateChildControls to create the controls with the code:
    > > >
    > > > protected override void CreateChildControls() {
    > > > this.Controls.Clear();
    > > > foreach (NestedChild child in _NestedChildren) {
    > > > this.Controls.Add(child);
    > > > }
    > > > }
    > > >
    > > > If I leave this override out then the control renders fine (with the
    > > > RequiredFieldValidators) but fails to generate any server side events.
    > > > Moving this code block to OnPreRender (with base.OnPreRend) after
    > > > adding the controls doesn't seem to make any difference.
    > > >
    > > > I have also tried moving the code in the overridden Render method to
    > > > the overridden RenderContents method. This doesn't appear to have made
    > > > any difference.
    > > >
    > > > Have I missed the point? Could I beg you for some sample code?
    > > >
    > > > Thanks,
    > > >
    > > > Stephen
    > > >
    > > > "Peter Blum" <> wrote in message

    > > news:<>...
    > > > > I think I see the problem. You are creating your control objects in

    the
    > > > > Render method. Leave the Render method alone. Generally, only use it

    if
    > > you
    > > > > wanted to output explicit HTML (although there are some cases where

    you
    > > can
    > > > > do what you did.) Instead, create the controls in either the
    > > > > CreateChildControls() or OnPreRender method.
    > > > >
    > > > > Here's the actual problem you are having. Validators must have their
    > > > > OnPreRender method run. Because your validators aren't in this

    control's
    > > > > Controls property, they don't get OnPreRender run. So move your code

    to
    > > > > OnPreRender and call base.OnPreRender() after those controls are

    added
    > > to
    > > > > the Controls property.
    > > > >
    > > > > --- Peter Blum
    > > > > www.PeterBlum.com
    > > > > Email:
    > > > >
    > > > > "Stephen Miller" <> wrote in message
    > > > > news:...
    > > > > > Peter,
    > > > > >
    > > > > > Thanks for the link. I have enjoyed looking at your site, but its

    not
    > > > > > going to give me the answer I need. My control needs to be able to
    > > > > > render any nested literal content the developer throws at it, so I
    > > > > > have to resolve this problem with MS's validators.
    > > > > >
    > > > > > I have designed my control in such a way that it only needs to
    > > > > > validate controls in the same naming container but as you noted,

    the
    > > > > > error message seems to indicate that the control thinks the

    textbox
    > > > > > and validator are in different containers.
    > > > > >
    > > > > > What I find particularly puzzling is that if I simple override the
    > > > > > render method with 'base.Render(output)' the validator has not

    problem
    > > > > > locating the textbox in the container. The problem only seems to

    arise
    > > > > > when I attempt to encapsulate the controls in my own formatting

    with
    > > > > > 'table.RenderControl(output)'.
    > > > > >
    > > > > > Should I be looking at overriding the 'table.RenderControl' method

    to
    > > > > > make this work? If so what should I be doing?
    > > > > >
    > > > > > I recently found another commercial product that is able to do

    what
    > > > > > I'm trying to achive
    > > > > > (http://www.infragistics.com/products/navigation.asp?sec=0&cat=3)

    so I
    > > > > > know it must be possible (... and I don't want to pay USD $500)
    > > > > >
    > > > > > Thanks,
    > > > > >
    > > > > > Stephen
    > > > > >
    > > > > >
    > > > > >
    > > > > > "Peter Blum" <> wrote in message

    > > news:<O#>...
    > > > > > > The ControlToValidate property only accepts the ID of another

    > > control in
    > > the
    > > > > > > same naming container. I believe that this error is due to

    naming
    > > container
    > > > > > > problems. While your code seems to enclose both the textbox and

    > > validator
    > > > > > > within the same naming container, the error message says

    otherwise.
    > > > > > >
    > > > > > > FYI: If the naming container is indeed the problem, I have a

    > > commercial
    > > > > > > product that replaces Microsoft's validators to overcome its

    many
    > > > > > > limitations. My validators support controls in any naming

    container.
    > > The
    > > > > > > product is "Professional Validation And More". Details are at
    > > > > > > http://www.peterblum.com/vam/home.aspx.
    > > > > > >
    > > > > > > --- Peter Blum
    > > > > > > www.PeterBlum.com
    > > > > > > Email:
    > > > > > >
    > > > > > > "Stephen Miller" <> wrote in message
    > > > > > > news:...
    > > > > > > > Gurus,
    > > > > > > >
    > > > > > > > I have a custom web control that in turn has nested child

    > > controls. I
    > > > > > > > want to be able to encapsulated and render any literal HTML

    and or
    > > > > > > > server controls placed between the child control's tags. This

    > > works
    > > > > > > > fine, unless I add a RequiredFieldValidator control at which

    point
    > > my
    > > > > > > > aspx page fails at

    > > 'WebControls.BaseValidator.CheckControlValidationProperty'
    > > > > > > > with the error message 'Unable to find control id 'TextBox1'
    > > > > > > > referenced by the 'ControlToValidate' property of
    > > > > > > > 'RequiredFieldValidator1'.
    > > > > > > >
    > > > > > > > My control 'Nested' contains a collection of 'NestedChild'

    > > controls
    > > > > > > > with a simple 'Caption' property. I override the 'Render'

    method
    > > on
    > > > > > > > the 'Nested' control and iterate though each NestedChild' in

    the
    > > > > > > > collection, outputting the child's literal content to a

    formatted
    > > > > > > > table.
    > > > > > > >
    > > > > > > > The full code for my control is (please cut-n-paste to

    replicate
    > > my
    > > > > > > > problem):
    > > > > > > >
    > > > > > > > using System;
    > > > > > > > using System.Web.UI;
    > > > > > > > using System.Web.UI.WebControls;
    > > > > > > > using System.ComponentModel;
    > > > > > > > using System.Collections;
    > > > > > > > using System.Collections.Specialized;
    > > > > > > > using System.Web.UI.HtmlControls;
    > > > > > > >
    > > > > > > > namespace Custom.Web.UI {
    > > > > > > >
    > > > > > > > [ToolboxData("<{0}:Nested runat=server></{0}:Nested2>")]
    > > > > > > > [ParseChildren(true, "NestedChild"), PersistChildren(false)]
    > > > > > > > public class Nested : WebControl, INamingContainer,
    > > > > > > > IPostBackDataHandler {
    > > > > > > >
    > > > > > > > private NestedChildCollection _NestedChildren = new
    > > > > > > > NestedChildCollection();
    > > > > > > >
    > > > > > > > #region Properties
    > > > > > > > [
    > > > > > > >
    > > > > > >
    > > > >

    > >

    DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
    > > > > > > > PersistenceMode(PersistenceMode.InnerDefaultProperty),
    > > > > > > > Description("A collection of nested children"),
    > > > > > > > ]
    > > > > > > > public NestedChildCollection NestedChild {
    > > > > > > > get{ return _NestedChildren; }
    > > > > > > > }
    > > > > > > > #endregion
    > > > > > > >
    > > > > > > > #region Overrides
    > > > > > > >
    > > > > > > > protected override void CreateChildControls() {
    > > > > > > > this.Controls.Clear();
    > > > > > > > foreach (NestedChild child in _NestedChildren) {
    > > > > > > > this.Controls.Add(child);
    > > > > > > > }
    > > > > > > > }
    > > > > > > >
    > > > > > > > protected override void Render(HtmlTextWriter output) {
    > > > > > > > output.Write("<b>before</b><br /><hr><br />");
    > > > > > > > //base.Render(output);
    > > > > > > > foreach(NestedChild child in _NestedChildren) {
    > > > > > > > output.Write("* " + child.Caption + "<BR>");
    > > > > > > >
    > > > > > > > // Display the literal contents of NestedChild
    > > > > > > > // in a single table cell
    > > > > > > > HtmlTableCell td = new HtmlTableCell();
    > > > > > > > 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.CellSpacing = 2;
    > > > > > > > table.CellPadding = 2;
    > > > > > > > table.Border = 1;
    > > > > > > > table.Style.Add("background-color", "#C0C0C0");
    > > > > > > > table.Controls.Add(tr);
    > > > > > > >
    > > > > > > > /* FAILS NEXT LINE
    > > > > > > > Error: "Unable to find control id 'txtTest1'

    referenced
    > > > > > > > by the 'ControlToValidate' property of 'valTest1'"
    > > > > > > > /*
    > > > > > > > table.RenderControl(output);
    > > > > > > > }
    > > > > > > > output.Write("<br /><hr><br /><b>after</b>");
    > > > > > > > }
    > > > > > > > #endregion
    > > > > > > >
    > > > > > > > #region Implements IPostBackDataHandler (postback only)
    > > > > > > > bool IPostBackDataHandler.LoadPostData(string

    strPostDataKey,
    > > > > > > > NameValueCollection postDataCollection) {
    > > > > > > > return true;
    > > > > > > > }
    > > > > > > > void IPostBackDataHandler.RaisePostDataChangedEvent() {}
    > > > > > > > #endregion
    > > > > > > > }
    > > > > > > >
    > > > > > > > [ToolboxItem(false), DefaultProperty("Caption")]
    > > > > > > > public class NestedChild : Control{
    > > > > > > > private String m_strCaption;
    > > > > > > > public String Caption {
    > > > > > > > get { return m_strCaption; }
    > > > > > > > set { m_strCaption = value; }
    > > > > > > > }
    > > > > > > > }
    > > > > > > >
    > > > > > > > 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);
    > > > > > > > }
    > > > > > > > }
    > > > > > > > }
    > > > > > > >
    > > > > > > > When deployed the aspx page looks like:
    > > > > > > >
    > > > > > > > <form id="Form1" method="post" runat="server">
    > > > > > > > <P>Nested Control</P>
    > > > > > > > <P>
    > > > > > > > <cc1:Nested id="Nested1" runat="server">
    > > > > > > > <cc1:NestedChild Caption="NestedChild">
    > > > > > > > <asp:Button id="cmdTest" runat="server" Text="Generate
    > > > > > > > Event"></asp:Button>
    > > > > > > > <asp:TextBox id="txtTest" runat="server"></asp:TextBox>
    > > > > > > > <asp:RequiredFieldValidator id="valTest" runat="server"
    > > > > > > > ControlToValidate="txtTest"
    > > > > > > > ErrorMessage="This field is
    > > > > > > > Required.">*</asp:RequiredFieldValidator>
    > > > > > > > </cc1:NestedChild>
    > > > > > > > </cc1:Nested2></P>
    > > > > > > > <P>&nbsp;</P>
    > > > > > > > <P>
    > > > > > > > <asp:label id="lblResult"

    > > runat="server">lblResult</asp:label></P>
    > > > > > > > </form>
    > > > > > > >
    > > > > > > > If I remove 'RequiredFieldValidator' my control render as

    > > expected.
    > > > > > > >
    > > > > > > > I have noticed, that If I replace the entire formatting logic

    in
    > > the
    > > > > > > > 'Render' method with a call to 'base.Render(output);', the
    > > > > > > > RequiredFieldValidator works as expected.
    > > > > > > >
    > > > > > > > I think this is quite a technical problem and I assume that
    > > > > > > > 'table.RenderControl(output)' is not preforming the necessary
    > > > > > > > server-side pre-validation to pass a 'RequiredFieldValidator'.

    How
    > > can
    > > > > > > > I do this?
    > > > > > > >
    > > > > > > > Regards,
    > > > > > > >
    > > > > > > > Stephen
     
    Alessandro Zifiglio, Jan 15, 2004
    #5
  6. in case it is not clear, i'm referring to the template you are using, which
    inherits control. When you develop templated controls, you should implement
    this interface InameingContainer to avoid naming conflicts on a page. . Your
    main class nested is un-necessarily implementing this interface as you
    already have clientID which is is unique, and you can name your table :
    clientID + "_table";

    "Alessandro Zifiglio" <> wrote in
    message news:x%rNb.3283$...
    > have your control implement INamingContainer. This will generate Unique

    Ids
    > "Stephen Miller" <> wrote in message
    > news:...
    > > 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
    > >
    > >
    > > "Peter Blum" <> wrote in message

    > news:<>...
    > > > I strongly encourage you to step back and learn the right way to build
    > > > custom controls. Otherwise, you will be developing code that may

    require
    > > > plenty more debugging and maintenance because it doesn't confirm to

    how
    > > > Microsoft designed web controls. I recommend the book "Developing

    > ASP.NET
    > > > Server Controls and Components" from Microsoft Press.
    > > >
    > > > In your code, I do not understand why you have the _NestedChildren

    > property.
    > > > The webcontrol framework automatically supports child controls using

    the
    > > > ParseChildrenAttribute. (See the .net docs on this attribute.) I do
    > > > understand what you are doing in CreateChildControls and that looks

    OK.
    > > >
    > > > Here are my recommendations.
    > > > - Move the code you have creating the table and its contents into
    > > > CreateChildControls without removing the existing code.
    > > > - Any "output.Write()" calls can become LiteralControl objects. (new
    > > > LiteralControl("<br>"))
    > > > - Add objects to the Controls collection on your control.
    > > >
    > > > Now your validators will be created before OnPreRender occurs. That

    > allows
    > > > the webcontrol system that Microsoft designed to call each of your
    > > > validators own OnPreRender method properly, setting up many things

    that
    > are
    > > > required for validation.
    > > >
    > > > --- Peter Blum
    > > > www.PeterBlum.com
    > > > Email:
    > > > "Stephen Miller" <> wrote in message
    > > > news:...
    > > > > Peter,
    > > > >
    > > > > I was under the mistaken impression that I was already overriding
    > > > > CreateChildControls to create the controls with the code:
    > > > >
    > > > > protected override void CreateChildControls() {
    > > > > this.Controls.Clear();
    > > > > foreach (NestedChild child in _NestedChildren) {
    > > > > this.Controls.Add(child);
    > > > > }
    > > > > }
    > > > >
    > > > > If I leave this override out then the control renders fine (with the
    > > > > RequiredFieldValidators) but fails to generate any server side

    events.
    > > > > Moving this code block to OnPreRender (with base.OnPreRend) after
    > > > > adding the controls doesn't seem to make any difference.
    > > > >
    > > > > I have also tried moving the code in the overridden Render method to
    > > > > the overridden RenderContents method. This doesn't appear to have

    made
    > > > > any difference.
    > > > >
    > > > > Have I missed the point? Could I beg you for some sample code?
    > > > >
    > > > > Thanks,
    > > > >
    > > > > Stephen
    > > > >
    > > > > "Peter Blum" <> wrote in message
    > > > news:<>...
    > > > > > I think I see the problem. You are creating your control objects

    in
    > the
    > > > > > Render method. Leave the Render method alone. Generally, only use

    it
    > if
    > > > you
    > > > > > wanted to output explicit HTML (although there are some cases

    where
    > you
    > > > can
    > > > > > do what you did.) Instead, create the controls in either the
    > > > > > CreateChildControls() or OnPreRender method.
    > > > > >
    > > > > > Here's the actual problem you are having. Validators must have

    their
    > > > > > OnPreRender method run. Because your validators aren't in this

    > control's
    > > > > > Controls property, they don't get OnPreRender run. So move your

    code
    > to
    > > > > > OnPreRender and call base.OnPreRender() after those controls are

    > added
    > > > to
    > > > > > the Controls property.
    > > > > >
    > > > > > --- Peter Blum
    > > > > > www.PeterBlum.com
    > > > > > Email:
    > > > > >
    > > > > > "Stephen Miller" <> wrote in message
    > > > > > news:...
    > > > > > > Peter,
    > > > > > >
    > > > > > > Thanks for the link. I have enjoyed looking at your site, but

    its
    > not
    > > > > > > going to give me the answer I need. My control needs to be able

    to
    > > > > > > render any nested literal content the developer throws at it, so

    I
    > > > > > > have to resolve this problem with MS's validators.
    > > > > > >
    > > > > > > I have designed my control in such a way that it only needs to
    > > > > > > validate controls in the same naming container but as you noted,

    > the
    > > > > > > error message seems to indicate that the control thinks the

    > textbox
    > > > > > > and validator are in different containers.
    > > > > > >
    > > > > > > What I find particularly puzzling is that if I simple override

    the
    > > > > > > render method with 'base.Render(output)' the validator has not

    > problem
    > > > > > > locating the textbox in the container. The problem only seems to

    > arise
    > > > > > > when I attempt to encapsulate the controls in my own formatting

    > with
    > > > > > > 'table.RenderControl(output)'.
    > > > > > >
    > > > > > > Should I be looking at overriding the 'table.RenderControl'

    method
    > to
    > > > > > > make this work? If so what should I be doing?
    > > > > > >
    > > > > > > I recently found another commercial product that is able to do

    > what
    > > > > > > I'm trying to achive
    > > > > > >

    (http://www.infragistics.com/products/navigation.asp?sec=0&cat=3)
    > so I
    > > > > > > know it must be possible (... and I don't want to pay USD $500)
    > > > > > >
    > > > > > > Thanks,
    > > > > > >
    > > > > > > Stephen
    > > > > > >
    > > > > > >
    > > > > > >
    > > > > > > "Peter Blum" <> wrote in message
    > > > news:<O#>...
    > > > > > > > The ControlToValidate property only accepts the ID of another
    > > > control in
    > > > the
    > > > > > > > same naming container. I believe that this error is due to

    > naming
    > > > container
    > > > > > > > problems. While your code seems to enclose both the textbox

    and
    > > > validator
    > > > > > > > within the same naming container, the error message says

    > otherwise.
    > > > > > > >
    > > > > > > > FYI: If the naming container is indeed the problem, I have a
    > > > commercial
    > > > > > > > product that replaces Microsoft's validators to overcome its

    > many
    > > > > > > > limitations. My validators support controls in any naming

    > container.
    > > > The
    > > > > > > > product is "Professional Validation And More". Details are at
    > > > > > > > http://www.peterblum.com/vam/home.aspx.
    > > > > > > >
    > > > > > > > --- Peter Blum
    > > > > > > > www.PeterBlum.com
    > > > > > > > Email:
    > > > > > > >
    > > > > > > > "Stephen Miller" <> wrote in message
    > > > > > > > news:...
    > > > > > > > > Gurus,
    > > > > > > > >
    > > > > > > > > I have a custom web control that in turn has nested child
    > > > controls. I
    > > > > > > > > want to be able to encapsulated and render any literal HTML

    > and or
    > > > > > > > > server controls placed between the child control's tags.

    This
    > > > works
    > > > > > > > > fine, unless I add a RequiredFieldValidator control at which

    > point
    > > > my
    > > > > > > > > aspx page fails at
    > > > 'WebControls.BaseValidator.CheckControlValidationProperty'
    > > > > > > > > with the error message 'Unable to find control id 'TextBox1'
    > > > > > > > > referenced by the 'ControlToValidate' property of
    > > > > > > > > 'RequiredFieldValidator1'.
    > > > > > > > >
    > > > > > > > > My control 'Nested' contains a collection of 'NestedChild'
    > > > controls
    > > > > > > > > with a simple 'Caption' property. I override the 'Render'

    > method
    > > > on
    > > > > > > > > the 'Nested' control and iterate though each NestedChild' in

    > the
    > > > > > > > > collection, outputting the child's literal content to a

    > formatted
    > > > > > > > > table.
    > > > > > > > >
    > > > > > > > > The full code for my control is (please cut-n-paste to

    > replicate
    > > > my
    > > > > > > > > problem):
    > > > > > > > >
    > > > > > > > > using System;
    > > > > > > > > using System.Web.UI;
    > > > > > > > > using System.Web.UI.WebControls;
    > > > > > > > > using System.ComponentModel;
    > > > > > > > > using System.Collections;
    > > > > > > > > using System.Collections.Specialized;
    > > > > > > > > using System.Web.UI.HtmlControls;
    > > > > > > > >
    > > > > > > > > namespace Custom.Web.UI {
    > > > > > > > >
    > > > > > > > > [ToolboxData("<{0}:Nested runat=server></{0}:Nested2>")]
    > > > > > > > > [ParseChildren(true, "NestedChild"),

    PersistChildren(false)]
    > > > > > > > > public class Nested : WebControl, INamingContainer,
    > > > > > > > > IPostBackDataHandler {
    > > > > > > > >
    > > > > > > > > private NestedChildCollection _NestedChildren = new
    > > > > > > > > NestedChildCollection();
    > > > > > > > >
    > > > > > > > > #region Properties
    > > > > > > > > [
    > > > > > > > >
    > > > > > > >
    > > > > >
    > > >

    > DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
    > > > > > > > > PersistenceMode(PersistenceMode.InnerDefaultProperty),
    > > > > > > > > Description("A collection of nested children"),
    > > > > > > > > ]
    > > > > > > > > public NestedChildCollection NestedChild {
    > > > > > > > > get{ return _NestedChildren; }
    > > > > > > > > }
    > > > > > > > > #endregion
    > > > > > > > >
    > > > > > > > > #region Overrides
    > > > > > > > >
    > > > > > > > > protected override void CreateChildControls() {
    > > > > > > > > this.Controls.Clear();
    > > > > > > > > foreach (NestedChild child in _NestedChildren) {
    > > > > > > > > this.Controls.Add(child);
    > > > > > > > > }
    > > > > > > > > }
    > > > > > > > >
    > > > > > > > > protected override void Render(HtmlTextWriter output) {
    > > > > > > > > output.Write("<b>before</b><br /><hr><br />");
    > > > > > > > > //base.Render(output);
    > > > > > > > > foreach(NestedChild child in _NestedChildren) {
    > > > > > > > > output.Write("* " + child.Caption + "<BR>");
    > > > > > > > >
    > > > > > > > > // Display the literal contents of NestedChild
    > > > > > > > > // in a single table cell
    > > > > > > > > HtmlTableCell td = new HtmlTableCell();
    > > > > > > > > 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.CellSpacing = 2;
    > > > > > > > > table.CellPadding = 2;
    > > > > > > > > table.Border = 1;
    > > > > > > > > table.Style.Add("background-color", "#C0C0C0");
    > > > > > > > > table.Controls.Add(tr);
    > > > > > > > >
    > > > > > > > > /* FAILS NEXT LINE
    > > > > > > > > Error: "Unable to find control id 'txtTest1'

    > referenced
    > > > > > > > > by the 'ControlToValidate' property of 'valTest1'"
    > > > > > > > > /*
    > > > > > > > > table.RenderControl(output);
    > > > > > > > > }
    > > > > > > > > output.Write("<br /><hr><br /><b>after</b>");
    > > > > > > > > }
    > > > > > > > > #endregion
    > > > > > > > >
    > > > > > > > > #region Implements IPostBackDataHandler (postback only)
    > > > > > > > > bool IPostBackDataHandler.LoadPostData(string

    > strPostDataKey,
    > > > > > > > > NameValueCollection postDataCollection) {
    > > > > > > > > return true;
    > > > > > > > > }
    > > > > > > > > void IPostBackDataHandler.RaisePostDataChangedEvent() {}
    > > > > > > > > #endregion
    > > > > > > > > }
    > > > > > > > >
    > > > > > > > > [ToolboxItem(false), DefaultProperty("Caption")]
    > > > > > > > > public class NestedChild : Control{
    > > > > > > > > private String m_strCaption;
    > > > > > > > > public String Caption {
    > > > > > > > > get { return m_strCaption; }
    > > > > > > > > set { m_strCaption = value; }
    > > > > > > > > }
    > > > > > > > > }
    > > > > > > > >
    > > > > > > > > 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);
    > > > > > > > > }
    > > > > > > > > }
    > > > > > > > > }
    > > > > > > > >
    > > > > > > > > When deployed the aspx page looks like:
    > > > > > > > >
    > > > > > > > > <form id="Form1" method="post" runat="server">
    > > > > > > > > <P>Nested Control</P>
    > > > > > > > > <P>
    > > > > > > > > <cc1:Nested id="Nested1" runat="server">
    > > > > > > > > <cc1:NestedChild Caption="NestedChild">
    > > > > > > > > <asp:Button id="cmdTest" runat="server" Text="Generate
    > > > > > > > > Event"></asp:Button>
    > > > > > > > > <asp:TextBox id="txtTest" runat="server"></asp:TextBox>
    > > > > > > > > <asp:RequiredFieldValidator id="valTest" runat="server"
    > > > > > > > > ControlToValidate="txtTest"
    > > > > > > > > ErrorMessage="This field is
    > > > > > > > > Required.">*</asp:RequiredFieldValidator>
    > > > > > > > > </cc1:NestedChild>
    > > > > > > > > </cc1:Nested2></P>
    > > > > > > > > <P>&nbsp;</P>
    > > > > > > > > <P>
    > > > > > > > > <asp:label id="lblResult"
    > > > runat="server">lblResult</asp:label></P>
    > > > > > > > > </form>
    > > > > > > > >
    > > > > > > > > If I remove 'RequiredFieldValidator' my control render as
    > > > expected.
    > > > > > > > >
    > > > > > > > > I have noticed, that If I replace the entire formatting

    logic
    > in
    > > > the
    > > > > > > > > 'Render' method with a call to 'base.Render(output);', the
    > > > > > > > > RequiredFieldValidator works as expected.
    > > > > > > > >
    > > > > > > > > I think this is quite a technical problem and I assume that
    > > > > > > > > 'table.RenderControl(output)' is not preforming the

    necessary
    > > > > > > > > server-side pre-validation to pass a

    'RequiredFieldValidator'.
    > How
    > > > can
    > > > > > > > > I do this?
    > > > > > > > >
    > > > > > > > > Regards,
    > > > > > > > >
    > > > > > > > > Stephen

    >
    >
     
    Alessandro Zifiglio, Jan 15, 2004
    #6
  7. Alessandro,

    That's the missing piece of the puzzle!

    I can see that I am now uncessarily implementing InamingContainer
    twice, however I found that if I gave the table a unique name (ie.
    table.ID = "_" + this.ID; ) and removed InamingContainer from the main
    class ('Nested'), then the control correctly renders any nested server
    controls placed within the 'NestedChild' tags (including
    RequiredFieldValidator's), but the command button was unable to
    generate a server-side onClick event.

    Having two implementations of InamingContainer does produce
    complicated naming of child controls, when rendered (ie cmdTest
    becomes Nested1:_ctl2:cmdTest). Is there a better way? Should I now be
    moving my implementation of IPostBackDataHandler to the 'NestedChild'
    class'?

    Regards,

    Stephen


    "Alessandro Zifiglio" <> wrote in message news:<PdtNb.3378$>...
    > in case it is not clear, i'm referring to the template you are using, which
    > inherits control. When you develop templated controls, you should implement
    > this interface InameingContainer to avoid naming conflicts on a page. . Your
    > main class nested is un-necessarily implementing this interface as you
    > already have clientID which is is unique, and you can name your table :
    > clientID + "_table";
    >
    > "Alessandro Zifiglio" <> wrote in
    > message news:x%rNb.3283$...
    > > have your control implement INamingContainer. This will generate Unique

    > Ids
    > > "Stephen Miller" <> wrote in message
    > > news:...
    > > > 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
    > > >
    > > >
    > > > "Peter Blum" <> wrote in message

    > news:<>...
    > > > > I strongly encourage you to step back and learn the right way to build
    > > > > custom controls. Otherwise, you will be developing code that may

    > require
    > > > > plenty more debugging and maintenance because it doesn't confirm to

    > how
    > > > > Microsoft designed web controls. I recommend the book "Developing

    > ASP.NET
    > > > > Server Controls and Components" from Microsoft Press.
    > > > >
    > > > > In your code, I do not understand why you have the _NestedChildren

    > property.
    > > > > The webcontrol framework automatically supports child controls using

    > the
    > > > > ParseChildrenAttribute. (See the .net docs on this attribute.) I do
    > > > > understand what you are doing in CreateChildControls and that looks

    > OK.
    > > > >
    > > > > Here are my recommendations.
    > > > > - Move the code you have creating the table and its contents into
    > > > > CreateChildControls without removing the existing code.
    > > > > - Any "output.Write()" calls can become LiteralControl objects. (new
    > > > > LiteralControl("<br>"))
    > > > > - Add objects to the Controls collection on your control.
    > > > >
    > > > > Now your validators will be created before OnPreRender occurs. That

    > allows
    > > > > the webcontrol system that Microsoft designed to call each of your
    > > > > validators own OnPreRender method properly, setting up many things

    > that
    > are
    > > > > required for validation.
    > > > >
    > > > > --- Peter Blum
    > > > > www.PeterBlum.com
    > > > > Email:
    > > > > "Stephen Miller" <> wrote in message
    > > > > news:...
    > > > > > Peter,
    > > > > >
    > > > > > I was under the mistaken impression that I was already overriding
    > > > > > CreateChildControls to create the controls with the code:
    > > > > >
    > > > > > protected override void CreateChildControls() {
    > > > > > this.Controls.Clear();
    > > > > > foreach (NestedChild child in _NestedChildren) {
    > > > > > this.Controls.Add(child);
    > > > > > }
    > > > > > }
    > > > > >
    > > > > > If I leave this override out then the control renders fine (with the
    > > > > > RequiredFieldValidators) but fails to generate any server side

    > events.
    > > > > > Moving this code block to OnPreRender (with base.OnPreRend) after
    > > > > > adding the controls doesn't seem to make any difference.
    > > > > >
    > > > > > I have also tried moving the code in the overridden Render method to
    > > > > > the overridden RenderContents method. This doesn't appear to have

    > made
    > > > > > any difference.
    > > > > >
    > > > > > Have I missed the point? Could I beg you for some sample code?
    > > > > >
    > > > > > Thanks,
    > > > > >
    > > > > > Stephen
    > > > > >
    > > > > > "Peter Blum" <> wrote in message

    > news:<>...
    > > > > > > I think I see the problem. You are creating your control objects

    > in
    > the
    > > > > > > Render method. Leave the Render method alone. Generally, only use

    > it
    > if
    > you
    > > > > > > wanted to output explicit HTML (although there are some cases

    > where
    > you
    > can
    > > > > > > do what you did.) Instead, create the controls in either the
    > > > > > > CreateChildControls() or OnPreRender method.
    > > > > > >
    > > > > > > Here's the actual problem you are having. Validators must have

    > their
    > > > > > > OnPreRender method run. Because your validators aren't in this

    > control's
    > > > > > > Controls property, they don't get OnPreRender run. So move your

    > code
    > to
    > > > > > > OnPreRender and call base.OnPreRender() after those controls are

    > added
    > to
    > > > > > > the Controls property.
    > > > > > >
    > > > > > > --- Peter Blum
    > > > > > > www.PeterBlum.com
    > > > > > > Email:
    > > > > > >
    > > > > > > "Stephen Miller" <> wrote in message
    > > > > > > news:...
    > > > > > > > Peter,
    > > > > > > >
    > > > > > > > Thanks for the link. I have enjoyed looking at your site, but

    > its
    > not
    > > > > > > > going to give me the answer I need. My control needs to be able

    > to
    > > > > > > > render any nested literal content the developer throws at it, so

    > I
    > > > > > > > have to resolve this problem with MS's validators.
    > > > > > > >
    > > > > > > > I have designed my control in such a way that it only needs to
    > > > > > > > validate controls in the same naming container but as you noted,

    > the
    > > > > > > > error message seems to indicate that the control thinks the

    > textbox
    > > > > > > > and validator are in different containers.
    > > > > > > >
    > > > > > > > What I find particularly puzzling is that if I simple override

    > the
    > > > > > > > render method with 'base.Render(output)' the validator has not

    > problem
    > > > > > > > locating the textbox in the container. The problem only seems to

    > arise
    > > > > > > > when I attempt to encapsulate the controls in my own formatting

    > with
    > > > > > > > 'table.RenderControl(output)'.
    > > > > > > >
    > > > > > > > Should I be looking at overriding the 'table.RenderControl'

    > method
    > to
    > > > > > > > make this work? If so what should I be doing?
    > > > > > > >
    > > > > > > > I recently found another commercial product that is able to do

    > what
    > > > > > > > I'm trying to achive
    > > > > > > >

    > (http://www.infragistics.com/products/navigation.asp?sec=0&cat=3)
    > so I
    > > > > > > > know it must be possible (... and I don't want to pay USD $500)
    > > > > > > >
    > > > > > > > Thanks,
    > > > > > > >
    > > > > > > > Stephen
    > > > > > > >
    > > > > > > >
    > > > > > > >
    > > > > > > > "Peter Blum" <> wrote in message

    > news:<O#>...
    > > > > > > > > The ControlToValidate property only accepts the ID of another
    > > > > control in
    > > > > the
    > > > > > > > > same naming container. I believe that this error is due to

    > naming
    > container
    > > > > > > > > problems. While your code seems to enclose both the textbox

    > and
    > validator
    > > > > > > > > within the same naming container, the error message says

    > otherwise.
    > > > > > > > >
    > > > > > > > > FYI: If the naming container is indeed the problem, I have a

    > commercial
    > > > > > > > > product that replaces Microsoft's validators to overcome its

    > many
    > > > > > > > > limitations. My validators support controls in any naming

    > container.
    > The
    > > > > > > > > product is "Professional Validation And More". Details are at
    > > > > > > > > http://www.peterblum.com/vam/home.aspx.
    > > > > > > > >
    > > > > > > > > --- Peter Blum
    > > > > > > > > www.PeterBlum.com
    > > > > > > > > Email:
    > > > > > > > >
    > > > > > > > > "Stephen Miller" <> wrote in message
    > > > > > > > > news:...
    > > > > > > > > > Gurus,
    > > > > > > > > >
    > > > > > > > > > I have a custom web control that in turn has nested child

    > controls. I
    > > > > > > > > > want to be able to encapsulated and render any literal HTML

    > and or
    > > > > > > > > > server controls placed between the child control's tags.

    > This
    > works
    > > > > > > > > > fine, unless I add a RequiredFieldValidator control at which

    > point
    > my
    > > > > > > > > > aspx page fails at

    > 'WebControls.BaseValidator.CheckControlValidationProperty'
    > > > > > > > > > with the error message 'Unable to find control id 'TextBox1'
    > > > > > > > > > referenced by the 'ControlToValidate' property of
    > > > > > > > > > 'RequiredFieldValidator1'.
    > > > > > > > > >
    > > > > > > > > > My control 'Nested' contains a collection of 'NestedChild'

    > controls
    > > > > > > > > > with a simple 'Caption' property. I override the 'Render'

    > method
    > on
    > > > > > > > > > the 'Nested' control and iterate though each NestedChild' in

    > the
    > > > > > > > > > collection, outputting the child's literal content to a

    > formatted
    > > > > > > > > > table.
    > > > > > > > > >
    > > > > > > > > > The full code for my control is (please cut-n-paste to

    > replicate
    > my
    > > > > > > > > > problem):
    > > > > > > > > >
    > > > > > > > > > using System;
    > > > > > > > > > using System.Web.UI;
    > > > > > > > > > using System.Web.UI.WebControls;
    > > > > > > > > > using System.ComponentModel;
    > > > > > > > > > using System.Collections;
    > > > > > > > > > using System.Collections.Specialized;
    > > > > > > > > > using System.Web.UI.HtmlControls;
    > > > > > > > > >
    > > > > > > > > > namespace Custom.Web.UI {
    > > > > > > > > >
    > > > > > > > > > [ToolboxData("<{0}:Nested runat=server></{0}:Nested2>")]
    > > > > > > > > > [ParseChildren(true, "NestedChild"),

    > PersistChildren(false)]
    > > > > > > > > > public class Nested : WebControl, INamingContainer,
    > > > > > > > > > IPostBackDataHandler {
    > > > > > > > > >
    > > > > > > > > > private NestedChildCollection _NestedChildren = new
    > > > > > > > > > NestedChildCollection();
    > > > > > > > > >
    > > > > > > > > > #region Properties
    > > > > > > > > > [
    > > > > > > > > >
    > > > > > > > >
    > > > > > >
    > > > >

    > DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
    > > > > > > > > > PersistenceMode(PersistenceMode.InnerDefaultProperty),
    > > > > > > > > > Description("A collection of nested children"),
    > > > > > > > > > ]
    > > > > > > > > > public NestedChildCollection NestedChild {
    > > > > > > > > > get{ return _NestedChildren; }
    > > > > > > > > > }
    > > > > > > > > > #endregion
    > > > > > > > > >
    > > > > > > > > > #region Overrides
    > > > > > > > > >
    > > > > > > > > > protected override void CreateChildControls() {
    > > > > > > > > > this.Controls.Clear();
    > > > > > > > > > foreach (NestedChild child in _NestedChildren) {
    > > > > > > > > > this.Controls.Add(child);
    > > > > > > > > > }
    > > > > > > > > > }
    > > > > > > > > >
    > > > > > > > > > protected override void Render(HtmlTextWriter output) {
    > > > > > > > > > output.Write("<b>before</b><br /><hr><br />");
    > > > > > > > > > //base.Render(output);
    > > > > > > > > > foreach(NestedChild child in _NestedChildren) {
    > > > > > > > > > output.Write("* " + child.Caption + "<BR>");
    > > > > > > > > >
    > > > > > > > > > // Display the literal contents of NestedChild
    > > > > > > > > > // in a single table cell
    > > > > > > > > > HtmlTableCell td = new HtmlTableCell();
    > > > > > > > > > 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.CellSpacing = 2;
    > > > > > > > > > table.CellPadding = 2;
    > > > > > > > > > table.Border = 1;
    > > > > > > > > > table.Style.Add("background-color", "#C0C0C0");
    > > > > > > > > > table.Controls.Add(tr);
    > > > > > > > > >
    > > > > > > > > > /* FAILS NEXT LINE
    > > > > > > > > > Error: "Unable to find control id 'txtTest1'

    > referenced
    > > > > > > > > > by the 'ControlToValidate' property of 'valTest1'"
    > > > > > > > > > /*
    > > > > > > > > > table.RenderControl(output);
    > > > > > > > > > }
    > > > > > > > > > output.Write("<br /><hr><br /><b>after</b>");
    > > > > > > > > > }
    > > > > > > > > > #endregion
    > > > > > > > > >
    > > > > > > > > > #region Implements IPostBackDataHandler (postback only)
    > > > > > > > > > bool IPostBackDataHandler.LoadPostData(string

    > strPostDataKey,
    > > > > > > > > > NameValueCollection postDataCollection) {
    > > > > > > > > > return true;
    > > > > > > > > > }
    > > > > > > > > > void IPostBackDataHandler.RaisePostDataChangedEvent() {}
    > > > > > > > > > #endregion
    > > > > > > > > > }
    > > > > > > > > >
    > > > > > > > > > [ToolboxItem(false), DefaultProperty("Caption")]
    > > > > > > > > > public class NestedChild : Control{
    > > > > > > > > > private String m_strCaption;
    > > > > > > > > > public String Caption {
    > > > > > > > > > get { return m_strCaption; }
    > > > > > > > > > set { m_strCaption = value; }
    > > > > > > > > > }
    > > > > > > > > > }
    > > > > > > > > >
    > > > > > > > > > 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);
    > > > > > > > > > }
    > > > > > > > > > }
    > > > > > > > > > }
    > > > > > > > > >
    > > > > > > > > > When deployed the aspx page looks like:
    > > > > > > > > >
    > > > > > > > > > <form id="Form1" method="post" runat="server">
    > > > > > > > > > <P>Nested Control</P>
    > > > > > > > > > <P>
    > > > > > > > > > <cc1:Nested id="Nested1" runat="server">
    > > > > > > > > > <cc1:NestedChild Caption="NestedChild">
    > > > > > > > > > <asp:Button id="cmdTest" runat="server" Text="Generate
    > > > > > > > > > Event"></asp:Button>
    > > > > > > > > > <asp:TextBox id="txtTest" runat="server"></asp:TextBox>
    > > > > > > > > > <asp:RequiredFieldValidator id="valTest" runat="server"
    > > > > > > > > > ControlToValidate="txtTest"
    > > > > > > > > > ErrorMessage="This field is
    > > > > > > > > > Required.">*</asp:RequiredFieldValidator>
    > > > > > > > > > </cc1:NestedChild>
    > > > > > > > > > </cc1:Nested2></P>
    > > > > > > > > > <P>&nbsp;</P>
    > > > > > > > > > <P>
    > > > > > > > > > <asp:label id="lblResult"

    > runat="server">lblResult</asp:label></P>
    > > > > > > > > > </form>
    > > > > > > > > >
    > > > > > > > > > If I remove 'RequiredFieldValidator' my control render as

    > expected.
    > > > > > > > > >
    > > > > > > > > > I have noticed, that If I replace the entire formatting

    > logic
    > in
    > the
    > > > > > > > > > 'Render' method with a call to 'base.Render(output);', the
    > > > > > > > > > RequiredFieldValidator works as expected.
    > > > > > > > > >
    > > > > > > > > > I think this is quite a technical problem and I assume that
    > > > > > > > > > 'table.RenderControl(output)' is not preforming the

    > necessary
    > > > > > > > > > server-side pre-validation to pass a

    > 'RequiredFieldValidator'.
    > How
    > can
    > > > > > > > > > I do this?
    > > > > > > > > >
    > > > > > > > > > Regards,
    > > > > > > > > >
    > > > > > > > > > Stephen

    > >
    > >
     
    Stephen Miller, Jan 17, 2004
    #7
  8. Guys,

    .... Two steps forward, one step back

    I've found another peculiarity regarding event handling. Extending the
    simplified version of the code posted previously, my control also
    implements IpostBackEventHandler and raises a default event
    'SelectedIndexChanged' for the onClick event for each nested child's
    table cell. For example:

    ....
    td.Attributes.Add("onclick", "javascript:" +
    Page.GetPostBackEventReference(this,
    _NestedChildren.IndexOf(NestedChild).ToString()))
    ....

    I have implemented the RaisePostBackEvent to pass the event argument
    to a local variable.

    void IPostBackEventHandler.RaisePostBackEvent(String strEventArgument)
    {

    if (strEventArgument == null)
    return;

    _selectedIndex = Int32.Parse(strEventArgument);

    }

    This enables a user to select a particular NestedChild, with further
    logic on postback.

    Using page tracing and liberal use of the 'Page.Trace.Write' method, I
    have observed some strange postback handling.

    If a selected NestedChild contains just simple HTML markup, then the
    processing order on onClick is:

    LoadViewState
    ProcessPostData
    `-> IPostBackDataHandler.LoadPostData
    ChangedEvents
    `-> IPostBackDataHandler.RaisePostDataChangedEvent
    PostBackEvent
    `-> IPostBackEventHandler.RaisePostBackEvent
    PreRender
    `-> CreateChildControls
    SaveViewState
    Render

    If however, I select a NestedChild that contains server controls, then
    the processing order becomes:

    LoadViewState
    ProcessPostData
    `-> CreateChildControls
    `-> IPostBackDataHandler.LoadPostData
    ChangedEvents
    `-> IPostBackDataHandler.RaisePostDataChangedEvent
    PostBackEvent
    `-> IPostBackEventHandler.RaisePostBackEvent
    PreRender
    SaveViewState
    Render

    I'm expecting CreateChildControls to be called in the PreRender phase
    (after RaisePostBackEvent, when the _selectedIndex becomes known). Why
    is CreateChildControls not being called in PreRender, when the
    NestedChild that contains server controls? How can I raise the
    PostBackEvent (and the OnSelectedIndexChanged method), before the
    CreateChildControls method is called?

    Regards,

    Stephen

    "Alessandro Zifiglio" <> wrote in message news:<PdtNb.3378$>...
    > in case it is not clear, i'm referring to the template you are using, which
    > inherits control. When you develop templated controls, you should implement
    > this interface InameingContainer to avoid naming conflicts on a page. . Your
    > main class nested is un-necessarily implementing this interface as you
    > already have clientID which is is unique, and you can name your table :
    > clientID + "_table";
    >
    > "Alessandro Zifiglio" <> wrote in
    > message news:x%rNb.3283$...
    > > have your control implement INamingContainer. This will generate Unique

    > Ids
    > > "Stephen Miller" <> wrote in message
    > > news:...
    > > > 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
    > > >
    > > >
    > > > "Peter Blum" <> wrote in message

    > news:<>...
    > > > > I strongly encourage you to step back and learn the right way to build
    > > > > custom controls. Otherwise, you will be developing code that may

    > require
    > > > > plenty more debugging and maintenance because it doesn't confirm to

    > how
    > > > > Microsoft designed web controls. I recommend the book "Developing

    > ASP.NET
    > > > > Server Controls and Components" from Microsoft Press.
    > > > >
    > > > > In your code, I do not understand why you have the _NestedChildren

    > property.
    > > > > The webcontrol framework automatically supports child controls using

    > the
    > > > > ParseChildrenAttribute. (See the .net docs on this attribute.) I do
    > > > > understand what you are doing in CreateChildControls and that looks

    > OK.
    > > > >
    > > > > Here are my recommendations.
    > > > > - Move the code you have creating the table and its contents into
    > > > > CreateChildControls without removing the existing code.
    > > > > - Any "output.Write()" calls can become LiteralControl objects. (new
    > > > > LiteralControl("<br>"))
    > > > > - Add objects to the Controls collection on your control.
    > > > >
    > > > > Now your validators will be created before OnPreRender occurs. That

    > allows
    > > > > the webcontrol system that Microsoft designed to call each of your
    > > > > validators own OnPreRender method properly, setting up many things

    > that
    > are
    > > > > required for validation.
    > > > >
    > > > > --- Peter Blum
    > > > > www.PeterBlum.com
    > > > > Email:
    > > > > "Stephen Miller" <> wrote in message
    > > > > news:...
    > > > > > Peter,
    > > > > >
    > > > > > I was under the mistaken impression that I was already overriding
    > > > > > CreateChildControls to create the controls with the code:
    > > > > >
    > > > > > protected override void CreateChildControls() {
    > > > > > this.Controls.Clear();
    > > > > > foreach (NestedChild child in _NestedChildren) {
    > > > > > this.Controls.Add(child);
    > > > > > }
    > > > > > }
    > > > > >
    > > > > > If I leave this override out then the control renders fine (with the
    > > > > > RequiredFieldValidators) but fails to generate any server side

    > events.
    > > > > > Moving this code block to OnPreRender (with base.OnPreRend) after
    > > > > > adding the controls doesn't seem to make any difference.
    > > > > >
    > > > > > I have also tried moving the code in the overridden Render method to
    > > > > > the overridden RenderContents method. This doesn't appear to have

    > made
    > > > > > any difference.
    > > > > >
    > > > > > Have I missed the point? Could I beg you for some sample code?
    > > > > >
    > > > > > Thanks,
    > > > > >
    > > > > > Stephen
    > > > > >
    > > > > > "Peter Blum" <> wrote in message

    > news:<>...
    > > > > > > I think I see the problem. You are creating your control objects

    > in
    > the
    > > > > > > Render method. Leave the Render method alone. Generally, only use

    > it
    > if
    > you
    > > > > > > wanted to output explicit HTML (although there are some cases

    > where
    > you
    > can
    > > > > > > do what you did.) Instead, create the controls in either the
    > > > > > > CreateChildControls() or OnPreRender method.
    > > > > > >
    > > > > > > Here's the actual problem you are having. Validators must have

    > their
    > > > > > > OnPreRender method run. Because your validators aren't in this

    > control's
    > > > > > > Controls property, they don't get OnPreRender run. So move your

    > code
    > to
    > > > > > > OnPreRender and call base.OnPreRender() after those controls are

    > added
    > to
    > > > > > > the Controls property.
    > > > > > >
    > > > > > > --- Peter Blum
    > > > > > > www.PeterBlum.com
    > > > > > > Email:
    > > > > > >
    > > > > > > "Stephen Miller" <> wrote in message
    > > > > > > news:...
    > > > > > > > Peter,
    > > > > > > >
    > > > > > > > Thanks for the link. I have enjoyed looking at your site, but

    > its
    > not
    > > > > > > > going to give me the answer I need. My control needs to be able

    > to
    > > > > > > > render any nested literal content the developer throws at it, so

    > I
    > > > > > > > have to resolve this problem with MS's validators.
    > > > > > > >
    > > > > > > > I have designed my control in such a way that it only needs to
    > > > > > > > validate controls in the same naming container but as you noted,

    > the
    > > > > > > > error message seems to indicate that the control thinks the

    > textbox
    > > > > > > > and validator are in different containers.
    > > > > > > >
    > > > > > > > What I find particularly puzzling is that if I simple override

    > the
    > > > > > > > render method with 'base.Render(output)' the validator has not

    > problem
    > > > > > > > locating the textbox in the container. The problem only seems to

    > arise
    > > > > > > > when I attempt to encapsulate the controls in my own formatting

    > with
    > > > > > > > 'table.RenderControl(output)'.
    > > > > > > >
    > > > > > > > Should I be looking at overriding the 'table.RenderControl'

    > method
    > to
    > > > > > > > make this work? If so what should I be doing?
    > > > > > > >
    > > > > > > > I recently found another commercial product that is able to do

    > what
    > > > > > > > I'm trying to achive
    > > > > > > >

    > (http://www.infragistics.com/products/navigation.asp?sec=0&cat=3)
    > so I
    > > > > > > > know it must be possible (... and I don't want to pay USD $500)
    > > > > > > >
    > > > > > > > Thanks,
    > > > > > > >
    > > > > > > > Stephen
    > > > > > > >
    > > > > > > >
    > > > > > > >
    > > > > > > > "Peter Blum" <> wrote in message

    > news:<O#>...
    > > > > > > > > The ControlToValidate property only accepts the ID of another
    > > > > control in
    > > > > the
    > > > > > > > > same naming container. I believe that this error is due to

    > naming
    > container
    > > > > > > > > problems. While your code seems to enclose both the textbox

    > and
    > validator
    > > > > > > > > within the same naming container, the error message says

    > otherwise.
    > > > > > > > >
    > > > > > > > > FYI: If the naming container is indeed the problem, I have a

    > commercial
    > > > > > > > > product that replaces Microsoft's validators to overcome its

    > many
    > > > > > > > > limitations. My validators support controls in any naming

    > container.
    > The
    > > > > > > > > product is "Professional Validation And More". Details are at
    > > > > > > > > http://www.peterblum.com/vam/home.aspx.
    > > > > > > > >
    > > > > > > > > --- Peter Blum
    > > > > > > > > www.PeterBlum.com
    > > > > > > > > Email:
    > > > > > > > >
    > > > > > > > > "Stephen Miller" <> wrote in message
    > > > > > > > > news:...
    > > > > > > > > > Gurus,
    > > > > > > > > >
    > > > > > > > > > I have a custom web control that in turn has nested child

    > controls. I
    > > > > > > > > > want to be able to encapsulated and render any literal HTML

    > and or
    > > > > > > > > > server controls placed between the child control's tags.

    > This
    > works
    > > > > > > > > > fine, unless I add a RequiredFieldValidator control at which

    > point
    > my
    > > > > > > > > > aspx page fails at

    > 'WebControls.BaseValidator.CheckControlValidationProperty'
    > > > > > > > > > with the error message 'Unable to find control id 'TextBox1'
    > > > > > > > > > referenced by the 'ControlToValidate' property of
    > > > > > > > > > 'RequiredFieldValidator1'.
    > > > > > > > > >
    > > > > > > > > > My control 'Nested' contains a collection of 'NestedChild'

    > controls
    > > > > > > > > > with a simple 'Caption' property. I override the 'Render'

    > method
    > on
    > > > > > > > > > the 'Nested' control and iterate though each NestedChild' in

    > the
    > > > > > > > > > collection, outputting the child's literal content to a

    > formatted
    > > > > > > > > > table.
    > > > > > > > > >
    > > > > > > > > > The full code for my control is (please cut-n-paste to

    > replicate
    > my
    > > > > > > > > > problem):
    > > > > > > > > >
    > > > > > > > > > using System;
    > > > > > > > > > using System.Web.UI;
    > > > > > > > > > using System.Web.UI.WebControls;
    > > > > > > > > > using System.ComponentModel;
    > > > > > > > > > using System.Collections;
    > > > > > > > > > using System.Collections.Specialized;
    > > > > > > > > > using System.Web.UI.HtmlControls;
    > > > > > > > > >
    > > > > > > > > > namespace Custom.Web.UI {
    > > > > > > > > >
    > > > > > > > > > [ToolboxData("<{0}:Nested runat=server></{0}:Nested2>")]
    > > > > > > > > > [ParseChildren(true, "NestedChild"),

    > PersistChildren(false)]
    > > > > > > > > > public class Nested : WebControl, INamingContainer,
    > > > > > > > > > IPostBackDataHandler {
    > > > > > > > > >
    > > > > > > > > > private NestedChildCollection _NestedChildren = new
    > > > > > > > > > NestedChildCollection();
    > > > > > > > > >
    > > > > > > > > > #region Properties
    > > > > > > > > > [
    > > > > > > > > >
    > > > > > > > >
    > > > > > >
    > > > >

    > DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
    > > > > > > > > > PersistenceMode(PersistenceMode.InnerDefaultProperty),
    > > > > > > > > > Description("A collection of nested children"),
    > > > > > > > > > ]
    > > > > > > > > > public NestedChildCollection NestedChild {
    > > > > > > > > > get{ return _NestedChildren; }
    > > > > > > > > > }
    > > > > > > > > > #endregion
    > > > > > > > > >
    > > > > > > > > > #region Overrides
    > > > > > > > > >
    > > > > > > > > > protected override void CreateChildControls() {
    > > > > > > > > > this.Controls.Clear();
    > > > > > > > > > foreach (NestedChild child in _NestedChildren) {
    > > > > > > > > > this.Controls.Add(child);
    > > > > > > > > > }
    > > > > > > > > > }
    > > > > > > > > >
    > > > > > > > > > protected override void Render(HtmlTextWriter output) {
    > > > > > > > > > output.Write("<b>before</b><br /><hr><br />");
    > > > > > > > > > //base.Render(output);
    > > > > > > > > > foreach(NestedChild child in _NestedChildren) {
    > > > > > > > > > output.Write("* " + child.Caption + "<BR>");
    > > > > > > > > >
    > > > > > > > > > // Display the literal contents of NestedChild
    > > > > > > > > > // in a single table cell
    > > > > > > > > > HtmlTableCell td = new HtmlTableCell();
    > > > > > > > > > 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.CellSpacing = 2;
    > > > > > > > > > table.CellPadding = 2;
    > > > > > > > > > table.Border = 1;
    > > > > > > > > > table.Style.Add("background-color", "#C0C0C0");
    > > > > > > > > > table.Controls.Add(tr);
    > > > > > > > > >
    > > > > > > > > > /* FAILS NEXT LINE
    > > > > > > > > > Error: "Unable to find control id 'txtTest1'

    > referenced
    > > > > > > > > > by the 'ControlToValidate' property of 'valTest1'"
    > > > > > > > > > /*
    > > > > > > > > > table.RenderControl(output);
    > > > > > > > > > }
    > > > > > > > > > output.Write("<br /><hr><br /><b>after</b>");
    > > > > > > > > > }
    > > > > > > > > > #endregion
    > > > > > > > > >
    > > > > > > > > > #region Implements IPostBackDataHandler (postback only)
    > > > > > > > > > bool IPostBackDataHandler.LoadPostData(string

    > strPostDataKey,
    > > > > > > > > > NameValueCollection postDataCollection) {
    > > > > > > > > > return true;
    > > > > > > > > > }
    > > > > > > > > > void IPostBackDataHandler.RaisePostDataChangedEvent() {}
    > > > > > > > > > #endregion
    > > > > > > > > > }
    > > > > > > > > >
    > > > > > > > > > [ToolboxItem(false), DefaultProperty("Caption")]
    > > > > > > > > > public class NestedChild : Control{
    > > > > > > > > > private String m_strCaption;
    > > > > > > > > > public String Caption {
    > > > > > > > > > get { return m_strCaption; }
    > > > > > > > > > set { m_strCaption = value; }
    > > > > > > > > > }
    > > > > > > > > > }
    > > > > > > > > >
    > > > > > > > > > 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);
    > > > > > > > > > }
    > > > > > > > > > }
    > > > > > > > > > }
    > > > > > > > > >
    > > > > > > > > > When deployed the aspx page looks like:
    > > > > > > > > >
    > > > > > > > > > <form id="Form1" method="post" runat="server">
    > > > > > > > > > <P>Nested Control</P>
    > > > > > > > > > <P>
    > > > > > > > > > <cc1:Nested id="Nested1" runat="server">
    > > > > > > > > > <cc1:NestedChild Caption="NestedChild">
    > > > > > > > > > <asp:Button id="cmdTest" runat="server" Text="Generate
    > > > > > > > > > Event"></asp:Button>
    > > > > > > > > > <asp:TextBox id="txtTest" runat="server"></asp:TextBox>
    > > > > > > > > > <asp:RequiredFieldValidator id="valTest" runat="server"
    > > > > > > > > > ControlToValidate="txtTest"
    > > > > > > > > > ErrorMessage="This field is
    > > > > > > > > > Required.">*</asp:RequiredFieldValidator>
    > > > > > > > > > </cc1:NestedChild>
    > > > > > > > > > </cc1:Nested2></P>
    > > > > > > > > > <P>&nbsp;</P>
    > > > > > > > > > <P>
    > > > > > > > > > <asp:label id="lblResult"

    > runat="server">lblResult</asp:label></P>
    > > > > > > > > > </form>
    > > > > > > > > >
    > > > > > > > > > If I remove 'RequiredFieldValidator' my control render as

    > expected.
    > > > > > > > > >
    > > > > > > > > > I have noticed, that If I replace the entire formatting

    > logic
    > in
    > the
    > > > > > > > > > 'Render' method with a call to 'base.Render(output);', the
    > > > > > > > > > RequiredFieldValidator works as expected.
    > > > > > > > > >
    > > > > > > > > > I think this is quite a technical problem and I assume that
    > > > > > > > > > 'table.RenderControl(output)' is not preforming the

    > necessary
    > > > > > > > > > server-side pre-validation to pass a

    > 'RequiredFieldValidator'.
    > How
    > can
    > > > > > > > > > I do this?
    > > > > > > > > >
    > > > > > > > > > Regards,
    > > > > > > > > >
    > > > > > > > > > Stephen

    > >
    > >
     
    Stephen Miller, Jan 17, 2004
    #8
  9. 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/d...html/cpcontemplateddataboundcontrolsample.asp

    Not very hard to follow once you get a hang of it ;)





    "Peter Blum" <> wrote in message
    news:...
    > 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:
    >
    > "Stephen Miller" <> wrote in message
    > news:...
    > > 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
    > >

    >
    >
     
    Alessandro Zifiglio, Jan 17, 2004
    #9
  10. 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(NestedChildCollection 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" <> wrote in
    message news:l_bOb.4409$...
    > 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/d...html/cpcontemplateddataboundcontrolsample.asp
    >
    > Not very hard to follow once you get a hang of it ;)
    >
    >
    >
    >
    >
    > "Peter Blum" <> wrote in message
    > news:...
    > > 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:
    > >
    > > "Stephen Miller" <> wrote in message
    > > news:...
    > > > 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
    > > >

    > >
    > >

    >
    >
     
    Alessandro Zifiglio, Jan 19, 2004
    #10
  11. 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" <> wrote in message news:<EEIOb.4937$>...
    > 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(NestedChildCollection 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" <> wrote in
    > message news:l_bOb.4409$...
    > > 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/d...html/cpcontemplateddataboundcontrolsample.asp
    > >
    > > Not very hard to follow once you get a hang of it ;)
    > >
    > >
    > >
    > >
    > >
    > > "Peter Blum" <> wrote in message
    > > news:...
    > > > 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:
    > > >
    > > > "Stephen Miller" <> wrote in message
    > > > news:...
    > > > > 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
    > > > >
    > > >
    > > >

    > >
    > >
     
    Stephen Miller, Jan 19, 2004
    #11
  12. 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" <> wrote in message
    news:...
    > 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" <> wrote in

    message news:<EEIOb.4937$>...
    > > 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(NestedChildCollection 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" <> wrote in
    > > message news:l_bOb.4409$...
    > > > 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/d...html/cpcontemplateddataboundcontrolsample.asp
    > > >
    > > > Not very hard to follow once you get a hang of it ;)
    > > >
    > > >
    > > >
    > > >
    > > >
    > > > "Peter Blum" <> wrote in message
    > > > news:...
    > > > > 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:
    > > > >
    > > > > "Stephen Miller" <> wrote in message
    > > > > news:...
    > > > > > 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
    > > > > >
    > > > >
    > > > >
    > > >
    > > >
     
    Alessandro Zifiglio, Jan 19, 2004
    #12
  13. Alessandro,

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

    I also solved my viewstate problem (see
    http://www.denisbauer.com/ASPNETControls/DynamicControlsPlaceholder.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" <> wrote in message news:<42ZOb.5350$>...
    > 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" <> wrote in message
    > news:...
    > > 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" <> wrote in

    > message news:<EEIOb.4937$>...
    > > > 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(NestedChildCollection 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" <> wrote in
    > > > message news:l_bOb.4409$...
    > > > > 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/d...html/cpcontemplateddataboundcontrolsample.asp
    > > > >
    > > > > Not very hard to follow once you get a hang of it ;)
    > > > >
    > > > >
    > > > >
    > > > >
    > > > >
    > > > > "Peter Blum" <> wrote in message
    > > > > news:...
    > > > > > 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:
    > > > > >
    > > > > > "Stephen Miller" <> wrote in message
    > > > > > news:...
    > > > > > > 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
    > > > > > >
    > > > > >
    > > > > >
    > > > >
    > > > >
     
    Stephen Miller, Jan 20, 2004
    #13
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Ranganath

    Custom Tags within Custom Tags.

    Ranganath, Oct 17, 2003, in forum: Java
    Replies:
    2
    Views:
    498
    Ranganath
    Oct 21, 2003
  2. Dave
    Replies:
    0
    Views:
    467
  3. John
    Replies:
    0
    Views:
    338
  4. Peter Colson

    Overriding ImageButton Render in a custom control

    Peter Colson, Nov 18, 2003, in forum: ASP .Net Web Controls
    Replies:
    3
    Views:
    327
    Peter Colson
    Nov 18, 2003
  5. Glenn Gillen
    Replies:
    0
    Views:
    323
    Glenn Gillen
    Nov 17, 2006
Loading...

Share This Page