Controlling HTML in ASP.NET WebForms

Listen to the Show

Listen to the Show!

Resources Mentioned in the Show


kick it on DotNetKicks.com

Developers want control. Developing for the web is seemingly more of a love/hate relationship with the tools at our disposal. Sometimes our tools and frameworks give us power, but not control. While we love the functionality and ease-of-use of many of the stock server controls found in ASP.NET WebForms, sometimes we find that the underlying HTML isn’t exactly what we need.

To address the growing need for more control over web page markup we’ve seen movements like the CSS Control Adapters and the emergence of ASP.NET MVC framework which give us 100% control over markup.

MVC and control adapters are not the only answer to gaining more control. For the sea of standard WebForm applications there are many techniques available for gaining more control.

Tame ViewState

One of the first places to exercise control over your HTML is to reign in ViewState. ViewState was once hailed as a huge time saver upon its introduction and now often the bedrock feature of ASP.NET is maligned for all manner of code bloat.

The fact is, though, ViewState is not inherently evil and is effective when used appropriately. The problem with ASP.NET is not that it features ViewState, but that the framework is cavalier in its use of ViewState. The problem is ViewState is turned on by default.

Consider a page that includes a data-bound ListView control using the default ASP.NET template. The page template and all controls automatically have ViewState enabled.

The following is a screen shot of the HTML generated by the ListView:

ViewSource created from ListView server control

Notice that the control simply renders a table. There is no editing behavior implemented on this page therefore there is no need for ViewState. Disabling ViewState will change the source to look like this:

Reduced markup from disabling ViewState

Note: Surprisingly, setting EnableViewState="false" does not get rid of ViewState entirely. If you have a HTML form set to run-at server, then you will always have at least one hidden control in your markup with some "ViewState" data. This data, though, is trivial in size.

Tip: Turn off ViewState by default and use it only when necessary. You can even turn off ViewState globally in your application by disabling it in the web.config in the pages element.

Disable ViewState in the web.config

If you find ViewState is unavoidable in your application you may consider looking into the options of moving it to the web server or even down to the database.

Question the Form Tag

Another foundational control in ASP.NET is the run-at server form tag – but do you really need it?

As you just saw when working with ViewState, as long as a server-controlled form element is on your page, extra HTML in the form of ViewState and supporting JavaScript for post backs is emitted to the page. The default ASP.NET page template includes a form tag on every page, but often its presence is not necessary.

Returning to the example from the previous section, look at the HTML generated from placing a read-only ListView in a server form with ViewState disabled:

Markup with unneeded form element

This page has no input controls and is simply a read-only page so having a form on the page doesn’t really make sense.

The next screen shot is how the page looks with the form tag removed:

Markup after removing unneeded form element

Tip: Remove server-controlled forms from your page if they are not necessary.

You may even consider removing them from your page first and wait for the design to dictate whether or not you need a form on the page. Perhaps you are even so bold as to edit the Visual Studio template to remove the form tag from new pages?

The template is found under this location:

C:\Program Files\Microsoft Visual Studio 9.0\Web\WebNewFileItems\CSharp\WebForm.aspx

or

C:\Program Files\Microsoft Visual Studio 9.0\Web\WebNewFileItems\VB\WebForm.aspx

NOTE: If you edit this file you are tinkering with Visual Studio’s default templates, which under normal circumstances is probably a bad idea. The change suggested here, however is pretty benign. In any event make backups - be certain of what you are doing, and if something goes wrong don’t blame me :)

No-Markup Controls

Server controls are valuable because they create a layer of abstraction above HTML elements. This abstraction determines how controls render to different browser versions and save developers time from having to write code by hand. While these benefits are realized in many situations, sometimes server controls are just more than you need.

I have often seen developers use inappropriate controls just to get some text on the screen or to control the visibility of controls.

The Literal Control

Do you need to render a simple string to the page? Then don’t use a Label control unless you have plans for the extra SPAN tags that come with the data – instead try using a Literal control.

The Literal control’s purpose is to render a string literal to the page – and only the string literal.

Take a look at the next two lines of ASPX code:

Literal vs Label control

The controls are effectively doing the same thing: rendering a name to the page. The resulting HTML from each of these controls is quite different:

Markup generated by a literal and label control

