Add a CSS Class in ASP.NET

kick it on DotNetKicks.com

Adding or removing a CSS class to an element in jQuery is easy enough:

$("#elementId").addClass("className");
$("#elementId").removeClass("className");

But how can this be done in ASP.NET? Shouldn't it be as easy? Should? Yes. Is? Almost. For the quick solution and code, jump to the end of this post. (That's what this is really here for anyway—to host the code for future reference.) For the gratuitously verbose explanation, continue.

ASP.NET Control Types

The value of an HTML tag's "class" attribute can contain one or more class names, separated by a space. For example:

<div id="myDiv" class="foo bar" />

In ASP.NET, if an HTML tag has a "runat" attribute with "server" as its value, ASP.NET types it as an object derived from System.Web.UI.HtmlControl:

<!-- (myDiv is System.Web.UI.HtmlControl) == true -->
<div runat="server" id="myDiv" class="foo bar" />

When an object is an HtmlControl, the "class" attribute's value is the control's Attribute["class"] value:

string classAttributeValue = this.myDiv.Attribute["class"];
//classAttributeValue == "foo bar"

If the tag is an <asp:... /> tag, ASP.NET types it as a System.Web.UI.WebControl:

<!-- (myTextBox is WebControl) == true -->
<asp:TextBox runat="server" id="myTextBox" class="foo bar" />

When an object is a WebControl, the tag's "class" attribute's value is the object's CssClass member:

string classAttributeValue = this.myTextBox.CssClass;
// classAttributeValue == "foo bar"

Adding/Removing a CSS Class

The naive approach to add a class to a control, which I saw, would be:

void CssAddClass(WebControl ctl, string className) 
{
	ctl.CssClass += " " + className;
}
 
void CssAddClass(HtmlControl ctl, string className)
{
	ctl.Attributes["class"] += " " + className;
}

Removing the classes would then be accomplished using similar methods:

void CssRemoveClass(WebControl ctl, string className) 
{
	ctl.CssClass = ctl.CssClass.Replace(" " + className, string.Empty);
}
 
void CssRemoveClass(HtmlControl ctl, string className)
{
	ctl.Attributes["class"] 
		= ctl.Attributes["class"].Replace(" " + className, string.Empty);
}

Nice and simple—and fallible. If the control doesn't have any "class" attribute defined, the control's member for the class value is null:

<!-- Given these tags... -->
<div id="myDiv" runat="server" />
<asp:TextBox id="myTextBox" runat="server" />
// ...and this codebehind, 
this.myDiv.Attributes["class"] += " " + className;
this.myTextBox.CssClass += " " + className;
// ...you get a NullReferenceException from both

Null reference checking is OOP 101. If a class member can return a reference type, it can return null. And if it doesn't return collections (an array, IEnumerable), it should return null if there's nothing to return (as opposed to an empty collection). That's what null means—there's nothing there. string.IsNullOrEmpty() cries every time you don't use it in a scenario like this.

Or, what if the CSS class is already defined in the attribute? By using += " " + className, the class name would be defined twice. Not a big deal, right? What if the web page was server hundreds of thousands of times a day? Still probably not a big deal, but over tiem, bytes add up, and so do bandwidth costs.

Program defensively. The class attribute should be identified as having a value or not before trying to change it, and checked for the pre-existence of the class to add.

Extension Goodies

I've implemented the add/remove functionality as extension methods, and they use LINQ, so you'll need .NET 3.5. The extension methods are on ASP.NET Control objects, but only work with WebControls and HtmlControls.

A quick superfluous example follows. Given an ASPX with this:

<div id="ViewContainer" runat="server" />
<asp:Label id="UserName" runat="server" />

Here's the Page's codebehind to add a couple classes:

this.ViewContainer.CssAddClass("container");
this.UserName.CssAddClass("hilight");

To remove a class, use CssRemoveClass(). Be careful though: if it isn't defined, an ArgumentException is thrown. If you're not sure if that class is defined or not and don't want to deal with the exception, pass true as the last, optional parameter:

// Sure ViewContainer has class="container"
this.ViewContainer.CssRemoveClass("container");
 
// Not sure UserName has class="error"
this.UserName.CssRemoveClass("error", true);

The methods begin with "Css" instead of "Add" or "Remove" so they're quicker to find in Intellisense. You may notice the redundancy with type-checking and getting/setting the CSS class name attribute, but I don't think it warrants augmenting the code.

Remember to include the usings if you just take the methods.

using System;
using System.Linq;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
 
