OdeToCode IC Logo

Themes For ASP.NET User Controls

Tuesday, June 27, 2006 by scott

Q: I can't apply a Theme attribute to my user control. How am I supposed to theme and skin my user controls?

A: User Controls don't have a theme property, but when you assign a theme to a page, the theme does influence all of the controls in the page. A user control uses the theme of its page.

If the theme includes CSS files, then ASP.NET will link to the CSS files. Any styles inside the CSS that are applicable to classes, names, and markup in the user control will style the user control.

If the theme includes skins, then the skins will skin the appropriate controls inside of user controls. For instance, if you have defined a default skin for Calendar controls, then any Calendar controls inside your user control will have that skin applied.

It is also possible to apply a skin directly to the user control, we just need to know a couple tricks. First, let's define a simple user control: MyWidget. MyWidget just renders a Message property.

<%@ Control Language="C#" CodeFile="MyWidget.ascx.cs" Inherits="MyWidget" %>
<h1><%= Message %></h1>

The CodeFile for the control declares a Message property, but more importantly, adds a Themeable attribute to the control.

using System;
using System.Web.UI;

[
Themeable(true)]
public partial class MyWidget : System.Web.UI.UserControl
{
    
private string _message;
    
public string Message
    {
        
get { return _message; }
        
set { _message = value; }
    }
   
}

Finally, we just need a .skin file. We need to @ Register our control, and then write the skin just like we'd write any other skin.

<%@ Register Src="~/MyWidget.ascx" TagName="MyWidget" TagPrefix="otc" %>

<otc:MyWidget Message="Sahil Looks Funny" runat="server" />

