Rambling about ASP.NET Themes

Wednesday, December 21, 2005

If I go to the trouble of designing multiple ASP.NET Themes using skin and css files, chances are I will let a user choose the theme they find most pleasing. To let the user choose a theme, I’d first have to know what themes are available….

Plan 1

I could maintain the list of available themes in a configuration file, or in a database table. When I add new themes, I’ll have to update the file or table. A better design would be for the application to figure out which themes are available auto-magically, perhaps by using a class provided by ASP.NET….

Plan 2

Digging around, I find the IThemeResolutionService interface and it’s GetAllThemeProviders method. Each ThemeProvider encapsulates a theme. Unfortunately, the service appears to be available only at design time.

Due to the dynamic compilation features in ASP.NET, I figure there is no foolproof way to see all available themes. Even if I reflected every Type in the AppDomain, some themes may still be left on disk and un-compiled. Themes … on disk …

Plan 3

A simple solution would just look at what directories are available underneath the App_Themes directory. Without any error checking, the code would look like:

string themesPath = Server.MapPath("~/App_Themes");

List<string> themes = new List<string>();
foreach (string d in Directory.GetDirectories(themesPath))
{
  
// strip away all but the directory name            
  themes.Add(
      d.Substring(
        d.LastIndexOf(
Path.DirectorySeparatorChar) + 1
      )
    );
}

The above code seems fine until I start thinking about how it could break. It occurs to me that the ASP.NET plumbing doesn’t look directly at the file-system, but sees files and directories through virtualization goggles. There is probably a safer way to write the code, just in case the themes live in a database, or get pre-compiled away….

Plan 4

VirtualPathProvider vPathProvider;    
vPathProvider =
HostingEnvironment.VirtualPathProvider;

VirtualDirectory themeDirectory;
themeDirectory = vPathProvider.GetDirectory(
"~/App_Themes");

List<string> themes = new List<string>();
foreach (VirtualDirectory d in themeDirectory.Directories)
{
  themes.Add(d.Name);
}

Now, I’m cooking with oil and a bottle of sherry.
Except … the rumors of the demise of global themes have been greatly exaggerated…

Plan 5

At some point in the preview builds of ASP.NET, Microsoft shipped a couple global themes. Although the themes themselves were dropped around beta 2, the ability to define global themes for all applications still exists. See the bottom of “How to: Define ASP.NET Page Themes” to setup global themes under WebDev and IIS.

Although I found it possible to get to the global themes directory using the VirtualPathProvider and HttpRuntime.AspClientScriptVirtualPath, the solution was beginning to feel a bit clunky. Also, since global themes live outside an application’s home directory, there is no way to read the available theme directories when running at medium trust. (The themes are still available to skin a page, however).

Finally, I’ve arrived at…

Plan 6

Go back to plan 1 … or plan 4.


Comments
Bryan Avery Monday, June 5, 2006
Great little solution, well done for a good write-up
gravatar Andrew Monday, May 24, 2010
I don't really see too much of a downside to doing this, which is pretty close to your Plan #3 above:

Public Class ThemeReader
Public Shared Function Read() As List(Of String)
Dim objRet As New List(Of String)

Try

Dim strThemePath As String = HttpContext.Current.Server.MapPath("~/App_Themes")
Dim MyDI As New System.IO.DirectoryInfo(strThemePath)

For Each Directory As System.IO.DirectoryInfo In MyDI.GetDirectories
objRet.Add(Directory.Name)
Next

Return objRet
Catch ex As Exception
Throw
End Try

End Function
End Class
Comments are now closed.
by K. Scott Allen K.Scott Allen
My Pluralsight Courses
The Podcast!