Saturday, January 6, 2007

ASP.NET: Adding Style Property to Web Control

Let's start off with something relatively easy. There are some web controls in ASP.NET (Calendar, TreeView and Menu for instance) that allow you to change various aspects of their looks with style properties.

There are two ways to set a style property in .aspx file. First you can set the property as an attribute to the element that defines the control by concatenating the name of the style object with the property:

<asp:TreeView ID="TreeView1" runat="server"
    NodeStyle-CssClass="treeViewNode"/>

Another way is to write the style as its own element that is placed inside the element defining the control:

<asp:TreeView ID="TreeView1" runat="server">
    <NodeStyle CssClass="treeViewNode" />
</asp:TreeView>

Naturally the style can be defined in codebehind as well:

TreeView1.NodeStyle.CssClass = "treeViewNode";

Now, what if you want to add your own style property to your custom web control? The task is not that difficult but there are few things that need to be taken into consideration. First style object is what they call a complex object so writing a simple setter/getter property for the style in your custom control class doesn't cut it. Secondly you might want to store the style object to view state if you're going to change the style at some point and have the change persist.

We'll start off by adding an instance variable for the style object to the class:

private Style myCustomStyle = null;

Next we will add a property for the style:

[DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
PersistenceMode(PersistenceMode.InnerProperty)]
public Style MyCustomStyle
{
    get
    {
        if (myCustomStyle == null)
        {
            myCustomStyle = new Style();
            // Set some default values here.

            if (IsTrackingViewState)
                ((IStateManager)myCustomStyle).TrackViewState();
        }

        return myCustomStyle;
    }
}

Notice how there is no setter part in the above property. The framework will call the getter and set properties directly to the object our getter provides. TrackViewState() is called to notify the framework that our style object should be tracked and changes to it should be saved as part of the control view state. We also add PersistenceMode(PersistenceMode.InnerProperty) to the property's metadata to specify that the property should persist as an inner property.

We are almost done but we still need to take care of view state. We want the view state for our style object to be loaded and saved at the same time with the control's view state. This is achieved by simply overloading the control's LoadViewState(), SaveViewState() and TrackViewState() methods as follows:

protected override void LoadViewState(object state)
{
    object[] states = (object[])state;

    base.LoadViewState(states[0]);
    ((IStateManager)MyCustomStyle).LoadViewState(states[1]);
}

protected override object SaveViewState()
{
    object[] states = new object[2];

    states[0] = base.SaveViewState();
    states[1] = (myCustomStyle != null) ? ((IStateManager)myCustomStyle).SaveViewState() : null;

    return states;
}

protected override void TrackViewState()
{
    base.TrackViewState();

    if (myCustomStyle != null)
        ((IStateManager)myCustomStyle).TrackViewState();
}

That's it! Now you should be able to use your style property just like with built-in ASP.NET controls. What you might still want to do is to apply the styles defined in the style object to the HTML code the control renders by overriding the control's Render() method.

Update (18 Mar 2007): I had a small typo in LoadViewState() method. Instead of accessing the myCustomStyle instance variable in the method, the property MyCustomStyle should have been accessed instead. Thanks to kal-EL@EFNet for pointing that out.

Friday, January 5, 2007

So, Why Yet Another Blog?

I'm very well aware that these days everybody and their mother has a blog and about 90 % of them are never read by anyone. The thing with this blog, though, is that I don't care at all whether someone reads it or not. I'm mainly going to use this as a place to gather some programming tips and tricks I've encountered so that I can access them quickly if I ever have the need. So, yeah, this is probably going to be a rather boring and geeky blog but fortunately no one is forced to read it (at least I hope so). However, if this blog actually manages to help someone then I've accomplished more than I originally expected.

I can't say I enjoy ranting about things but sometimes I just can't help myself. So don't be surprised if you occasionally find a blog entry that contains ranting and whining about something totally insignificant and unrelated. Consider yourself warned.