Tuesday, June 19, 2007

ASP.NET, or more accurately Web Forms, offer us a completely new way to create web pages. We can now work with control just like Windows developers. We even get to use an event driven model. How make best use of this? Below I present a useful pattern that I'm almost always following while working with ASP.NET.

Suppose that you are building a web application with lots of pages and forms for data input. On those forms you typically have input fields in form of TextBoxes, DropDownLists etc. Usually there are also some buttons that allow for invoking actions. Those actions include things like "Add", "Update" or "Cancel".

The standard way to create a form for inputing data would be to use mentioned before standard ASP.NET controls. That is fine. It gives us access to all that ASP.NET Web Forms can offer in terms of Page structure and event model. There is however one issue. In most cases there we will be several pages with the same buttons on them. Take for example the "Cancel", "Add" or "Update" buttons. Almost every time there is a data to be edited, there will be those buttons. If we use the "out of the box" approach, we end up with code similar to the following, on lots of pages:

<asp:Button ID="Add" runat="server" Text="Add" />

At first glance, there is not much wrong with it. If you look closer however, we can see the potential problems. One of the is certainly the Text property.

Why is it a problem? Suppose that now we need to localize the application? In this case, we will have to make a change in many places. Simple technique would be to decorate the Button control with attributes used by the Localization feature of ASP.NET like meta:resourcekey. But still we end up with one major problem: every time a change is required (that cannot be dealt with using Localization, Skins or other built in feature), we have to make it in many places. As we know: change in many places is a BAD PRACTICE.

What if we find out, that our add buttons need to have some special CssClass attribute? What if all our "Cancel" Buttons should have CausesValidation property set to false? Once again: a change is required in many places - which is ... BAD PRACTICE!

For localization, we can add an implicit localization attributes as mentioned above. What about CausesValidation or CssClass properties? Some of them we can try to set using Skin files, but as you will sooner or later discover, theming support in ASP.NET is VERY limited, especially when it comes to localization. You cannot localize skin files, some of the properties are not themeable etc. There are many examples of potential problems but what is a solution? It seams that there has to be a better way.

In my practice I have found that it is usually good not to use the standard controls that come with ASP.NET but rather make new ones that directly inherit from them. Doing so we inherit all the functionality without loosing anything (almost). As an example, take the aforementioned "Cancel" button. If we create a new control like this:

public class CancelButton : Button
{
    protected override void OnInit(EventArgs e)
    {
        Text = "Cancel"; // get value from resource
        CausesValidation = false;
        base.OnInit(e);
    }
}

And on pages, instead of the standard button, use this new control:

<my:CancelButton ID="Add" runat="server" />

In doing we allow ourselves to make changes in one place that will affect the whole application. In this way, without much work, we have gained a lot of flexibility for the future and saved a lot of potential work. If some time in the future, we need to localize the Text property for all Cancel buttons, we can do it in just one place which is a GOOD PRACTICE.

Benefits do not end there! If some day, there is a requirement, that all buttons should now be LinkButtons instead of normal Buttons we can easily do it bu just changing the base class from which our CancelButton inherits and the same technique can be used with other controls too (as long as their public interfaces match at least in naming).

Now the question is if you should always use the inherited controls. As usual, there is no simple answer to that question. There is a cost of having a lots of additional classes in the project if we try to follow this rule. Imagine that just for standard buttons, there will be quite a few: Add, Cancel, Remove, Back, Change, Edit just to mention some of them. For small, fast'n'dirty projects it will of course be a huge overkill (or will it?), but if you know that the project is not a small task, it is, in my opinion, a thing that should be seriously considered.

kick it on DotNetKicks.com

Wednesday, June 20, 2007 1:37:04 PM (Central European Standard Time, UTC+01:00)
Nice post, but what about taking it further ... Let's say that I have same TextArea-controls (for e.g. FirstName, LastName ..) across the application (on five pages) and I should add a new TextBox-control named Date of Birth. The difficult way of doing this would be adding declaratively this new TextArea-control five pages separately, but this would be tedious task to do. So should I just create one big ascx-control, which includes all the necessary fields and depending of the page functionality, hide or expose fields as needed ?
Timo
Wednesday, June 20, 2007 2:35:05 PM (Central European Standard Time, UTC+01:00)
There is no answer to this question. As always, everything depends on the problem at hand. Even using inherited controls may not be the best way to go in some cases.
I often use user controls and by using declarative syntax I enable/disable some of its features, but does it mean I'm right?
Friday, June 22, 2007 7:07:54 PM (Central European Standard Time, UTC+01:00)
Perfectly said -- I utilized this practice in my last project and it was a big win. Thanks for detailing the advantages so clearly!
R Sturim
Thursday, June 28, 2007 10:46:39 AM (Central European Standard Time, UTC+01:00)
Whilst this works if you have a variety of almost-similar control labels you can end up with a massive amount of classes.

What we have been using is adding a Localise method to each of our pages that is called from OnRender that simply sets the labels there. This allows us to re-use the Resource text as required or vary without the overhead of additional control classes.

e.g.

public override void OnRender(object sender, EventArgs e)
{
base.OnRender(sender, e);
Localise();
}

public void Localise()
{
CancelButton.Text = Resources.Language.CancelButtonText;
OKButton.Text = Resources.Language.OKButtonText;
Page1SpecificButton.Text = Resources.Language.Page1SpecificButtonText;
}

Also if you are upgrading an existing application to do this remember to check out the Resource Refactoring Tool at http://www.codeplex.com/ResourceRefactoring

[)amien
Name
E-mail
Home page

Comment (Some html is allowed: a@href@title, strike) where the @ means "attribute." For example, you can use <a href="" title=""> or <blockquote cite="Scott">.  

Enter the code shown (prevents robots):

Live Comment Preview