/// <summary>
/// Extension methods for System.Web.UI.Control.
/// </summary>
public static class ControlExtension
{
    /// <summary>
    /// Add a CSS class to a WebControl or HtmlControl.
    /// </summary>
    /// <param name="control">
    /// The WebControl or HtmlControl to add the CSS class to.
    /// </param>
    /// <param name="className">
    /// The name of the CSS class to add.
    /// </param>
    /// <exception cref="System.ArgumentNullException">
    /// control is null or className is null.
    /// </exception>
    /// <exception cref="System.ArgumentException">
    /// The control is not a WebControl or HtmlControl.
    /// </exception>
    public static void CssAddClass(this Control control, string className)
    {
        // by Chris O'Brien, prettycode.org
 
        if (control == null)
        {
            throw new ArgumentNullException("control");
        }
 
        if (className == null)
        {
            throw new ArgumentNullException("className");
        }
 
        string classAttributeValue;
 
        if (control is WebControl)
        {
            classAttributeValue = (control as WebControl).CssClass;
        }
        else if (control is HtmlControl)
        {
            classAttributeValue = (control as HtmlControl).Attributes["class"];
        }
        else
        {
            throw new ArgumentException
                ("Must be WebControl or HtmlControl.", "control");
        }
 
        if (string.IsNullOrEmpty(classAttributeValue))
        {
            classAttributeValue = className;
        }
        else
        {
            var classNameList = classAttributeValue.Split
                (new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
 
            if (classNameList.Contains(className))
            {
                return;
            }
 
            classAttributeValue = string.Concat
                    (classNameList.Select(name => name + " ").ToArray()) + className;
        }
 
        if (control is WebControl)
        {
            (control as WebControl).CssClass = classAttributeValue;
        }
        else if (control is HtmlControl)
        {
            (control as HtmlControl).Attributes["class"] = classAttributeValue;
        }
    }
 
    /// <summary>
    /// Remove a CSS class from a WebControl or HtmlControl.
    /// </summary>
    /// <param name="control">
    /// The WebControl or HtmlControl to remove the CSS class from.
    /// </param>
    /// <param name="className">
    /// The name of the CSS class to remove.
    /// </param>
    public static void CssRemoveClass(this Control control, string className)
    {
        control.CssRemoveClass(className, false);
    }
 
    /// <summary>
    /// Remove a CSS class from a WebControl or HtmlControl.
    /// </summary>
    /// <param name="control">
    /// The WebControl or HtmlControl to remove the CSS class from.
    /// </param>
    /// <param name="className">
    /// The name of the CSS class to remove.
    /// </param>
    /// <param name="mayNotExist">
    /// Specify true to not throw an ArgumentException if the control doesn't
    /// have the CSS class to remove.
    /// </param>
    /// <exception cref="System.ArgumentNullException">
    /// control is null or className is null.
    /// </exception>
    /// <exception cref="System.ArgumentException">
    /// The control is not a WebControl or HtmlControl, or when mayNotExist is false, 
    /// the CSS class cannot be removed because the control does not define it.
    /// </exception>
    public static void CssRemoveClass
        (this Control control, string className, bool mayNotExist)
    {
        // by Chris O'Brien, prettycode.org
 
        if (control == null)
        {
            throw new ArgumentNullException("control");
        }
 
        if (className == null)
        {
            throw new ArgumentNullException("className");
        }
 
        string classAttributeValue;
 
        if (control is WebControl)
        {
            classAttributeValue = (control as WebControl).CssClass;
        }
        else if (control is HtmlControl)
        {
            classAttributeValue = (control as HtmlControl).Attributes["class"];
        }
        else
        {
            throw new ArgumentException
                ("Must be WebControl or HtmlControl.", "control");
        }
 
        bool isNullOrEmpty = string.IsNullOrEmpty(classAttributeValue);
 
        if (!mayNotExist && isNullOrEmpty)
        {
            throw new ArgumentException
                ("Does not have any CSS classes defined.", className);
        }
        else if (isNullOrEmpty)
        {
            classAttributeValue = string.Empty;
        }
 
 
        var classNames =
            from name in classAttributeValue.Split
                (new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)
            where name != className
            select name + " ";
 
        classAttributeValue = string.Concat(classNames.ToArray()).TrimEnd();
 
        if (control is WebControl)
        {
            (control as WebControl).CssClass = classAttributeValue;
        }
        else if (control is HtmlControl)
        {
            (control as HtmlControl).Attributes["class"] = classAttributeValue;
        }
    }
}

kick it on DotNetKicks.com

Tags: ,

Leave a Reply

To include code, use <pre lang="csharp">// code...</pre>, where "csharp" is any language (e.g. "javascript", "html", "xml", etc).