Monday, January 25, 2010

WebResource and PerformSubstitution in Visual Studio 2008 design mode

I was trying to make a web control that would be completely styled by CSS rules. That was hard enough with the browsers all thinking differently about what I was saying, but then I had to also make it look decent in the Visual Studio 2008 designer.

You see, in order to use in the CSS rules (or in Javascript resources, for that matter) files that have been stored as embedded resources in the control assembly, a special construct that looks like <%=WebResource("")%> is required.

The complete resource name is obtained by appending the path of the embedded resource inside the project to the namespace of the assembly. So if you have a namespace like MyNamespace.Controls and you have an image called image.gif in the Resources/Images folder in your project, the rule for a background image using it is background-image:url(<%=WebResource("MyNamespace.Controls.Resources.Images.Image.gif")%>);.

Also, in order to access the CSS file you need something like [assembly:
"text/css", PerformSubstitution = true)]
somewhere outside a class (I usually put them in AssemblyInfo.cs). Take notice of the PerformSubstitution = true part, that looks for WebResource constructs and interprets them.

Great! Only that PerformSubstitution is ignored in design mode in Visual Studio!!. The only solution to render the control correctly is to render the CSS file yourself and handle the WebResource tokens yourself. Here is a method to do just that:

private static readonly Regex sRegexWebResource =
new Regex(@"\<%\s*=\s*WebResource\(""(?<resourceName>.+?)""\)\s*%\>",
RegexOptions.ExplicitCapture | RegexOptions.Compiled);

public static string RenderDesignTimeCss2(Control control, string cssResource)
var type = control.GetType();
/*or a type in the assembly containing the CSS file*/

Assembly executingAssembly = type.Assembly;
var stream=executingAssembly.GetManifestResourceStream(cssResource);

string content;
using (stream)
StreamReader reader = new StreamReader(stream);
using (reader)
content = reader.ReadToEnd();
content = sRegexWebResource
new MatchEvaluator(m =>
return string.Format("<style>{0}</style>", content);

All you have to do is then override the Render method of the control and add:

if (DesignMode) {

It seems that rendering a style block before the control disables the resizing of the control in the Visual Studio designer. The problem and the solution are described here.