Comicster's New Skin System
For a while now I've been unhappy with the code that allows you to switch "skins" in Comicster. It has grown organically over the past year and a half and has evolved into the C# equivalent on the great Flying Spaghetti Monster. So last night I resolved to rewrite it in such a way that it would be faster, more flexible and easier to maintain.
A bit of back-story: Comicster consists of a main form with a SplitContainer, containing a TreeView on the left and a WebBrowser on the right. Whenever you select a node in the tree, the details of the item represented by that node are rendered as HTML and displayed in the WebBrowser. The HTML itself is read from text files defined by the user (assuming they've created their own skin) which means the entire right-hand pane is completely customizable.
I opened up the three C# files, containing a total of about 2500 lines of code, and hit Ctrl+A, Delete. Now Comicster had no skin system whatsoever. Of course, nor would it compile. So, I spent a while making sure that the classes (Skin, UISkin and ExportSkin) were defined, and that they implemented the minimum methods and properties to get Comicster compiling and running. So now I had a version of Comicster that ran, but showed a blank screen when you selected an item to view.
The next step was to write a class that I could use over and over again, which could search for custom tags within a string. I ended up creating a new class that I called HtmlContentProvider. It looks something like this:
public class HtmlContentProvider { /// Occurs when a custom tag is encountered within the template. public event EventHandler<CustomTagEventArgs> CustomTag; /// The template used for the HTML body content public string Template { get { /* ... */ } set { /* ... */ } } /// Determines whether to use reflection to automatically
/// default the custom tags /// to the value of the corresponding properties of the subject. public bool AutoFill { get { /* ... */ } set { /* ... */ } } /// Returns the body template with custom tags replaced
/// with content derived from 'subject'. public string GetContent(object subject) { /* ... */ } }
So what I have now is a class that can find tags like <#name> inside an HTML template, and, given an object to read from, can replace those tags with values from the object. It can even handle attributes, so <#name default="No Name"> works just fine. It uses a fairly complex regular expression, as you may have guessed.
Now that I had a reusable class I was able to flesh out the base Skin class. It contains one of these HtmlContentProviders for each kind of item that Comicster can display, and a bunch of event handlers for the CustomTag events of these providers.
The end result is a much cleaner code base which uses a lot of shared code and virtual methods with overrides in descendant classes. It's about two thirds of the original amount of code, which can only be a good thing.
This is going to make it really easy to add arbitrary attributes to certain custom tags. For example, if I want to allow a skin to specify the way in which it needs a title's list of issues rendered, I might use a special attribute like <#issues render="li"> or <#issues render="table">. That sort of thing.
