Click here to Skip to main content
Click here to Skip to main content

Extending the Label Control

, 2 Jan 2003
Rate this:
Please Sign up or sign in to vote.
Extend the ASP.NET Label control to read strings from a cached resource XML file. Useful if you want to have an ASP.NET application in multiple languages or just want to let the site owner change the texts on pages and buttons etc.

Introduction

This small article shows how to extend the ASP.NET Label Control, so it reads resource strings from an XML file, which is stored in memory as a Cached DataSet with cache-dependency to the XML-file. Each web page has its own XML-file where texts are stored.

A control like this might come in handy if you need to translate your web application into other languages, or if the owner of the web site wants to change the “static” texts in the web pages by just editing the XML-files.

The program could easily be extended so the resource texts are read from another source, like a database, Web Service or a resource file, but I wanted to keep this article fairly small and easy, just to show the general idea.

I’ve used this technique in production, but since we use a web farm in production, having the resource strings in files isn’t that convenient, so instead I keep all strings in a database. Its also a good idea to give the web application owner, a nice interface for changing the texts. I might write another article to show you how to do this.

Some of the .NET features used in the sample project:

  • Custom controls
  • DataSet
  • XML
  • Cache

Code overview

The Control is called TextControl because it handles texts from a resource of strings. Perhaps a name like Text or StringResource is better, but TextControl will have to do. Feel free to change it Smile | :)

The central part of the code is made up from a single custom control, which inherits from, and extends the ASP.NET Label control. The control also uses a helper class called XMLResource, which will be explained later.

[DefaultProperty("Key"), 
ToolboxData("<{0}:TextControl runat="server"></{0}:TextControl>")]
public class TextControl : System.Web.UI.WebControls.Label

The reason I wanted to inherit from the Label control instead of the base Control class is that I want to use the rendering functions that are already implemented in the Label control for different fonts, colors and stuff. I’m lazy Wink | ;)

The TextControl needs to know what text string it should get from its resource of texts, so therefore I added a new property to it called Key. I might as well have re-used the inherited Text property that is already in there, but I had better use for that Text property (shown later).

private string key;

[Bindable(true), 
    Category("Appearance"),
    DefaultValue(""),
    Description("ID/Key of the resource text to get")] 
public string Key 
{
    get
    {
        return key;
    }
    set
    {
        key = value;
    }
}

There really isn’t much to say about the code snippet above. Its important to have the right attributes or the property might not show up in the property dialog in VisualStudio.NET.

The final (and interesting part) of the control is the code that renders the output in both run-time and design-time. To do this you must override the Render() method of the Label class. When the ASPX page renders content, the page calls this Render() method for each control on the page.

protected override void Render(HtmlTextWriter output)
{
    try
    {
        //get the text string from the resource
        //the Text property is used as "default" value
        Text = new XMLResource().GetValueFromKey(Key, Text);

        //call base class for rendering
        base.Render(output);
    }
    catch //catch design-time exceptions 
    {
        //render the Text property with brackets
        String tmpText = Text;
        Text = "[" + Text + "]";

        //call base class for rendering
        base.Render(output);

        //put back the default text without brackets
        Text = tmpText;
    }
}

As you can see in the code above, I’m using a try/catch block to handle errors, that might happen when we’re getting the text from the resource. I’m getting the texts from an XML-file with the same name as the ASPX file (as described in the introduction), so if the TextControl sits in an ASPX page called Default.aspx, the resource XML file for that page is called Default.xml and located in the same directory as the ASPX page.

To get the name of the ASPX file, I use the CurrentExecutionFilePath property of the Request object. The Request object is accessed via the current HttpContext, which isn’t available at design-time, so therefore I catch the exception thrown and show the default text within square brackets. You might as well prevent the exception in the XMLResource class (shown below) but this works fine for me.

If the requested string (based on the Key) isn’t found or if an exception occurs, I show the default text, which I get from the re-used Text property of the parent Label control.

So, how do we get the text from the resource file? All is done in the XMLResource class, which I’ll try to explain below. The XMLResource class has two methods; one public to get the requested text string and one private, which get the XML file from a cached DataSet. First the public method:

public String GetValueFromKey(String key, String defaultValue)
{
    DataSet ds = GetResourceDS();

    //filter out everything except our resource string in a dataview
    DataView dv = ds.Tables[0].DefaultView;
    dv.RowFilter = "key = '" + key + "'";
    if(dv.Count > 0)    //key found, show the value
        return dv[0]["value"].ToString();
    else //key not found return default value
        return defaultValue;
}

It’s pretty simple. Get a DataSet, which contains all the resource strings for this ASPX page. Then filter out our text string based on the key. If the key isn’t found, the default value is returned.