The Label is rendered with wrapping SPAN tags. In this case, the control is on a page associated to a master page so the HTML bloats even further with the unique client ID generated by the server. Often when using a Label control developers do not use the accompanying HTML that’s rendered with the data.

Using the Literal control, however simply renders the text to the screen as desired.

Tip: Be aware of when you simply need text on the page and use the appropriate controls.

Note: Take care to not confuse the Literal control with a different control name LiteralControl.The LiteralControl exists to aid server control construction and is rarely used directly in code.

The Placeholder Control

Do you need to programmatically show or hide a group of controls? Don’t just make that DIV runat="server" or turn to a Panel control – instead try wrapping your controls in a Placeholder control.

The Placeholder control is a very useful control that doesn’t seem to get a lot of attention. What makes the Placeholder useful is that the control may contain children controls and it renders no HTML to the page.

Often Placeholders are used for template designs and other scenarios where you don’t know which controls are needed until runtime. There are a number of other applications for the Placeholder beyond its normal use. You can also use this control to help control what is shown and hidden on your page.

Consider the typical scenario where a message is initially hidden from the user, but is later displayed on the screen. A common approach to this problem is to create a server-controlled DIV on the page and set the Visiblity property to false:

DIV element set to run-at server

For simple pages the markup that is rendered to the page is exactly what you would expect, but in more practical settings the HTML gets altered.

The next screenshot shows you how that DIV is rendered to the page when the markup lives inside a master page:

Resulting markup from server DIV element

The age-old problem of ASP.NET changing a control’s ID creeps up. This is a problem because once you start interacting with style sheets and JavaScript the client IDs become very important. While there are a number of ways around this issue for both CSS and scripting, why not dodge the problem altogether?

Instead of making the DIV a server control, wrap your content inside a Placeholder and then you have the same programmatic control over the block of controls without having to worry about the HTML being affected.

You could also use the MultiView control to accomplish this same behavior without generating any extra markup.

Tip: Use controls that do not generate HTML wherever appropriate.

Expressions Have No Ego

If you truly don’t need extra markup created for you – consider skipping the use of a server control altogether and use in-line expressions. ASP.NET MVC makes heavy use of expressions and while some think heavy use can create messy co-mingled code – there is a good pragmatic reason for using them.

If you use a server control, you have to have some strategy for providing the control with data. Getting data into the control requires you to write code in the codebehind. Writing code is not a bad practice at all – it can just be time-consuming. Depending on where the control resides on the page you will often have to do most if not all of these steps:

  • Declaring a control in the ASPX
  • Finding the appropriate event method in the page lifecycle to tap into
  • Locating and cast the control to the appropriate type (in some cases)
  • Provide the control with data

If you opt for using expressions instead you can simply inject the data exactly where it need to go.

So instead of doing this:

Try doing this:

In the end you’ll find that not using server controls when they are truly unnecessary will help tame your rendered markup significantly.

Choose Controls Wisely

One of the best ways to control the HTML that your pages generate is to be familiar with what common controls with render. The following tables contain an abbreviated list of ASP.NET server controls and a summary of the type of markup they emit.

Standard Controls

Control Name Renders
Label SPAN elements surrounding Text property value
TextBox HTML text input element
Button HTML button input element
LinkButton Anchor element
ImageButton Anchor and image (IMG) elements
HyperLink Anchor element
DropDownList Select element with options for values
ListBox Select element with option tags for values. (The ListBox renders a value for the SIZE attribute allowing more than one option to appear at a time.)
CheckBox HTML checkbox input element
CheckBoxList If RepeatLayout is set to Table, then the control renders standard table markup with the checkbox input elements. If RepeatLayout is set to Flow, then the control renders a series of Label and BR elements with the checkbox input elements.
RadioButton HTML radio input element
RadioButtonList If RepeatLayout is set to Table, then the control renders standard table markup with the radio input elements. If RepeatLayout is set to Flow, then the control renders a series of Label and BR elements with the radio input elements.
Image IMG element
ImageMap IMG element and MAP element
Table HTML table
BulletedList Series of LI elements inside a UL element
HiddenField HTML hidden input element
Literal N/A
Calendar Complex series of elements including links and table markup
AdRotator Anchor and image elements
Wizard Complex series of elements including links and table markup
MultiView N/A
Panel DIV or Table element depending on the browser capabilities
Placeholder N/A