Now, anytime we drop MyWidget on a page, the skin file will set a default message (assuming the control's page has a theme selected).

URL-Rewriting breaks my HREFs

Monday, June 26, 2006 by scott

Q: I've been using the RewritePath method of the HttpContext class to rewrite URLs since ASP.NET 1.1. Everything was working until I moved my CSS files inside an ASP.NET 2.0 Theme directory. ASP.NET automatically writes out link tags for my stylesheets, but the href inside is broken. How do I fix this problem?

A: When rewriting URLs, don't use the RewritePath method that only accepts a path parameter, use the new overload that accepts a path parameter and a rebaseClientPath parameter. Pass a value of false for rebaseClientPath and the auto-magic injection of stylesheets will work.

Full explanation: The href in the stylesheet link provided by ASP.NET is a relative URL computed by Control.ResolveClientUrl. ResolveClientUrl is computing a path relative to the rewritten URL, not the original URL. Unfortunately, the relative URL won't work in the browser, because the browser is using the original URL.

By default, RewritePath rewrites a private field in the HttpRequest class by the name of _clientBaseDir. This field is used by Control.ResolveClientUrl to compute the relative URL. Passing false in the overloaded version of RewritePath tells the method to not rewrite this field. ResolveClientUrl will then use the original URL (the same URL the browser is using) when calculating relative URL, so everything works, and harmony remains.

Software Librarian

Friday, June 23, 2006 by scott

One of the nice benefits of an MSDN subscription is having a boatload of software for development, test, and experimentation. The drawback is the boatload of discs that accumulate during the year.

Consonica Software has a product they call the “Software Librarian for MSDN”. After a quick install, you tell the software what type of subscription you have, and what products you use, and then it tells you which discs to feed into the computer. Sometime later, everything you use is now copied to a hard drive. The monthly updates are a breeze, and no more fumbling around for discs. Watch the video demo.

Software Librarian

Disclaimer: Consonica sent me a copy of their software to use. I can honestly say I've found it works very well and saves time. In a partner setting, you typically have 5 MSDN licenses for every 1 set of physical media. In theory, this works well, but in practice, it works like this:

0:00 Decide to install SQL Server 2005
0:01 Start to rummage through 50 DVDs in the MSDN storage case
0:15 Check the DVD drive on dev machine #1
0:16 Check the DVD drive on dev laptop
0:17 Check the DVD drive on dev machine #2
0:18 Start an email rant about DVDs missing from the case
0:19 Remember you sent the same email last month
0:20 Copy last month’s rant into current email, press Send
0:23 Start SQL 2005 download from MSDN subscribers page
1:23 Download completes
1:23 Someone finds the SQL 2005 DVD in the break room

Check out Software Librarian, it does save some time.

The Base Activity Library in Windows Workflow

Thursday, June 22, 2006 by scott

The latest OdeToCode article covers the base activity library in Windows Workflow. We look at the activities to communicate with local and remote services, evaluate rules, listen for events, and every other activity that ships in the WF toolbox.

Read it here: Windows Workflow: The Base Activity Library

As always, I appreciated any feedback you can offer.

The System.Exception to the Rule

Tuesday, June 20, 2006 by scott

Two weekends ago, I did presentations on ASP.NET master pages and Windows Workflow at the MAD Code Camp in Reston, Virginia. A bit of .NET trivia came up during the workflow session when we talked about fault handlers and the types of exceptions we can throw and catch in .NET.

Despite the fact that this code:

static void Main(string[] args)
{
    
throw "You've done sometihng wrong!";
}

.. results in a compiler error (The type caught or thrown must be derived from System.Exception), the CLR itself has no such restriction. I planned on doing some research and putting together a blog post, but Jason Bock did all of the hard work already. See “Throwing Exceptions That Are Not Exceptions” – full IL code included.

This Post Has No Name

Monday, June 19, 2006 by scott

One of the hardest tasks in software is finding a good name. Names for variables, classes, libraries, and products requires countless mental cycles - as it should. Names are a serious business. In software, names bring imaginary things to life. Good names lead to readability, shared vocabularies, and emotional attachments. Bad names lead to confusion, pain, and assistance from highly paid consultants.

The Art of Naming Variables

Have you ever wondered how many hours the software world has spent inventing naming standards? You know the "prefix this" and "suffix that" and "start all of these with a capital C" documents. These standards are the perfect method for making Code look Consistent, but always include the rule "use logical and descriptive names". A naming standard is to good names as "use pronouns clearly" is to good grammar.

How to: Bastardize a Common Vocabulary

It's a shame Visual Basic uses keywords like MustInherit and Overridable. The rest of the world talks about abstract classes and virtual functions, but Visual Basic has to be different. They say it's because VB is geared to the developer who wants to add value to their business. Yet the developer who is typing "MustInherit" clearly isn't thinking about a business problem - they are thinking about a class hierarchy. Why shouldn't VB have the same vocabulary as the rest of the OOP world? Ever hear of a MustInherit factory pattern?

Every Time I Hear Your Name

For libraries and frameworks, the naming stakes get even higher. If the framework is going into widespread use, people will encounter the name in book titles, articles, power point slides, and technical conversations. People become attached to the name, as it covers many abstract and conceptual things. Witness the outcry over the Indigo to WCF switch, and the Avalon to WPF switch. Will Atlas stay Atlas, or will Atlas become the ASP.NET Client Side Integration Framework (ACSIF)?

The Name Game

Naming a product is the toughest assignment of all. We need to search trademarks and domain names. We need something that is snappy and looks good on a business card. The safe approach is just to make something up, like FlazBlatter. Someday, there will be a product named FlazBlatter, mark my words….

The Mysteries Of System.Messaging

Sunday, June 11, 2006 by scott

The majority of the .NET base class library feels clean and intuitive. Every so often though, you come across some oddballs. For instance…

Given a MessageQueue object named queue, what does the following code produce at compile time?

foreach (Message message in queue)
{
    
// ...
}

The code results in a warning:

'System.Messaging.MessageQueue.GetEnumerator()' is obsolete: 'This method returns a MessageEnumerator that implements RemoveCurrent family of methods incorrectly. Please use GetMessageEnumerator2 instead.'

Wow! Deprecating the GetEnumerator method is serious business. Using foreach is now out of the question.

The AcknowledgeTypes enum baffles me. There seems to be a lack of symmetry in the names. If we want an acknowledgment that a message has reached it's queue, we use the "PositiveArrival" flag, but if want an acknowledgment when a message doesn't arrive we use the "NotAcknowledgeReachQueue" flag. Arrival and reach are easy to distinguish from a receive, which means something successfully pulled the message from the queue, but what if we want to request an acknowledgement if something doesn't receive our message? There is the NotAcknowledgeReceive flag, which combines the NegativeReceive and the NotAcknowledgeReachQueue. Do you acknowledge all this? Negative! 

Finally, there seems to be too much of a good thing going on with asynch operations. I can call BeginReceive on a queue to start a receive, and pass a callback method, or I can subscribed to the ReceiveCompleted event and then call BeginReceive. In the ReceiveCompleted event handler I can call EndReceive manually (which feels good, because Begin and End calls should match), or use the Message property of the incoming RecieveCompletedEventArgs parameter and fetch the Message property (which calls EndReceive under the covers).

Choices, choices.