private DataSet GetResourceDS()
{
    //Try to get the DataSet from the Cache first
    //Note that this one bombs at design time, (we have no HttpContext)
    //but this exception is caught in the custom control
    DataSet ds = (DataSet)HttpContext.Current.Cache[HttpContext.Current.
        Request.CurrentExecutionFilePath];
    if(ds == null)
    {
        //no DataSet in the Cache, get a new one and store it in the cache
        //get the text-id from the resource file for our web-page
        ds = new DataSet("resourceStrings");
        //build the name of the xml file from the name of the web
        // page we're sitting in
        String fileName = HttpContext.Current.
            Request.CurrentExecutionFilePath;
        fileName = fileName.Substring(0, fileName.LastIndexOf(".")) 
                                                            + ".xml";

        try
        {
            //read ds from the xml file
            ds.ReadXml(HttpContext.Current.Server.MapPath(fileName));
        }
        catch (System.IO.FileNotFoundException)
        {
            //xml file not found, we will return the empty DataSet
        }
        //put in Cache with dependency to the xml file
        HttpContext.Current.Cache.Insert(HttpContext.Current.
            Request.CurrentExecutionFilePath,
        ds, new CacheDependency(HttpContext.Current.
            Server.MapPath(fileName)));
    }

    return ds;
}

The GetResourceDS method is longer, but not too complicated. First it tries to get the DataSet from the built in .NET Cache object, which is very cool. As a Cache-key I use the name of the ASPX page. If the DataSet wasn’t in the Cache, I create a new DataSet and read in the content from the XML file, which should have the same name as the ASPX page where the TextControl is used (as explained earlier).

The cached DataSet should expire whenever someone updates the XML file, right? So, therefore I add a cache-dependency, which points at the XML file. So now, when the file is updated, the Cache object detects this and removes the cached DataSet from its memory.

The ASP.NET Cache might be even more useful if you decide to put all your texts into one big, single XML file. There is no reason to read that XML file every time a TextControl is rendered, right?

Using the code

To use the TextControl from VisualStudio.NET, you need to add the assembly containing the control to the toolbox. Load up VisualStudio.NET and create a new ASP.NET application. Then add a WebForm and bring it up in design view. Now right click in the Toolbox containing all the other built-in server controls and select “Customize Toolbox...” from the popup menu. Now select the tab named “.NET Framework Components” and use the “Browse...” button to find the DLL containing the TextControl. The sample project has the TextControl in the MyControls.dll assembly.

VS.NET will automatically find and activate the TextControl inside the MyControls DLL. You can put the control on any of the different tabs in the Toolbox, but I prefer to have it together with the other server controls.

The TextControl is now ready for use on a web page. Create a WebForm called WebForm1.aspx and drag/drop the TextControl from the Toolbox on it in design view. Select the TextControl on the ASPX page and look at the property dialog. Change the Key property to “key1” (for example) and optionally put some text in the Text property. It should look similar to this in the HTML view:

<form id="Form1" method="post" runat="server">
    <cc1:TextControl id="TextControl1" runat="server" Key="key1">
    </cc1:TextControl>
</form>

Note that VS.NET automatically added a reference to MyControls in the project view and also added code at the top of the file for registering the custom control:

<%@ Register TagPrefix="cc1" Namespace="MyControls" Assembly="MyControls" %>

The only thing to do now is to create the XML file containing the resource strings. Just add a new XML file called WebForm1.xml in the same directory as your newly created ASPX page. The XML file is pretty simple and looks like this:

<?xml version="1.0" encoding="utf-8" ?> 
<resourceStrings>
    <string>
        <key>key1</key>
        <value>sampleValue 1</value>
    </string>
    <string>
        <key>key2</key>
        <value>sampleValue 2</value>
    </string>
</resourceStrings>

Each string node in the XML file makes up a key/value pair in the resource file.

The web page is ready for testing and should show a plain web page with the text “sampleValue 1” on it. If you change the text “sampleValue 1” in the XML file into something else and reload the page, it should show the new value at once.

The XMLResource class can also be used from code behind to get and set texts at run-time. Just call the public method GetValueFromKey() from the code behind:

//set the text of the label in run-time
Label1.Text = new MyControls.XMLResource().GetValueFromKey("key2",
                                                        Label1.Text);

Conclusion

As I wrote earlier, there are lots of things you could (and should) do with this code to use it in production, but I hope this little article can get some of you into making your own controls, which is very fun and really, really useful. I recommend doing similar controls for Buttons, Hyperlinks and so on, the owner of the web site might want to change the texts of all the submit buttons to SAVE or DO IT...

Some things that I’ve added to this control in a production site:

  • XML Schema for validating the XML resource file (never trust a customer to write perfect XML Wink | ;)
  • Password protected web form for updating the XML texts

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Johan Danforth
Web Developer
Sweden Sweden
Johan started coding a long time ago on a ZX Spectrum and the Z80 processor. His parents were rather sceptical about him sitting up all night coding and playing Manic Miner but they don't regret buying that computer now, since it turned out pretty good in the end.
Johan is now working as a Systems Architect and jump between a number of different platforms and computer languages every day. He wouldn’t cope if it weren’t for IntelliSense and code completion...

Comments and Discussions

 
GeneralTry Catch Block change Pinmemberpirklk19-Nov-03 5:45 
GeneralRe: Try Catch Block change Pinsusspixie@ausystem.se19-Nov-03 7:34 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140814.1 | Last Updated 3 Jan 2003
Article Copyright 2003 by Johan Danforth
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid