![]() |
Problem overriding render method to format literal content of nested tags in custom control
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.CheckControlValidationP roperty' 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(DesignerSerializat ionVisibility.Content), PersistenceMode(PersistenceMode.InnerDefaultProper ty), 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> </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 |
Re: Problem overriding render method to format literal content of nested tags in custom control
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...sp?sec=0&cat=3) so I know it must be possible (... and I don't want to pay USD $500) Thanks, Stephen "Peter Blum" <PLBlum@Blum.info> wrote in message news:<O#52x0h0DHA.540@tk2msftngp13.phx.gbl>... > 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: PLBlum@PeterBlum.com > > "Stephen Miller" <jsausten@hotmail.com> wrote in message > news:cdb404de.0401022120.363a51d4@posting.google.c om... > > 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.CheckControlValidationP roperty' > > 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(DesignerSerializat ionVisibility.Content), > > PersistenceMode(PersistenceMode.InnerDefaultProper ty), > > 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> </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 |
Re: Problem overriding render method to format literal content of nested tags in custom control
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" <PLBlum@Blum.info> wrote in message news:<uR1AAkx0DHA.1684@TK2MSFTNGP12.phx.gbl>... > 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: PLBlum@PeterBlum.com > > "Stephen Miller" <jsausten@hotmail.com> wrote in message > news:cdb404de.0401032152.5faee8e3@posting.google.c om... > > 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...sp?sec=0&cat=3) so I > > know it must be possible (... and I don't want to pay USD $500) > > > > Thanks, > > > > Stephen > > > > > > > > "Peter Blum" <PLBlum@Blum.info> wrote in message > news:<O#52x0h0DHA.540@tk2msftngp13.phx.gbl>... > > > 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: PLBlum@PeterBlum.com > > > > > > "Stephen Miller" <jsausten@hotmail.com> wrote in message > > > news:cdb404de.0401022120.363a51d4@posting.google.c om... > > > > 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.CheckControlValidationP roperty' > > > > 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(DesignerSerializat ionVisibility.Content), > > > > PersistenceMode(PersistenceMode.InnerDefaultProper ty), > > > > 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> </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 |
Re: Problem overriding render method to format literal content of nested tags in custom control
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" <PLBlum@Blum.info> wrote in message news:<O6QJcFt1DHA.2676@tk2msftngp13.phx.gbl>... > 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: PLBlum@PeterBlum.com > "Stephen Miller" <jsausten@hotmail.com> wrote in message > news:cdb404de.0401062350.6818678@posting.google.co m... > > 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" <PLBlum@Blum.info> wrote in message > news:<uR1AAkx0DHA.1684@TK2MSFTNGP12.phx.gbl>... > > > 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: PLBlum@PeterBlum.com > > > > > > "Stephen Miller" <jsausten@hotmail.com> wrote in message > > > news:cdb404de.0401032152.5faee8e3@posting.google.c om... > > > > 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...sp?sec=0&cat=3) so I > > > > know it must be possible (... and I don't want to pay USD $500) > > > > > > > > Thanks, > > > > > > > > Stephen > > > > > > > > > > > > > > > > "Peter Blum" <PLBlum@Blum.info> wrote in message > news:<O#52x0h0DHA.540@tk2msftngp13.phx.gbl>... > > > > > 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: PLBlum@PeterBlum.com > > > > > > > > > > "Stephen Miller" <jsausten@hotmail.com> wrote in message > > > > > news:cdb404de.0401022120.363a51d4@posting.google.c om... > > > > > > 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.CheckControlValidationP roperty' > > > > > > 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(DesignerSerializat ionVisibility.Content), > > > > > > PersistenceMode(PersistenceMode.InnerDefaultProper ty), > > > > > > 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> </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 |
Re: Problem overriding render method to format literal content of nested tags in custom control
have your control implement INamingContainer. This will generate Unique Ids
"Stephen Miller" <jsausten@hotmail.com> wrote in message news:cdb404de.0401110024.b442c6f@posting.google.co m... > Peter, > > Again, thanks for your on going help. > > I have been struggling with this problem for 3+ months and on someone > else's advice purchased "Developing Microsoft ASP.NET Server Controls > and Components". Following the book religiously, I started again from > the "Hello World" example and worked forward to recreate my problem. > Kothari & Datye's touches on controls "whose nested content does not > correspond to properties" on pages 332-7, but the example provided is > very trivial and doesn't address any of the issues I've encountered. > If I've missed something, please point me to the section. > > My control uses the ParseChildrenAttribute attribute with the > declaration '[ParseChildren(true, "NestedChild")', which defines a > public property named 'NestedChild', with nested (child) elements > corresponding to child elements of the 'NestedChild' property. The > idea with the '_NestedChildren' property is to add each nested child > element to a collection, which I can iterate through and hide or show > based on additional conditions. I have extra logic here, which I have > omitted for clarity. > > As you suggested, I have move my code to CreateChildControls. The > control renders ok in design time, but fails in runtime at > FillNamedControlsTable with the error message "Multiple controls with > the same ID 'myControl1' were found. FindControl requires that > controls have unique IDs". With the RequiredFieldValidator removed, > the control renders but generates the same error with the buttons > onClick event. Looking at the source code I notice that the command > button has now rendered as: > > <input type="submit" name="myControl1:cmdTest1" value="Test" > id="myControl1_cmdTest1" /> > > I can't compile my code behind with an event handling > myControl1_cmdTest1.Click, because the compiler is expecting > cmdTest1.click. > > My code for CreateChildControls now looks like: > > protected override void CreateChildControls() { > > this.Controls.Clear(); > this.Controls.Add(new LiteralControl("<b>before</b><br /><hr><br > />")); > > foreach (NestedChild child in _NestedChildren) { > //this.Controls.Add(child); > > this.Controls.Add(new LiteralControl("* " + child.Caption + > "<BR>")); > > // Display the contents of child in a single cell table > HtmlTableCell td = new HtmlTableCell(); > td.Style.Add("width", "250px"); > td.Style.Add("height", "100px"); > td.Controls.Add(child); > > // Add the table cell to a new table row > HtmlTableRow tr = new HtmlTableRow(); > tr.Controls.Add(td); > > // Add the table row to a new table > HtmlTable table = new HtmlTable(); > table.ID = this.UniqueID; > table.Width = this.Width.ToString(); > table.Height = this.Height.ToString(); > table.CellSpacing = 2; > table.CellPadding = 2; > table.Border = 1; > table.Style.Add("background-color", "#C0C0C0"); > table.Controls.Add(tr); > > this.Controls.Add(table); > > } > this.Controls.Add(new LiteralControl("<br /><hr><br > /><b>after</b>")); > } > > Any other suggestions? > > Regards, > > Stephen > > > "Peter Blum" <PLBlum@Blum.info> wrote in message news:<O6QJcFt1DHA.2676@tk2msftngp13.phx.gbl>... > > 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: PLBlum@PeterBlum.com > > "Stephen Miller" <jsausten@hotmail.com> wrote in message > > news:cdb404de.0401062350.6818678@posting.google.co m... > > > 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" <PLBlum@Blum.info> wrote in message > > news:<uR1AAkx0DHA.1684@TK2MSFTNGP12.phx.gbl>... > > > > 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: PLBlum@PeterBlum.com > > > > > > > > "Stephen Miller" <jsausten@hotmail.com> wrote in message > > > > news:cdb404de.0401032152.5faee8e3@posting.google.c om... > > > > > 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...sp?sec=0&cat=3) so I > > > > > know it must be possible (... and I don't want to pay USD $500) > > > > > > > > > > Thanks, > > > > > > > > > > Stephen > > > > > > > > > > > > > > > > > > > > "Peter Blum" <PLBlum@Blum.info> wrote in message > > news:<O#52x0h0DHA.540@tk2msftngp13.phx.gbl>... > > > > > > 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: PLBlum@PeterBlum.com > > > > > > > > > > > > "Stephen Miller" <jsausten@hotmail.com> wrote in message > > > > > > news:cdb404de.0401022120.363a51d4@posting.google.c om... > > > > > > > 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.CheckControlValidationP roperty' > > > > > > > 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(DesignerSerializat ionVisibility.Content), > > > > > > > PersistenceMode(PersistenceMode.InnerDefaultProper ty), > > > > > > > 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> </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 |
Re: Problem overriding render method to format literal content of nested tags in custom control
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" <alessandrozifiglio@NO-SPAM-hotmail.com> wrote in message news:x%rNb.3283$nC1.1688@news.edisontel.com... > have your control implement INamingContainer. This will generate Unique Ids > "Stephen Miller" <jsausten@hotmail.com> wrote in message > news:cdb404de.0401110024.b442c6f@posting.google.co m... > > Peter, > > > > Again, thanks for your on going help. > > > > I have been struggling with this problem for 3+ months and on someone > > else's advice purchased "Developing Microsoft ASP.NET Server Controls > > and Components". Following the book religiously, I started again from > > the "Hello World" example and worked forward to recreate my problem. > > Kothari & Datye's touches on controls "whose nested content does not > > correspond to properties" on pages 332-7, but the example provided is > > very trivial and doesn't address any of the issues I've encountered. > > If I've missed something, please point me to the section. > > > > My control uses the ParseChildrenAttribute attribute with the > > declaration '[ParseChildren(true, "NestedChild")', which defines a > > public property named 'NestedChild', with nested (child) elements > > corresponding to child elements of the 'NestedChild' property. The > > idea with the '_NestedChildren' property is to add each nested child > > element to a collection, which I can iterate through and hide or show > > based on additional conditions. I have extra logic here, which I have > > omitted for clarity. > > > > As you suggested, I have move my code to CreateChildControls. The > > control renders ok in design time, but fails in runtime at > > FillNamedControlsTable with the error message "Multiple controls with > > the same ID 'myControl1' were found. FindControl requires that > > controls have unique IDs". With the RequiredFieldValidator removed, > > the control renders but generates the same error with the buttons > > onClick event. Looking at the source code I notice that the command > > button has now rendered as: > > > > <input type="submit" name="myControl1:cmdTest1" value="Test" > > id="myControl1_cmdTest1" /> > > > > I can't compile my code behind with an event handling > > myControl1_cmdTest1.Click, because the compiler is expecting > > cmdTest1.click. > > > > My code for CreateChildControls now looks like: > > > > protected override void CreateChildControls() { > > > > this.Controls.Clear(); > > this.Controls.Add(new LiteralControl("<b>before</b><br /><hr><br > > />")); > > > > foreach (NestedChild child in _NestedChildren) { > > //this.Controls.Add(child); > > > > this.Controls.Add(new LiteralControl("* " + child.Caption + > > "<BR>")); > > > > // Display the contents of child in a single cell table > > HtmlTableCell td = new HtmlTableCell(); > > td.Style.Add("width", "250px"); > > td.Style.Add("height", "100px"); > > td.Controls.Add(child); > > > > // Add the table cell to a new table row > > HtmlTableRow tr = new HtmlTableRow(); > > tr.Controls.Add(td); > > > > // Add the table row to a new table > > HtmlTable table = new HtmlTable(); > > table.ID = this.UniqueID; > > table.Width = this.Width.ToString(); > > table.Height = this.Height.ToString(); > > table.CellSpacing = 2; > > table.CellPadding = 2; > > table.Border = 1; > > table.Style.Add("background-color", "#C0C0C0"); > > table.Controls.Add(tr); > > > > this.Controls.Add(table); > > > > } > > this.Controls.Add(new LiteralControl("<br /><hr><br > > /><b>after</b>")); > > } > > > > Any other suggestions? > > > > Regards, > > > > Stephen > > > > > > "Peter Blum" <PLBlum@Blum.info> wrote in message > news:<O6QJcFt1DHA.2676@tk2msftngp13.phx.gbl>... > > > 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: PLBlum@PeterBlum.com > > > "Stephen Miller" <jsausten@hotmail.com> wrote in message > > > news:cdb404de.0401062350.6818678@posting.google.co m... > > > > 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" <PLBlum@Blum.info> wrote in message > > > news:<uR1AAkx0DHA.1684@TK2MSFTNGP12.phx.gbl>... > > > > > 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: PLBlum@PeterBlum.com > > > > > > > > > > "Stephen Miller" <jsausten@hotmail.com> wrote in message > > > > > news:cdb404de.0401032152.5faee8e3@posting.google.c om... > > > > > > 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...sp?sec=0&cat=3) > so I > > > > > > know it must be possible (... and I don't want to pay USD $500) > > > > > > > > > > > > Thanks, > > > > > > > > > > > > Stephen > > > > > > > > > > > > > > > > > > > > > > > > "Peter Blum" <PLBlum@Blum.info> wrote in message > > > news:<O#52x0h0DHA.540@tk2msftngp13.phx.gbl>... > > > > > > > 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: PLBlum@PeterBlum.com > > > > > > > > > > > > > > "Stephen Miller" <jsausten@hotmail.com> wrote in message > > > > > > > news:cdb404de.0401022120.363a51d4@posting.google.c om... > > > > > > > > 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.CheckControlValidationP roperty' > > > > > > > > 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(DesignerSerializat ionVisibility.Content), > > > > > > > > PersistenceMode(PersistenceMode.InnerDefaultProper ty), > > > > > > > > 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> </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 > > |
Re: Problem overriding render method to format literal content of nested tags in custom control
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" <alessandrozifiglio@NO-SPAM-hotmail.com> wrote in message news:<PdtNb.3378$nC1.474@news.edisontel.com>... > 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" <alessandrozifiglio@NO-SPAM-hotmail.com> wrote in > message news:x%rNb.3283$nC1.1688@news.edisontel.com... > > have your control implement INamingContainer. This will generate Unique > Ids > > "Stephen Miller" <jsausten@hotmail.com> wrote in message > > news:cdb404de.0401110024.b442c6f@posting.google.co m... > > > Peter, > > > > > > Again, thanks for your on going help. > > > > > > I have been struggling with this problem for 3+ months and on someone > > > else's advice purchased "Developing Microsoft ASP.NET Server Controls > > > and Components". Following the book religiously, I started again from > > > the "Hello World" example and worked forward to recreate my problem. > > > Kothari & Datye's touches on controls "whose nested content does not > > > correspond to properties" on pages 332-7, but the example provided is > > > very trivial and doesn't address any of the issues I've encountered. > > > If I've missed something, please point me to the section. > > > > > > My control uses the ParseChildrenAttribute attribute with the > > > declaration '[ParseChildren(true, "NestedChild")', which defines a > > > public property named 'NestedChild', with nested (child) elements > > > corresponding to child elements of the 'NestedChild' property. The > > > idea with the '_NestedChildren' property is to add each nested child > > > element to a collection, which I can iterate through and hide or show > > > based on additional conditions. I have extra logic here, which I have > > > omitted for clarity. > > > > > > As you suggested, I have move my code to CreateChildControls. The > > > control renders ok in design time, but fails in runtime at > > > FillNamedControlsTable with the error message "Multiple controls with > > > the same ID 'myControl1' were found. FindControl requires that > > > controls have unique IDs". With the RequiredFieldValidator removed, > > > the control renders but generates the same error with the buttons > > > onClick event. Looking at the source code I notice that the command > > > button has now rendered as: > > > > > > <input type="submit" name="myControl1:cmdTest1" value="Test" > > > id="myControl1_cmdTest1" /> > > > > > > I can't compile my code behind with an event handling > > > myControl1_cmdTest1.Click, because the compiler is expecting > > > cmdTest1.click. > > > > > > My code for CreateChildControls now looks like: > > > > > > protected override void CreateChildControls() { > > > > > > this.Controls.Clear(); > > > this.Controls.Add(new LiteralControl("<b>before</b><br /><hr><br > > > />")); > > > > > > foreach (NestedChild child in _NestedChildren) { > > > //this.Controls.Add(child); > > > > > > this.Controls.Add(new LiteralControl("* " + child.Caption + > > > "<BR>")); > > > > > > // Display the contents of child in a single cell table > > > HtmlTableCell td = new HtmlTableCell(); > > > td.Style.Add("width", "250px"); > > > td.Style.Add("height", "100px"); > > > td.Controls.Add(child); > > > > > > // Add the table cell to a new table row > > > HtmlTableRow tr = new HtmlTableRow(); > > > tr.Controls.Add(td); > > > > > > // Add the table row to a new table > > > HtmlTable table = new HtmlTable(); > > > table.ID = this.UniqueID; > > > table.Width = this.Width.ToString(); > > > table.Height = this.Height.ToString(); > > > table.CellSpacing = 2; > > > table.CellPadding = 2; > > > table.Border = 1; > > > table.Style.Add("background-color", "#C0C0C0"); > > > table.Controls.Add(tr); > > > > > > this.Controls.Add(table); > > > > > > } > > > this.Controls.Add(new LiteralControl("<br /><hr><br > > > /><b>after</b>")); > > > } > > > > > > Any other suggestions? > > > > > > Regards, > > > > > > Stephen > > > > > > > > > "Peter Blum" <PLBlum@Blum.info> wrote in message > news:<O6QJcFt1DHA.2676@tk2msftngp13.phx.gbl>... > > > > 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: PLBlum@PeterBlum.com > > > > "Stephen Miller" <jsausten@hotmail.com> wrote in message > > > > news:cdb404de.0401062350.6818678@posting.google.co m... > > > > > 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" <PLBlum@Blum.info> wrote in message > news:<uR1AAkx0DHA.1684@TK2MSFTNGP12.phx.gbl>... > > > > > > 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: PLBlum@PeterBlum.com > > > > > > > > > > > > "Stephen Miller" <jsausten@hotmail.com> wrote in message > > > > > > news:cdb404de.0401032152.5faee8e3@posting.google.c om... > > > > > > > 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...sp?sec=0&cat=3) > so I > > > > > > > know it must be possible (... and I don't want to pay USD $500) > > > > > > > > > > > > > > Thanks, > > > > > > > > > > > > > > Stephen > > > > > > > > > > > > > > > > > > > > > > > > > > > > "Peter Blum" <PLBlum@Blum.info> wrote in message > news:<O#52x0h0DHA.540@tk2msftngp13.phx.gbl>... > > > > > > > > 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: PLBlum@PeterBlum.com > > > > > > > > > > > > > > > > "Stephen Miller" <jsausten@hotmail.com> wrote in message > > > > > > > > news:cdb404de.0401022120.363a51d4@posting.google.c om... > > > > > > > > > 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.CheckControlValidationP roperty' > > > > > > > > > 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(DesignerSerializat ionVisibility.Content), > > > > > > > > > PersistenceMode(PersistenceMode.InnerDefaultProper ty), > > > > > > > > > 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> </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 > > > > |
Re: Problem overriding render method to format literal content of nested tags in custom control
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" <alessandrozifiglio@NO-SPAM-hotmail.com> wrote in message news:<PdtNb.3378$nC1.474@news.edisontel.com>... > 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" <alessandrozifiglio@NO-SPAM-hotmail.com> wrote in > message news:x%rNb.3283$nC1.1688@news.edisontel.com... > > have your control implement INamingContainer. This will generate Unique > Ids > > "Stephen Miller" <jsausten@hotmail.com> wrote in message > > news:cdb404de.0401110024.b442c6f@posting.google.co m... > > > Peter, > > > > > > Again, thanks for your on going help. > > > > > > I have been struggling with this problem for 3+ months and on someone > > > else's advice purchased "Developing Microsoft ASP.NET Server Controls > > > and Components". Following the book religiously, I started again from > > > the "Hello World" example and worked forward to recreate my problem. > > > Kothari & Datye's touches on controls "whose nested content does not > > > correspond to properties" on pages 332-7, but the example provided is > > > very trivial and doesn't address any of the issues I've encountered. > > > If I've missed something, please point me to the section. > > > > > > My control uses the ParseChildrenAttribute attribute with the > > > declaration '[ParseChildren(true, "NestedChild")', which defines a > > > public property named 'NestedChild', with nested (child) elements > > > corresponding to child elements of the 'NestedChild' property. The > > > idea with the '_NestedChildren' property is to add each nested child > > > element to a collection, which I can iterate through and hide or show > > > based on additional conditions. I have extra logic here, which I have > > > omitted for clarity. > > > > > > As you suggested, I have move my code to CreateChildControls. The > > > control renders ok in design time, but fails in runtime at > > > FillNamedControlsTable with the error message "Multiple controls with > > > the same ID 'myControl1' were found. FindControl requires that > > > controls have unique IDs". With the RequiredFieldValidator removed, > > > the control renders but generates the same error with the buttons > > > onClick event. Looking at the source code I notice that the command > > > button has now rendered as: > > > > > > <input type="submit" name="myControl1:cmdTest1" value="Test" > > > id="myControl1_cmdTest1" /> > > > > > > I can't compile my code behind with an event handling > > > myControl1_cmdTest1.Click, because the compiler is expecting > > > cmdTest1.click. > > > > > > My code for CreateChildControls now looks like: > > > > > > protected override void CreateChildControls() { > > > > > > this.Controls.Clear(); > > > this.Controls.Add(new LiteralControl("<b>before</b><br /><hr><br > > > />")); > > > > > > foreach (NestedChild child in _NestedChildren) { > > > //this.Controls.Add(child); > > > > > > this.Controls.Add(new LiteralControl("* " + child.Caption + > > > "<BR>")); > > > > > > // Display the contents of child in a single cell table > > > HtmlTableCell td = new HtmlTableCell(); > > > td.Style.Add("width", "250px"); > > > td.Style.Add("height", "100px"); > > > td.Controls.Add(child); > > > > > > // Add the table cell to a new table row > > > HtmlTableRow tr = new HtmlTableRow(); > > > tr.Controls.Add(td); > > > > > > // Add the table row to a new table > > > HtmlTable table = new HtmlTable(); > > > table.ID = this.UniqueID; > > > table.Width = this.Width.ToString(); > > > table.Height = this.Height.ToString(); > > > table.CellSpacing = 2; > > > table.CellPadding = 2; > > > table.Border = 1; > > > table.Style.Add("background-color", "#C0C0C0"); > > > table.Controls.Add(tr); > > > > > > this.Controls.Add(table); > > > > > > } > > > this.Controls.Add(new LiteralControl("<br /><hr><br > > > /><b>after</b>")); > > > } > > > > > > Any other suggestions? > > > > > > Regards, > > > > > > Stephen > > > > > > > > > "Peter Blum" <PLBlum@Blum.info> wrote in message > news:<O6QJcFt1DHA.2676@tk2msftngp13.phx.gbl>... > > > > 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: PLBlum@PeterBlum.com > > > > "Stephen Miller" <jsausten@hotmail.com> wrote in message > > > > news:cdb404de.0401062350.6818678@posting.google.co m... > > > > > 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" <PLBlum@Blum.info> wrote in message > news:<uR1AAkx0DHA.1684@TK2MSFTNGP12.phx.gbl>... > > > > > > 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: PLBlum@PeterBlum.com > > > > > > > > > > > > "Stephen Miller" <jsausten@hotmail.com> wrote in message > > > > > > news:cdb404de.0401032152.5faee8e3@posting.google.c om... > > > > > > > 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...sp?sec=0&cat=3) > so I > > > > > > > know it must be possible (... and I don't want to pay USD $500) > > > > > > > > > > > > > > Thanks, > > > > > > > > > > > > > > Stephen > > > > > > > > > > > > > > > > > > > > > > > > > > > > "Peter Blum" <PLBlum@Blum.info> wrote in message > news:<O#52x0h0DHA.540@tk2msftngp13.phx.gbl>... > > > > > > > > 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: PLBlum@PeterBlum.com > > > > > > > > > > > > > > > > "Stephen Miller" <jsausten@hotmail.com> wrote in message > > > > > > > > news:cdb404de.0401022120.363a51d4@posting.google.c om... > > > > > > > > > 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.CheckControlValidationP roperty' > > > > > > > > > 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(DesignerSerializat ionVisibility.Content), > > > > > > > > > PersistenceMode(PersistenceMode.InnerDefaultProper ty), > > > > > > > > > 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> </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 > > > > |
Re: Problem overriding render method to format literal content of nested tags in custom control
Now that the naming issue has been resolved by PeterBlum --your second
problem is the events being fired in your nestedchild controls. For any events that fire in your template(nestedchild) you need to bubble them up to the container(nested), you can then either take action in nested if this is the last destination or bubble them futher up to the page, that is the container of nested. to handle or to raise the bubbled event, you must override the OnBubbleEvent method for your control. Now always building up on the same code you posted --the first one that is, you have a button in your template --the clickevent for the button will fire in your template --override the OnBubbleEvent method there and have it fire in the container "nested" --in nested override the OnBUbbleEvent again and send it up to its container(the page where the control sits) You do not need to implement the IPostBackEventHandler for this. There is a very nice example on msdn which goes into all the details. However its very vast so i'm coping out the steps you need to take. For more look at the example to which i'm including the link after the code below : //First bubble events in NestedChildCollection //Note how it looks only for a CommandEventArgs //and passes that information to the //class TemplatedListCommandEventArgs which will in turn delegate //the event. public class NestedChildCollection : CollectionBase { public NestedChild this[int nIndex]{ get { return (NestedChild) base.List[nIndex]; } } public void Add(NestedChild child){ base.List.Add(child); } public int IndexOf(NestedChild child){ return base.List.IndexOf(child); } } protected override bool OnBubbleEvent(object source, EventArgs e) { if (e is CommandEventArgs) { // Add the information about Item to CommandEvent. TemplatedListCommandEventArgs args = new TemplatedListCommandEventArgs(this, source, (CommandEventArgs)e); RaiseBubbleEvent(this, args); return true; } return false; } } //Now the class TemplatedListCommandEventArgs --this is where we passed the captured event, and now we delegate it. public sealed class TemplatedListCommandEventArgs : CommandEventArgs { private TemplatedListItem item; private object commandSource; public TemplatedListCommandEventArgs(TemplatedListItem item, object commandSource, CommandEventArgs originalArgs) : base(originalArgs) { this.item = item; this.commandSource = commandSource; } public TemplatedListItem Item { get { return item; } } public object CommandSource { get { return commandSource; } } } public delegate void TemplatedListCommandEventHandler(object source, TemplatedListCommandEventArgs e); //now in your class(nested) first Override OnBubbleEvent method and //bubble the events to the container(that is the page the control sits) //note how OnItemCommand is called when the TemplatedListCommandEventHandler //is the one that fired --the one that we bubbled //in the Template protected override bool OnBubbleEvent(object source, EventArgs e) { // Handle events raised by children by overriding OnBubbleEvent. bool handled = false; if (e is TemplatedListCommandEventArgs) { TemplatedListCommandEventArgs ce = (TemplatedListCommandEventArgs)e; OnItemCommand(ce); handled = true; } return handled; } //now the OnItemCommand --the one that we called in the above method : //this will raise the bubbled Event to the client with //the handler and all protected virtual void OnItemCommand(TemplatedListCommandEventArgs e) { TemplatedListCommandEventHandler onItemCommandHandler = (TemplatedListCommandEventHandler)Events[EventItemCommand]; if (onItemCommandHandler != null) onItemCommandHandler(this, e); } //expose the handler TemplatedListCommandEventHandler to the client(the page) [ Category("Action"), Description("Raised when a CommandEvent occurs within the Template.") ] public event TemplatedListCommandEventHandler ItemCommand { add { Events.AddHandler(EventItemCommand, value); } remove { Events.RemoveHandler(EventItemCommand, value); } } ------------------------------------------------------Finish---------------- -------------------------------------- now in your page where the control sits you have the event handler use it, all events fired in your templates can be captured here --you now have one unique place to search for events as it is with templated controls, you can have many controls in there that fire events, and you do not want an event handler for every control in there, instead you want ONE handler that will handle all --the trick it is to use the commandName for your buttons and then look for this command name here --that way you can differentiate between buttons and take action accordingly. So on the button that you have used in your template pass use the CommandName property to like say if you set the CommandName="save" then you can check if the save button was the one clicked, see code below : protected void MyList_ItemCreated(object sender, TemplatedListItemEventArgs e) { if (e.CommandName == "save"){ response.write("save button was clicked, lets do something") } } This is the templated databound sample, surely you will learn a lot besides the event bubbling ;P Ok --please look at the sample code on MSDN. If you have the docs then the link is : ms-help://MS.VSCC/MS.MSDNVS/cpguide/html/cpcontemplateddataboundcontrolsampl e.htm http://msdn.microsoft.com/library/de...trolsample.asp Not very hard to follow once you get a hang of it ;) "Peter Blum" <PLBlum@Blum.info> wrote in message news:OmiFYnI3DHA.1804@TK2MSFTNGP12.phx.gbl... > 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: PLBlum@PeterBlum.com > > "Stephen Miller" <jsausten@hotmail.com> wrote in message > news:cdb404de.0401110024.b442c6f@posting.google.co m... > > Peter, > > > > Again, thanks for your on going help. > > > > I have been struggling with this problem for 3+ months and on someone > > else's advice purchased "Developing Microsoft ASP.NET Server Controls > > and Components". Following the book religiously, I started again from > > the "Hello World" example and worked forward to recreate my problem. > > Kothari & Datye's touches on controls "whose nested content does not > > correspond to properties" on pages 332-7, but the example provided is > > very trivial and doesn't address any of the issues I've encountered. > > If I've missed something, please point me to the section. > > > > My control uses the ParseChildrenAttribute attribute with the > > declaration '[ParseChildren(true, "NestedChild")', which defines a > > public property named 'NestedChild', with nested (child) elements > > corresponding to child elements of the 'NestedChild' property. The > > idea with the '_NestedChildren' property is to add each nested child > > element to a collection, which I can iterate through and hide or show > > based on additional conditions. I have extra logic here, which I have > > omitted for clarity. > > > > As you suggested, I have move my code to CreateChildControls. The > > control renders ok in design time, but fails in runtime at > > FillNamedControlsTable with the error message "Multiple controls with > > the same ID 'myControl1' were found. FindControl requires that > > controls have unique IDs". With the RequiredFieldValidator removed, > > the control renders but generates the same error with the buttons > > onClick event. Looking at the source code I notice that the command > > button has now rendered as: > > > > <input type="submit" name="myControl1:cmdTest1" value="Test" > > id="myControl1_cmdTest1" /> > > > > I can't compile my code behind with an event handling > > myControl1_cmdTest1.Click, because the compiler is expecting > > cmdTest1.click. > > > > My code for CreateChildControls now looks like: > > > > protected override void CreateChildControls() { > > > > this.Controls.Clear(); > > this.Controls.Add(new LiteralControl("<b>before</b><br /><hr><br > > />")); > > > > foreach (NestedChild child in _NestedChildren) { > > //this.Controls.Add(child); > > > > this.Controls.Add(new LiteralControl("* " + child.Caption + > > "<BR>")); > > > > // Display the contents of child in a single cell table > > HtmlTableCell td = new HtmlTableCell(); > > td.Style.Add("width", "250px"); > > td.Style.Add("height", "100px"); > > td.Controls.Add(child); > > > > // Add the table cell to a new table row > > HtmlTableRow tr = new HtmlTableRow(); > > tr.Controls.Add(td); > > > > // Add the table row to a new table > > HtmlTable table = new HtmlTable(); > > table.ID = this.UniqueID; > > table.Width = this.Width.ToString(); > > table.Height = this.Height.ToString(); > > table.CellSpacing = 2; > > table.CellPadding = 2; > > table.Border = 1; > > table.Style.Add("background-color", "#C0C0C0"); > > table.Controls.Add(tr); > > > > this.Controls.Add(table); > > > > } > > this.Controls.Add(new LiteralControl("<br /><hr><br > > /><b>after</b>")); > > } > > > > Any other suggestions? > > > > Regards, > > > > Stephen > > > > |
Re: Problem overriding render method to format literal content of nested tags in custom control
Stephen, when I made that last post, I was in a hurry and missed out a few
things. I have given the code another look and here are some corrections. However note that I have not taken the time to test this with your code, but that i have just copied and pasted the essential parts that you need to implement, and I have tried to adapt it to your code. Also note that the example on MSDN if you tried to compile it along with its designer class --you will end up with errors, this is because there are numerous bugs in the code and i had to make many corrections before i was able to run that code myself --however it demonstrates how to code and puts you in the right direction on many techniques like with using templates --databinding --associating a designer to your control, event bubbling etc Here is a much better step by step --the one i did last I missed out a few things like declaring the static event EventItemCommand as object and i removed some un-necessary code --Still did not test --the rest is up to you to get this working ;) //First bubble events in NestedChildCollection //Note how it looks only for a CommandEventArgs //and passes that information to the //class TemplatedListCommandEventArgs which will in turn delegate //the event. public class NestedChildCollection : CollectionBase { public NestedChild this[int nIndex]{ get { return (NestedChild) base.List[nIndex]; } } public void Add(NestedChild child){ base.List.Add(child); } public int IndexOf(NestedChild child){ return base.List.IndexOf(child); } } protected override bool OnBubbleEvent(object source, EventArgs e) { if (e is CommandEventArgs) { // Add the information about Item to CommandEvent. TemplatedListCommandEventArgs args = new TemplatedListCommandEventArgs(this, source, (CommandEventArgs)e); RaiseBubbleEvent(this, args); return true; } return false; } } //Now the class TemplatedListCommandEventArgs --this is where we passed the captured event, and now we delegate it. public sealed class TemplatedListCommandEventArgs : CommandEventArgs { private NestedChildCollection item; private object commandSource; public TemplatedListCommandEventArgs(NestedChildCollectio n item, object commandSource, CommandEventArgs originalArgs) : base(originalArgs) { this.item = item; this.commandSource = commandSource; } public object CommandSource { get { return commandSource; } } } public delegate void TemplatedListCommandEventHandler(object source, TemplatedListCommandEventArgs e); //now in your class(nested) first declare the static event variable EventItemCommand as object //Next Override OnBubbleEvent method and //bubble the events to the container(that is the page the control sits) private static readonly object EventItemCommand = new object(); //note how OnItemCommand is called when the TemplatedListCommandEventHandler //is the one that fired --the one that we bubbled //in the Template protected override bool OnBubbleEvent(object source, EventArgs e) { // Handle events raised by children by overriding OnBubbleEvent. bool handled = false; if (e is TemplatedListCommandEventArgs) { TemplatedListCommandEventArgs ce = (TemplatedListCommandEventArgs)e; OnItemCommand(ce); handled = true; } return handled; } //now the OnItemCommand --the one that we called in the above method : //this will raise the bubbled Event to the client with //the handler and all protected virtual void OnItemCommand(TemplatedListCommandEventArgs e) { TemplatedListCommandEventHandler onItemCommandHandler = (TemplatedListCommandEventHandler)Events[EventItemCommand]; if (onItemCommandHandler != null) onItemCommandHandler(this, e); } //expose the handler TemplatedListCommandEventHandler to the client(the page) [ Category("Action"), Description("Raised when a CommandEvent occurs within the Template.") ] public event TemplatedListCommandEventHandler ItemCommand { add { Events.AddHandler(EventItemCommand, value); } remove { Events.RemoveHandler(EventItemCommand, value); } } ------------------------------------------------------Finish---------------- -------------------------------------- protected void MyList_ItemCreated(object sender, TemplatedListCommandEventArgs e) { if (e.CommandName == "save"){ response.write("save button was clicked, lets do something") } } "Alessandro Zifiglio" <alessandrozifiglio@NO-SPAM-hotmail.com> wrote in message news:l_bOb.4409$nC1.1845@news.edisontel.com... > Now that the naming issue has been resolved by PeterBlum --your second > problem is the events being fired in your nestedchild controls. For any > events that fire in your template(nestedchild) you need to bubble them up to > the container(nested), you can then either take action in nested if this is > the last destination or bubble them futher up to the page, that is the > container of nested. > > to handle or to raise the bubbled event, you must override the OnBubbleEvent > method for your control. > > Now always building up on the same code you posted --the first one that is, > you have a button in your template --the clickevent for the button will fire > in your template --override the OnBubbleEvent method there and have it fire > in the container "nested" --in nested override the OnBUbbleEvent again and > send it up to its container(the page where the control sits) > You do not need to implement the IPostBackEventHandler for this. There is a > very nice example on msdn which goes into all the details. However its very > vast so i'm coping out the steps you need to take. For more look at the > example to which i'm including the link after the code below : > //First bubble events in NestedChildCollection > //Note how it looks only for a CommandEventArgs > //and passes that information to the > //class TemplatedListCommandEventArgs which will in turn delegate > //the event. > public class NestedChildCollection : CollectionBase { > > public NestedChild this[int nIndex]{ > get { return (NestedChild) base.List[nIndex]; } > } > public void Add(NestedChild child){ > base.List.Add(child); > } > > public int IndexOf(NestedChild child){ > return base.List.IndexOf(child); > } > } > > protected override bool OnBubbleEvent(object source, EventArgs e) { > if (e is CommandEventArgs) { > // Add the information about Item to CommandEvent. > > TemplatedListCommandEventArgs args = > new TemplatedListCommandEventArgs(this, source, > (CommandEventArgs)e); > > RaiseBubbleEvent(this, args); > return true; > } > return false; > } > > } > > > //Now the class TemplatedListCommandEventArgs --this is where we passed the > captured event, and now we delegate it. > > public sealed class TemplatedListCommandEventArgs : CommandEventArgs { > > private TemplatedListItem item; > private object commandSource; > > public TemplatedListCommandEventArgs(TemplatedListItem item, object > commandSource, CommandEventArgs originalArgs) : > base(originalArgs) { > this.item = item; > this.commandSource = commandSource; > } > > public TemplatedListItem Item { > get { > return item; > } > } > > public object CommandSource { > get { > return commandSource; > } > } > } > > public delegate void TemplatedListCommandEventHandler(object source, > TemplatedListCommandEventArgs e); > > > //now in your class(nested) first Override OnBubbleEvent method and > //bubble the events to the container(that is the page the control sits) > > > > > //note how OnItemCommand is called when the TemplatedListCommandEventHandler > //is the one that fired --the one that we bubbled > //in the Template > > protected override bool OnBubbleEvent(object source, EventArgs e) { > // Handle events raised by children by overriding OnBubbleEvent. > > bool handled = false; > > if (e is TemplatedListCommandEventArgs) { > TemplatedListCommandEventArgs ce = > (TemplatedListCommandEventArgs)e; > > OnItemCommand(ce); > handled = true; > } > > return handled; > } > > //now the OnItemCommand --the one that we called in the above method : > //this will raise the bubbled Event to the client with > //the handler and all > > protected virtual void OnItemCommand(TemplatedListCommandEventArgs e) { > TemplatedListCommandEventHandler onItemCommandHandler = > (TemplatedListCommandEventHandler)Events[EventItemCommand]; > if (onItemCommandHandler != null) onItemCommandHandler(this, e); > } > > > //expose the handler TemplatedListCommandEventHandler to the client(the > page) > > [ > Category("Action"), > Description("Raised when a CommandEvent occurs within the > Template.") > ] > public event TemplatedListCommandEventHandler ItemCommand { > add { > Events.AddHandler(EventItemCommand, value); > } > remove { > Events.RemoveHandler(EventItemCommand, value); > } > } > > > > ------------------------------------------------------Finish-------------- -- > -------------------------------------- > > now in your page where the control sits you have the event handler use it, > all events fired in your templates can be captured here --you now have one > unique place to search for events as it is with templated controls, you can > have many controls in there that fire events, and you do not want an event > handler for every control in there, instead you want ONE handler that will > handle all --the trick it is to use the commandName for your buttons and > then look for this command name here --that way you can differentiate > between buttons and take action accordingly. So on the button that you have > used in your template pass use the CommandName property to like say if you > set the CommandName="save" then you can check if the save button was the one > clicked, see code below : > > protected void MyList_ItemCreated(object sender, TemplatedListItemEventArgs > e) { > > if (e.CommandName == "save"){ > response.write("save button was clicked, lets do something") > } > > } > > This is the templated databound sample, surely you will learn a lot besides > the event bubbling ;P > Ok --please look at the sample code on MSDN. > > If you have the docs then the link is : > ms-help://MS.VSCC/MS.MSDNVS/cpguide/html/cpcontemplateddataboundcontrolsampl > e.htm > > http://msdn.microsoft.com/library/de...trolsample.asp > > Not very hard to follow once you get a hang of it ;) > > > > > > "Peter Blum" <PLBlum@Blum.info> wrote in message > news:OmiFYnI3DHA.1804@TK2MSFTNGP12.phx.gbl... > > 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: PLBlum@PeterBlum.com > > > > "Stephen Miller" <jsausten@hotmail.com> wrote in message > > news:cdb404de.0401110024.b442c6f@posting.google.co m... > > > Peter, > > > > > > Again, thanks for your on going help. > > > > > > I have been struggling with this problem for 3+ months and on someone > > > else's advice purchased "Developing Microsoft ASP.NET Server Controls > > > and Components". Following the book religiously, I started again from > > > the "Hello World" example and worked forward to recreate my problem. > > > Kothari & Datye's touches on controls "whose nested content does not > > > correspond to properties" on pages 332-7, but the example provided is > > > very trivial and doesn't address any of the issues I've encountered. > > > If I've missed something, please point me to the section. > > > > > > My control uses the ParseChildrenAttribute attribute with the > > > declaration '[ParseChildren(true, "NestedChild")', which defines a > > > public property named 'NestedChild', with nested (child) elements > > > corresponding to child elements of the 'NestedChild' property. The > > > idea with the '_NestedChildren' property is to add each nested child > > > element to a collection, which I can iterate through and hide or show > > > based on additional conditions. I have extra logic here, which I have > > > omitted for clarity. > > > > > > As you suggested, I have move my code to CreateChildControls. The > > > control renders ok in design time, but fails in runtime at > > > FillNamedControlsTable with the error message "Multiple controls with > > > the same ID 'myControl1' were found. FindControl requires that > > > controls have unique IDs". With the RequiredFieldValidator removed, > > > the control renders but generates the same error with the buttons > > > onClick event. Looking at the source code I notice that the command > > > button has now rendered as: > > > > > > <input type="submit" name="myControl1:cmdTest1" value="Test" > > > id="myControl1_cmdTest1" /> > > > > > > I can't compile my code behind with an event handling > > > myControl1_cmdTest1.Click, because the compiler is expecting > > > cmdTest1.click. > > > > > > My code for CreateChildControls now looks like: > > > > > > protected override void CreateChildControls() { > > > > > > this.Controls.Clear(); > > > this.Controls.Add(new LiteralControl("<b>before</b><br /><hr><br > > > />")); > > > > > > foreach (NestedChild child in _NestedChildren) { > > > //this.Controls.Add(child); > > > > > > this.Controls.Add(new LiteralControl("* " + child.Caption + > > > "<BR>")); > > > > > > // Display the contents of child in a single cell table > > > HtmlTableCell td = new HtmlTableCell(); > > > td.Style.Add("width", "250px"); > > > td.Style.Add("height", "100px"); > > > td.Controls.Add(child); > > > > > > // Add the table cell to a new table row > > > HtmlTableRow tr = new HtmlTableRow(); > > > tr.Controls.Add(td); > > > > > > // Add the table row to a new table > > > HtmlTable table = new HtmlTable(); > > > table.ID = this.UniqueID; > > > table.Width = this.Width.ToString(); > > > table.Height = this.Height.ToString(); > > > table.CellSpacing = 2; > > > table.CellPadding = 2; > > > table.Border = 1; > > > table.Style.Add("background-color", "#C0C0C0"); > > > table.Controls.Add(tr); > > > > > > this.Controls.Add(table); > > > > > > } > > > this.Controls.Add(new LiteralControl("<br /><hr><br > > > /><b>after</b>")); > > > } > > > > > > Any other suggestions? > > > > > > Regards, > > > > > > Stephen > > > > > > > > > |
| All times are GMT. The time now is 10:46 PM. |
Powered by vBulletin®. Copyright ©2000 - 2013, vBulletin Solutions, Inc.
SEO by vBSEO ©2010, Crawlability, Inc.