Applying ASP.NET 2.0 CSS Themes for Web Controls from DevExpress
I’m using DXperience v2007 vol 3 web controls from DevExpress in my project. These controls rendering is based on CSS delivered by vendor. Of course every developer can easily deliver his own CSS files for custom rendering. All is described in "How to use a sample CSS file together with the CssFilePath and CssPostfix properties" knowledge base article. I recommend to read it first before following the rest of this text.
CSS files for DevExpress controls are passed to the browser by WebResource.axd if any of controls on page has empty CSSFilePath and CSSPostfix. Here is HTML head rendered for ASP.NET’s StyleSheetTheme "WarmSky".
1: <head>
2: <link rel="stylesheet" type="text/css" href="/WebResource.axd?d=0pW-VO1NI7dfSm0O0Zq2ACMk4iRYek_Z5HOc04vPAnw8rKCun3Q8F4Pym8tFdctHHl4XQbUWFljcY6QXnl3p7siLCGDJ1iKxufw0w7iv7o1SPl7T7o3UZQUTi-mf3HfnnTK_ucsD_MJr-bXTL61n0g2&t=633379322277157280" />
3: <link rel="stylesheet" type="text/css" href="/WebResource.axd?d=0pW-VO1NI7dfSm0O0Zq2ADLPYMn7C88nFaD1LYCGrG_m7LNXR4VkbF6eCu4o_f7P_mjdqJx3sBZZrQ4bq76FtHEcj1_39ooWlWE2tPILiLqxFiQpwhIOKyztP9HejXbuTlRxa7U-fgC-XpX8zwnYmQ2&t=633379322285068656" />
4: <link rel="stylesheet" type="text/css" href="/WebResource.axd?d=0pW-VO1NI7dfSm0O0Zq2AMHyrkIXBazF7PZQtbA9x6A1Th7TVoM38ntOqyETlkyMPdYqI3W_YqfVOkNGOxuNNsjpxsJ17m3w-X4OU9YXQdI1&t=633379322298387808" />
5: <link href="../App_Themes/WarmSky/Styles/main.css" type="text/css" rel="stylesheet" />
6: <title>Some Title</title>
7: </head>
Good solution is to prepare CSS files for DevExpress controls and put them in theme folder. Then just make sure that all DevExpress controls in page has set CSSPostfix property. Of course it is quite boring to set properties on every DevExpress control in design time. To make it more automatic let’s create simple DXControlsHelper static class and put there method which applies theme to control.
1: /// <summary>
2: /// Applies theme for DevExpressControl.
3: /// </summary>
4: /// <param name="control">The control.</param>
5: /// <param name="themeName">Name of the theme.</param>
6: internal static void ApplyThemeToControl(ASPxWebControl control, string themeName)
7: {
8: if(control == null)
9: return;
10:
11: if(String.IsNullOrEmpty(themeName))
12: {
13: control.CssPostfix = null;
14: control.CssFilePath = null;
15: }
16: else
17: {
18: control.CssPostfix = themeName;
19: // control.CssFilePath = "~/App_Themes/" + themeName + "/DXControls/{0}/styles.css";
20: }
21: }
This method sets only CssPostfix property because we’ve put DXControls CSS files in Theme folder so they will be automatically included during page rendering. That’s why line (19) is commented out.
Now it’s is time to deliver all DXControls from the page. To make sure that we will deliver all controls let’s make some recursive method to find all of them. To make it more universal let’s use generics.
1: /// <summary>
2: /// Finds recursively all controls of defined type.
3: /// </summary>
4: /// <typeparam name="TControlType">The type of the control type.</typeparam>
5: /// <param name="root">The root.</param>
6: /// <returns>List of controls.</returns>
7: internal static IEnumerable<TControlType> FindAllControlsRecursively<TControlType>(Control root)
8: where TControlType : Control
9: {
10: if(root == null)
11: yield return null;
12:
13: foreach(Control item in root.Controls)
14: {
15: if (item is TControlType)
16: yield return item as TControlType;
17: foreach (TControlType itemChild in FindAllControlsRecursively<TControlType>(item))
18: yield return itemChild;
19: }
20: }
Now it is time to apply theme to all controls. Hmm … maybe not to all …
1: /// <summary>
2: /// Applies theme to all child DXControls from root.
3: /// </summary>
4: /// <param name="root">The root.</param>
5: /// <param name="themeName">Name of the theme.</param>
6: internal static void ApplyThemeToControlsRecursively(Control root, string themeName)
7: {
8: foreach (ASPxWebControl item in FindAllControlsRecursively<ASPxWebControl>(root))
9: {
10: if(String.IsNullOrEmpty(item.CssPostfix) && String.IsNullOrEmpty(item.CssFilePath))
11: ApplyThemeToControl(item, themeName);
12: }
13: }
Line (10) checks if control has already set one of property, for example in design mode then theme will not be applied to such control. Last step is to modify BasePage like this …
1: /// <summary>
2: /// Handles the Render event of the Page control.
3: /// </summary>
4: /// <param name="sener">The source of the event.</param>
5: /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
6: protected virtual void Page_PreRender(object sender, EventArgs e)
7: {
8: DXControlsHelper.ApplyThemeToControlsRecursively(this.Master, this.Page.StyleSheetTheme);
9: }
.. now HTML header of rendered page looks like this.
1: <head>
2: <link href="../App_Themes/WarmSky/DXControls/Editors/styles.css" type="text/css" rel="stylesheet" />
3: <link href="../App_Themes/WarmSky/DXControls/GridView/styles.css" type="text/css" rel="stylesheet" />
4: <link href="../App_Themes/WarmSky/DXControls/Web/styles.css" type="text/css" rel="stylesheet" />
5: <link href="../App_Themes/WarmSky/Styles/main.css" type="text/css" rel="stylesheet" />
6: <title>Title</title>
7: </head>

