6

It's easy to set CssClass in the code-behind, but this runs the risk of overwriting existing classes.

I need to set certain elements to ReadOnly = true; and I'd like to apply a style as a visual cue that the item cannot be altered...easy enough:

.CssClass += " ReadOnlyStyle";

But at times I will also need to change the same element to ReadOnly = false; which means that I will need to remove the CSS class that I set without removing any other styles that I might have assigned.

What's the best way to do this?

7 Answers 7

14

I've taken AnthonyWJones original code and amended it so that it works no matter what scenario:

static class WebControlsExtensions
    {
        public static void AddCssClass(this WebControl control, string cssClass)
        {
            List<string> classes = control.CssClass.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).ToList();

            classes.Add(cssClass);

            control.CssClass = classes.ToDelimitedString(" ");
        }

        public static void RemoveCssClass(this WebControl control, string cssClass)
        {
            List<string> classes = control.CssClass.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).ToList();

            classes.Remove(cssClass);

            control.CssClass = classes.ToDelimitedString(" ");
        }
    }

    static class StringExtensions
    {
        public static string ToDelimitedString(this IEnumerable<string> list, string delimiter)
        {
            StringBuilder sb = new StringBuilder();
            foreach (string item in list)
            {
                if (sb.Length > 0)
                    sb.Append(delimiter);

                sb.Append(item);
            }

            return sb.ToString();
        }
    }
Sign up to request clarification or add additional context in comments.

4 Comments

By extracting a method from the split/manipulate/join code and creating a second extension method for this HtmlControl control using control.Attributes("class") instead of control.CssClass, you can extend this ability to generic HTML controls that have runat="server" added to them as well.
This will add a duplicate class to the list if AddCssClass() is called on a string that already contains that class.
Looks like you just want string.Join instead of defining ToDelimitedString!
To all answers, HTML living standard says class attribute is a set of space-separated tokens, where the space characters are U+0020 SPACE, U+0009 CHARACTER TABULATION (tab), U+000A LINE FEED (LF), U+000C FORM FEED (FF), and U+000D CARRIAGE RETURN (CR).
8

In C# 3 you can add some extension methods.

 static class WebControlsExtensions
 {
     public static void AddCssClass (this WebControl control, string cssClass)
     {
         control.CssClass += " " + cssClass;
     }
     public static void RemoveCssClass (this WebControl control, string cssClass)
     {
         control.CssClass = control.CssClass.replace(" " + cssClass, "");
     }
 }

Usage:-

ctl.AddCssClass("ReadOnly");
ctl.RemoveCssClass("ReadOnly");

Note the RemoveCssClass is designed to remove only those classes added by AddCssClass and has the limitation that where 2 additional class names is added the shortest name should not match exactly the start of the longest name. E.g., If you added "test" and "test2" you can't remove test without corrupting the CssClass. This could be improved with RegEx by I expect the above to be adequate for your needs.

Note if you don't have C#3 then remove the this keyword from the first parameter and use the static methods in the conventional manner.

4 Comments

The RemoveCssClass will not work if you are removing the original CSS style.
@John: Quite so and I did allude to that "RemoveCssClass is designed to remove only those classes added by AddCssClass".
Fair play I did skim over the rest of your text.
Why should I use this methods with the only line of code instead of using their content? What is benefit? Also your methods have close limits of using (as you noted).
3

Related... if you just want to toggle a Class based upon a condition...

bool disable = true;      // this will vary (true/false) based on UI state

string newClass = disable ? "BtnGray" : "BtnPink";

string currentClass = disable ? "BtnPink" : "BtnGray";

myButton.CssClass = myButton.CssClass.Replace( currentClass, newClass );

Comments

1

This version checks to make sure the given class isn't already added before adding it.

public static void CssAddClass(this WebControl control, string className)
{
    var classNames = control.CssClass.Split
        (new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

    if (classNames.Contains(className))
    {
        return;
    }

    control.CssClass = string.Concat
        (classNames.Select(name => name + " ").ToArray()) + className;
}

public static void CssRemoveClass(this WebControl control, string className)
{
    var classNames = from name in control.CssClass.
                         Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries)
                     where name != className
                     select name + " ";


    control.CssClass = string.Concat(classNames.ToArray()).TrimEnd();
}

Comments

1

I made a version for pre-C#3:

        public static class WebControlsExtensions
        {
            public static void AddCssClass(WebControl control, string cssClass)
            {
                string[] cssClasses = control.CssClass.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                List<string> classes = new List<string>(cssClasses);

                if (!classes.Contains(cssClass)) {
                    classes.Add(cssClass);
                }

                control.CssClass = StringExtensions.ToDelimitedString(classes, " ");
            }

            public static void RemoveCssClass(WebControl control, string cssClass)
            {
                string[] cssClasses = control.CssClass.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                List<string> classes = new List<string>(cssClasses);

                bool removed = true;
                while (removed) {
                    removed = classes.Remove(cssClass);
                }

                control.CssClass = StringExtensions.ToDelimitedString(classes, " ");
            }
    }
    static class StringExtensions {
        public static string ToDelimitedString(List<string> list, string delimiter)
        {
            StringBuilder sb = new StringBuilder();
            foreach (string item in list) {
                if (sb.Length > 0)
                    sb.Append(delimiter);

                sb.Append(item);
            }

            return sb.ToString();
        }
    }

Used like:

WebControlsExtensions.AddCssClass(ctl, "classname");
WebControlsExtensions.RemoveCssClass(ctl, "classname");

This one will only add a class if it's not already there. It will also remove all instances of a class (if, for some reason, there are multiple in there)

Comments

1

Pure .NET 2.0 (No extensions! No LINQ! No RegEx! No unnecessary WebControl class!). These methods are quite general to be used not for CSS classes only.

public static string AddCssClass(string classContainer, string className)
    {
        if (string.IsNullOrEmpty(classContainer)) return className ?? string.Empty;
        if (string.IsNullOrEmpty(className)) return classContainer;

        var classNames = classContainer.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
        if (Array.Exists(classNames, delegate(string s) { return s.Equals(className); })) return classContainer;

        return classContainer + " " + className;
    }

    public static string RemoveCssClass(string classContainer, string className)
    {
        if (string.IsNullOrEmpty(classContainer)) return className ?? string.Empty;
        if (string.IsNullOrEmpty(className)) return classContainer;

        var classNames = classContainer.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
        int index = Array.FindIndex(classNames, delegate(string s) { return s.Equals(className); });
        if (index >= 0)
        {
            return string.Join(" ", classNames, 0, index) +
                (   index + 1 < classNames.Length ?
                    " " + string.Join(" ", classNames, index + 1, classNames.Length - index - 1)
                    :
                    string.Empty    );
        }

        return classContainer;
    }

    public static string ToggleCssClass(string classContainer, string className)
    {
        if (string.IsNullOrEmpty(classContainer)) return className ?? string.Empty;
        if (string.IsNullOrEmpty(className)) return classContainer;

        var classNames = classContainer.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

        if (Array.Exists(classNames, delegate(string s) { return s.Equals(className); })) return RemoveCssClass(classContainer, className);

        return classContainer + " " + className;
    }

Comments

0

Can you make your own custom classes? Derive from the ASP.NET Button and add a propert for Read only. Somewhere...probably in the OnPreRender, you can check the new property and set (or not set) the CSSClass property accordingly.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.