Data Controls

Control Name Renders
GridView Table element with optional anchors
DataList If RepeatLayout is set to Table, then the control renders standard table markup If RepeatLayout is set to Flow, then the control renders a series of Label and BR elements
DetailsView Series of elements including links and table markup
FormView HTML table
Repeater N/A
ListView N/A

ASP.NET SEO

Much of the talk surrounding taming HTML is often in the context of building website to be search-engine friendly. In some arenas ASP.NET is viewed as a sub-standard platform for creating search engine optimized websites. While this charge is categorically false, sometimes it’s not hard to see why some reach such an extreme conclusion.

ASP.NET is no better or worse platform for building sites optimized for search engine placement than a Java or Rails site – the problem is often the UI developer. While Microsoft and a host of third-party organizations work hard building tools and controls that aid in RAD development, using these techniques in their default states, may not always be the best approach for building public facing websites.

The bottom line is know the HTML you are generating and use the right tool for the right job.

kick it on DotNetKicks.com

17 Comments

  • Very nice post! I personally knew all this, but I'm aware that most don't, so any push for these "secrets" to come out is welcomed.

    P.S. no need for "this", it's implied, if you want to get a hold of the options you can type just press Ctrl+Space.

  • Thanks Peter!

    I like using "this" though... I know it is necessary but I can type it quicker than CTRL+Space and it distinguishes scope in a glance...

    Craig

  • Great post...one of our themes for ASP.NET 4.0 is to make a lot of this stuff easier to achieve. We're already announced being able to control the ids but we're also looking at the framework as a whole to trim the extraneous markup...

  • Always using controls can definitely be excessive...

    Thanks for the good read.

  • I'm relatively new to programming and learned quite a bit from this post. I was wondering if you wouldn't mind providing further guidance on when it makes sense to not have a form on the page?

    Right now, I can see that I shouldn't use a form when there are no input controls--when the page is "read only." Presumably this means that for a simple blog, etc., we shouldn't be using a form on the home page.

    Is there anything else I should consider when deciding whether to use a form?

  • Tim:

    You hit the nail on the head. In strict HTML terms you only use a form when you are sending data gathered by input controls back to the server.

    ASP.NET changes this a little bit as you might use a LinkButton to perhaps change what is displayed on the page (but not process any input) and will still need a form for the LinkButton to operate properly.

    Again, if you are unsure you can remove the form and if you try to use a server control that requires a form the runtime will raise an error to let you know.

    HTH,

    Craig

  • Regarding the label tag, if you use the the AssociatedControlID property a label element, with the for attribute appropiately populated, is actually emitted intead of a span element. This is handy where usability is concerned For example, if you set the AssociatedControlID equal to the control ID of a TextBox control and you click on the label then the TextBox is focused.

  • David:

    Ah... I had forgotten about that feature. Thanks!

    Craig

  • This is cool. I tried adding controls to a page w/o a server-side form and would get runtime error messages like:

    Control 'ctl00_ContentPlaceHolder1_Grid' of type 'GridView' must be placed inside a form tag with runat=server

    So I gave up. I wish I had persisted, b/c I just tried the same logic using a DataList instead of a GridView and it worked. That makes sense I guess, b/c a GridView relies on postbacks for sorting/paging/editing, etc.

    Pretty useful for pages that only display data - or even if you want to use plain old HTML client-side forms (like to do a GET).

    What's even better is that you can remove the form runat="server" from your master page, and then add it only to the pages that need it (an alternative to modifying the VS templates). I just confirmed this works with my above page that binds and displays table data using DataList w/o a form.

    Thanks Craig!

  • Great post! I always thought that using inline expressions was bad but that changed when I began to work with ASP.NET MVC. Now I started to appreciate plain old HTML and try to avoid the use server controls.

  • Thanks for sharing!

  • I like the paper snippets.... thanks a lot :-)

  • Thanks for the post. I particularly liked all of the advice about how to make a section of code visible/invisible at runtime without the extra markup. It also reminded me to use a literal control in the project I am working on instead of a label.

  • Thanks for this clear article. No misunderstanding:) You can be a teacher...

  • Nice learning how to control things with HTML and ASP. Thanks.

  • Agree with Botticelli!

  • Agree with the earlier comments. Webforms controlling is difficult.

Comments have been disabled for this content.