Skip navigation.

Chaining Delegates with Return ValuesAll recent postsPoison 'em While Young

Stripping SPAN from WebControl

Whenever I develop custom server controls, I pretty much always run into weird issues. I don’t know if it’s me, or server controls are tricky.

The past few days I’ve been polishing a composite control of my making, until it came time to wire some client-side script. In a nutshell, I’m building something like this:

[ToolboxData("<{0}:AdvancedList runat=server></{0}:AdvancedList>")]
public class AdvancedList : WebControl, INamingContainer
{
 protected override void CreateChildControls()
 {
  DropDownList users = new DropDownList();
  this.Controls.Add (users);

  users.Items.Add ("John Doe");
  users.Items.Add ("Jane Smith");
 }
}

Nothing too fancy: a composite control which drives a dropdown list a certain way which makes sense to our product. I simply needed to add a snip of JavaScript to process the selection on the client-side. Now, oddly enough, as defined above, the control renders this:

<span id=”UserList”>
 <select name=”UserList:_ctl0”>
  <option value=”John Doe”>John Doe</option>
  <option value=”Jane Smith”>Jane Smith</option>
 </select>
</span>

Where does the outer <span> come from? It so happens that WebControl renders its child controls within a span. Crank up Reflector and peek inside WebControl’s Render method:

protected override void Render(HtmlTextWriter writer)
{
 this.RenderBeginTag(writer);
 this.RenderContents(writer);
 this.RenderEndTag(writer);
}

WebControl’s constructor sets span as the default tag referred to above:

protected WebControl() : this(HtmlTextWriterTag.Span) { }

Quite annoying, I’d say. Also, the dropdown control has no id, which makes it complicated to locate it with plain DOM techniques, such as getElementById(). A quick search through newsgroups revealed a suggestion to overwrite the Render method:

protected override void Render(HtmlTextWriter writer)
{
 RenderContents (writer);
}

Basically, we suppress the outer tag. Not a bad idea, except that the dropdown still has no id. Actually throwing it by hand into the Attributes collection does the job:

protected override void CreateChildControls()
{
 DropDownList users = new DropDownList();
 this.Controls.Add (users);

 users.Items.Add ("John Doe");
 users.Items.Add ("Jane Smith");

 users.Attributes.Add ("id", this.ClientID);
}

This hodge-podge finally gave me what I was after:

<select name=”UserList:_ctl0” id=”UserList”>
 <option value=”John Doe”>John Doe</option>
 <option value=”Jane Smith”>Jane Smith</option>
</select>

Now, I imagine thousands of other people must’ve encountered this predicament before, and doing all this makes me feel dirty. Am I not seeing some painfully simple issue here? Can you share a better way?

Comments

Comment permalink 1 Nikhil Kothari |
Initializing your control like this after adding it to the control tree will cause it to contribute to view state... probably not what you want to have happen...
Comment permalink 2 Rick |
To get rid of the span I just inherit from a different constructor:

public MyControl() : base()
{
}

You can also change the span to something more useful:

public MyControl() : base(HtmlTextwriterTag.Fieldset)
{
}
Comment permalink 3 Joe |
Would it make sense to inherit from DropDownList instead of WebControl?

Something like this:

public class AdvancedList : DropDownList
{
public AdvancedList()
{
this.Items.Add ("John Doe");
this.Items.Add ("Jane Smith");
}
}

Or, if you are adding other controls in there, you could inherit from Control, instead of WebControl, then it should only give you the contents of your control, and won't expose un-implemented properties like style to developers that would do nothing.
Comment permalink 4 Milan Negovan |
Nikhil, actually I do want it to contribute to view state because there's also autopostback involved. This is exactly what Scott Mitchell wrote about.

Rick, I have to extend WebControl to retain all the plumbing with attributes and styles. Besides, having an outer tag assigns it the ID, as I mentioned about, and makes scripting challenging.

Joe, what I'm trying to build is a "strongly typed" picker which would return Guid and integer IDs as opposed to [something].SelectedValue or [something].SelectedItem.Text. It also has other helper methods. Containing a dropdown allows me to get rid of everything irrelevant---all the various properties, events, etc,---and to expose only a subset of what a developer is supposed to see.
Comment permalink 5 mal |
How do I add an all values item to my databounded dropdown list?
Thank you
Mal
Comment permalink 6 Milan Negovan |
Don't quite follow your question, Mal. In my case I added a DataBind override and simply bound a Dataset/Dataview to the picker as usual.
Comment permalink 7 Chris Linderman |
You can also do this:

VB:
Protected Overrides ReadOnly Property TagKey() As System.Web.UI.HtmlTextWriterTag
Get
Return HtmlTextWriterTag.Select
End Get
End Property
Comment permalink 8 Jeff Dean |
I put most of my control creation logic into the CreatChildControls, instead of Render. Then my render method looks like this:

protected override void Render(HtmlTextWriter writer)
{
RenderContents(writer);
}

It renders the contents, and omits the tag. It might not work for every situation, but it's a little cleaner than overriding the RenderBeginTag and RenderEndTag.
Comment permalink 9 Michelle |
i've been a lurker (and frequent visitor) for quite some time. your articles and blog entries have been immensely helpful (including this one). just want to say thanks!
Comment permalink 10 Milan Negovan |
Thank you, Michelle. Awesome blog, btw. ;)
Comment permalink 11 Terence Tan |
The span tag is there to hold all the styles which the developer can set in Visual Studio .NET. You know, by clicking on the control, going to the Properties window, then setting the background colour, font colour, etc. etc.

If you don't want the span tags (and it sounds like you don't have a use for them, since you don't want to style your control) then you should inherit from System.Web.UI.Control instead. It leaves out all that stuff.

If you want to be able to style the control, but want to use a different tag (say it's a block-level element, so you want div instead), you can use the techniques in comments #2 or #7 to override the HtmlTextWriterTag.
Comment permalink 12 Cliff |
Terrence,

You wrote:

"If you don't want the span tags (and it sounds like you don't have a use for them, since you don't want to style your control) then you should inherit from System.Web.UI.Control instead. It leaves out all that stuff."
This is exactly what I want to do but I don't understand how to "inherit from System.Web.UI.Control instead". Can you help please?
Thanks,
Cliff
Comment permalink 13 Milan Negovan |
Cliff, it'll look like this:

using System.Web.UI;

public class YourControl : Control
{
/* Your rendering logic goes here */
}
Comment permalink 14 Web Server India |
I am new to ASP.NET Development, your guidelines about the Spanning SPAN from Web Controls is good for people like me who is looking for the same. Especially web controls rendering method is nice.
Thanks
Andy
Comment permalink 15 Robert |
I've been searching for most of the afternoon looking for a solution to how the WebControl Render method works. From my understanding the Page(or parent control) calls the rendercontrol method for the specific control. The RenderControl() method contains code that calls the Render() method for the control. The Render() method looks like what Milan wrote above:

Render( writer )
{
RenderBeginTag( writer );
RenderContents( writer );
RenderEndTag( writer );
}

Does anyone know what happens exactly in those methods? I'm writing some custom Validation controls that inherit from the BaseValidator class (which inherits from WebControl ). My problem is I'm trying to completely overwrite the render method and still get the functionality of the BaseValidator i.e. the client and server side validation callbacks. So far I'm not having much luck. One other thing that makes this so complicated is that I don't want to add the validation control in the .aspx page, but rather the .aspx.cs code-behind page. The only reason I'm doing this is because I want to create the validation controls on the fly and not be locked-in to a specific structure for validating my form. Any help of any kind would be greatly appreciated. Thanks.
Comment permalink 16 Heath A |
Great Post!!!

Overridding the Render method is just what I needed to get rid of the
span tag that is automatically added to a web custom control.

Thanks All!
Comment permalink 17 Chris Amelinckx |
Another option if you don't need CompositeControl or WebControl's extra properties (CssClass, etc).

public class MyControl : HtmlGenericControl
{
public ContentOptionsList() : base("Div")
{
}
protected override void CreateChildControls(){...}
}

This way you tell it which outer tag you want, DIV in the example.
Comment permalink 18 Mike Butler |
You're awesome! I hate the ASP.Net default . I'm so happy to see it gone!
Comment permalink 19 Dan |
this is about to drive me mad...can anyone see what i am doing wrong. i want to remove the dang SPAN tag around the text for my custom label control. the commented code is what I have already tried and still the dang SPAN tag is rendered.

[DefaultProperty("Text")]
[ToolboxData("<{0}:Links runat=server>")]
public class Links : WebPart
{
private List _links = new List();

private bool _includeCheckBox = false;

[Bindable(true)]
[Category("Appearance")]
[DefaultValue("")]
[Localizable(true)]
public string Text
{
get
{
String s = (String)ViewState["Text"];
return ((s == null) ? String.Empty : s);
}

set
{
ViewState["Text"] = value;
}
}

protected override void CreateChildControls()
{

if (_links.Count > 0)
{

foreach (Link link in _links)
{

this.Controls.Add(GenerateLink(link.Url, link.Title));

if (link.Description != String.Empty)
{

this.Controls.Add(GenerateLabel(link.Description));

}
}
}
}

public List LinkCollection
{
get
{
return _links;
}
set
{
_links = value;
}

}

private HyperLink GenerateLink(string url, string title)
{
HyperLink link = new HyperLink();
link.NavigateUrl = url;
link.Text = title;
return link;
}

private Label GenerateLabel(string description)
{
Label urlLabel = new Label();
urlLabel.Text = description;

return urlLabel;
}

protected override HtmlTextWriterTag TagKey
{

get
{

return HtmlTextWriterTag.Div;

}

}


//protected override void RenderContents(HtmlTextWriter writer)
//{

// writer.RenderBeginTag(HtmlTextWriterTag.Div);

// writer.AddAttribute(HtmlTextWriterAttribute.Id, "urlDescription");

// foreach (Control childControl in Controls)
// {

// childControl.RenderControl(writer);

// }

// writer.RenderEndTag();

//}

//protected override HtmlTextWriterTag TagKey
//{
// get
// {
// return HtmlTextWriterTag.Div;
// }
//}

//protected override void Render(HtmlTextWriter writer)
//{
// RenderContents(writer);
//}

}
Comment permalink 20 Chris |
This was exactly what I wanted. Everything default in ASP.NET is breaking standards and conventions!
I can now make standard xHTML! Thanks a lot.
Comment permalink 21 neekgreen |
Good job!
Comment permalink 22 Rob |
Thanks very much for posting this, took me too long to find an simple, concise explanation such as yours.

Thanks again! :)

Emails and Notifications

Would you like to be notified when somebody responds to this post?  Would you like to have these comments emailed to you?

Submit your comment

Please enter only text since all HTML tags except hyperlinks will be stripped. Hyperlinks will become live links. Any comments with flaming or offensive language will be deleted. Be courteous to other posters. Thank you.

Your name (required):
Your email (optional):
Your site's URL (optional):
Enter this number
Type in the number above:
Comment (required):