Tuesday, July 12, 2011

Turning URLs into Hyperlinks (with Truncating Long URLs) in ASP.NET

Turning URLs into hypyerlinks is fairly easy and straightforward, and actually has been discussed many times before (for example, see here and here). But some times, you also need to truncate the URL in the link text if it's too long. For example, if you have some text with a very long URL like this:

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco http://www.example.com/thisisaveeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeerylongurl.html laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

You'll probably want to not only turn the URL into a hyperlink, but also truncate the URL in the hyperlink's text to look something like this:

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco http://www.example.com/thisisaveeeeeeeeee... laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Finding and Truncating the URLs

Finding the URLs is straightforward and is easily done using Regular expressions, but to truncate the long URLs, we'll need to to check whether the URL is too long, and if so, truncate it before using it in the hyperlink's text.

In this post, I'm going to discuss the details of how to find the URLs. For truncating long URLs, I'm going to use code from my previous post about truncating long text in ASP.NET. Please refer to that post for the details of how the text is truncated.


The Code
public static class UIHelper
{
    private static string ConvertURLs(string text, int textMaxWidth, string fontName, int fontSizeInPixels, bool isFontBold)
    {
        if (string.IsNullOrEmpty(text))
            return text;

        return Regex.Replace(
            text, 
            @"(http(s)*://([\w.]+/?)\S*)",
            LinkTextTruncator.GetLinkTextTruncator(textMaxWidth, fontName, fontSizeInPixels, isFontBold).MatchEvaluator,
            RegexOptions.IgnoreCase | RegexOptions.Compiled);
    }
}

class LinkTextTruncator
{
    public LinkTextTruncator(
        int textMaxWidth, 
        string fontName, 
        int fontSizeInPixels, 
        bool isFontBold)
    {
        _textMaxWidth = textMaxWidth;
        _fontName = fontName;
        _fontSizeInPixels = fontSizeInPixels;
        _isFontBold = isFontBold;
        _matchEvaluator = new MatchEvaluator(TruncateText);
    }

    private int _textMaxWidth;
    private string _fontName;
    private int _fontSizeInPixels;
    private bool _isFontBold;
    private MatchEvaluator _matchEvaluator;

    public string TruncateText(Match m)
    {
        string url = m.ToString();
        return string.Format(
            "{1}",
            url,
            TextTruncator.TruncateText( // For the implementation of TextTruncator.TruncateText(), please refer to my post about truncating long text in ASP.NET
                url,
                _textMaxWidth,
                _fontName,
                _fontSizeInPixels,
                _isFontBold));
    }

    public MatchEvaluator MatchEvaluator
    {
        get
        {
            return _matchEvaluator;
        }
    }

    //
    // Static Members
    //

    private static Dictionary _linkTextTruncatorDic;
    private static Dictionary LinkTextTruncatorDic
    {
        get
        {
            if (_linkTextTruncatorDic == null)
            {
                _linkTextTruncatorDic = new Dictionary();
            }
            return _linkTextTruncatorDic;
        }
    }

    private static object _linkTextTruncatorLocker = new object();
    public static LinkTextTruncator GetLinkTextTruncator(int textMaxWidth, string fontName, int fontSizeInPixels, bool isFontBold)
    {
        string specs = textMaxWidth.ToString() + "px_" + fontName.ToLower() + "_" + fontSizeInPixels.ToString() + "px" + (isFontBold ? "_bold" : "");
        if (!LinkTextTruncatorDic.ContainsKey(specs))
        {
            lock (_linkTextTruncatorLocker)
            {
                LinkTextTruncatorDic.Add(specs, new LinkTextTruncator(textMaxWidth, fontName, fontSizeInPixels, isFontBold));
            }
        }

        return LinkTextTruncatorDic[specs];
    }
}

Usage Example
<%= UIHelper.ConvertURLs("Some text that contains URLs", 200, "Verdana", 12, false) %>

Notes About the Code

For the implementation of TextTruncator.TruncateText(), please check my previous post about truncating long text in ASP.NET

If you check the code in UIHelper.ConvertURLs(), you'll find that I use one of the overloaded versions of Regex.Replace() that takes a MatchEvaluator delegate. Every time a match is found (a URL in this case), LinkTextTruncator.TruncateText() is called, which converts the URL into an anchor tag and calls TextTruncator.TruncateText(), which truncates the URL if it's longer than the specified width, otherwise it returns the same URL untruncated.

I chose to cache the LinkTextTruncator objects (by font family, size and weight) in order to avoid creating a new object every time URLs are truncated and so slightly alleviate the overhead on the garbage collector. But, it should be easy to remove the caching if you find it unneeded.

Needless to say, you need to encode the text passed to ConvertURLs() (using HttpUtility.HtmlEncode(), or Html.Encode() if you're using ASP.NET MVC). You should notice that any ampersands in the URL will be converted to & when the text is encoded, but this is not only valid in HTML, it's actually required but often ignored. If for some reason you don't want the ampersands in the URLs to be converted, you can use Replace("&", "&") on the URL in LinkTextTruncator.TruncateText().

Wednesday, September 8, 2010

Truncating a text string in ASP.NET to fit within a given pixel width

Sometimes when developing a web application, you have strings that must fit within a certain pixel width (usually the width of a container, like a div, p, td .. etc). This is esp. more common with links, as you sometimes need every link to fit within one single line and doesn't wrap to the next line (which will happen if the text of the link is too wide to fit within the width of the container). Or to express it visually, you might have a link that looks like this:

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis.

And want to make it look like this:

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor ...

While this should eventually be possible with CSS (using the text-overflow style), it's still not implemented in all browsers. There are some javascript solutions for now that can do the same thing across all browsers, still, I personally prefer a server-side solution as it:

  1. Saves bandwidth (which can also be good for the user experience if the text is too long and takes more time to download, esp. if the user has a slow connection).
  2. Is guaranteed to work in all browsers (including the older ones) regardless of whether they have javascript enabled, or even support it.

OK, now that you're (hopefully!) convinced, let's see how this can be done in ASP.NET. Obviously, first thing we need to do is measure the text width so that if it's longer than the specified width, we truncate it. For this, we'll need to use TextRenderer.MeasureText(), which is actually intended for Windows Forms Applications, but still can be used with ASP.NET.

I wrapped the code required for the truncation into a class, TextTruncator, which you can see its code here (also see the download link at the end of the post):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;

namespace aspnet_TextTruncator
{
    public static class TextTruncator
    {
        // Private Properties
        //

        private static Dictionary<string, int[]> _fontWidthDic;
        private static Dictionary<string, int[]> FontWidthDic
        {
            get
            {
                if (_fontWidthDic == null)
                {
                    _fontWidthDic = new Dictionary<string, int[]>();
                }
                return _fontWidthDic;
            }
        }

        //
        // Public Methods
        //

        public static string TruncateText(string text, int textMaxWidth, string fontName, int fontSizeInPixels)
        {
            return TruncateText(text, textMaxWidth, fontName, fontSizeInPixels, false);
        }

        public static string TruncateText(string text, int textMaxWidth, string fontName, int fontSizeInPixels, bool isFontBold)
        {
            if (string.IsNullOrEmpty(text))
                return text;

            // Check
            //
            if (textMaxWidth < 1 ||
                string.IsNullOrEmpty(fontName) ||
                fontSizeInPixels < 1)
            {
                throw new ArgumentException();
            }

            int[] fontWidthArray = GetFontWidthArray(fontName, fontSizeInPixels, isFontBold);
            int ellipsisWidth = fontWidthArray['.'] * 3;
            int totalCharCount = text.Length;
            int textWidth = 0;
            int charIndex = 0;
            for (int i = 0; i < totalCharCount; i++)
            {
                textWidth += fontWidthArray[text[i]];
                if (textWidth > textMaxWidth)
                {
                    return text.Substring(0, charIndex) + "...";
                }
                else if (textWidth + ellipsisWidth <= textMaxWidth)
                {
                    charIndex = i;
                }
            }
            return text;
        }

        //
        // Private Methods
        //

        private static int[] GetFontWidthArray(string fontName, int fontSizeInPixels, bool isFontBold)
        {
            string fontEntryName = fontName.ToLower() + "_" + fontSizeInPixels.ToString() + "px" + (isFontBold ? "_bold" : "");
            int[] fontWidthArray;
            if (!FontWidthDic.TryGetValue(fontEntryName, out fontWidthArray))
            {
                fontWidthArray = CreateFontWidthArray(new Font(fontName, fontSizeInPixels, isFontBold ? FontStyle.Bold : FontStyle.Regular, GraphicsUnit.Pixel));
                FontWidthDic[fontEntryName] = fontWidthArray;
            }

            return fontWidthArray;
        }

        private static int[] CreateFontWidthArray(Font font)
        {
            int[] fontWidthArray = new int[256];
            for (int i = 32; i < 256; i++)
            {
                char c = (char)i;
                fontWidthArray[i] = IsIllegalCharacter(c, false) ? 0 : GetCharWidth(c, font);
            }
            return fontWidthArray;
        }

        private static int GetCharWidth(char c, Font font)
        {
            // Note1: For typography related reasons, TextRenderer.MeasureText() doesn't return the correct
            // width of the character in pixels, hence the need to use this hack (with the '<' & '>'
            // characters and the subtraction). Note that <' and '>' were chosen randomly, other characters 
            // can be used.
            //

            // Note2: As the TextRenderer class is intended to be used with Windows Forms Applications, it has a 
            // special use for the ampersand character (used for Mnemonics). Therefore, we need to check for the 
            // ampersand character and replace it with '&&' to escape it (TextRenderer.MeasureText() will treat 
            // it as one ampersand character)
            //

            return
                TextRenderer.MeasureText("<" + (c == '&' ? "&&" : c.ToString()) + ">", font).Width -
                TextRenderer.MeasureText("<>", font).Width;
        }

        private static bool ContainsIllegalCharacters(string text, bool excludeLineBreaks)
        {
            if (!string.IsNullOrEmpty(text))
            {
                foreach (char c in text)
                {
                    if (IsIllegalCharacter(c, excludeLineBreaks))
                        return true;
                }
            }

            return false;
        }

        private static bool IsIllegalCharacter(char c, bool excludeLineBreaks)
        {
            // See the Windows-1252 encoding (we use ISO-8859-1, but all browsers, or at least
            // IE, FF, Opera, Chrome and Safari, interpret ISO-8859-1 as Windows-1252).
            // For more information, see http://en.wikipedia.org/wiki/ISO/IEC_8859-1#ISO-8859-1_and_Windows-1252_confusion
            //

            return
                (c < 32 && (!excludeLineBreaks || c != '\n')) ||
                c > 255 ||
                c == 127 ||
                c == 129 ||
                c == 141 ||
                c == 143 ||
                c == 144 ||
                c == 157;
        }
    }
}

Code Description:

The class has only one public method, TruncateText(), which you should call to truncate the text. For performance reasons, I cache the width of the letters so that I don't need to call TextRenderer.MeasureText() again. This was done for good reasons as TextRenderer.MeasureText() can be really slow (in my testing, this change cut the truncation time for a million strings from 8 minutes to 2 seconds!).

Because I chose to cache the width of the letters, I had to limit the character set to ISO-8859-1 (for Latin based languages) which works well for my own purposes. If you need this code to work with Unicode, you'll need to remove the letter width caching mechanism and call TextRenderer.MeasureText() every time you want to measure the text width (which will slow things down a bit, but shouldn't be noticeable unless you plan to use this on a very high traffic website and on not-fast-enough machines, you'll have to do your own testing to be sure). You could also change the letter width caching mechanism to use a dictionary instead of an array and only add the letters to the dictionary when they are actually used.

You may notice that in TruncateText(), instead of passing a font object, I pass the font name, size in pixels and whether it's bold. I do this for convenience reasons, but this should be fairly easy to change if you want to pass a Font object (probably, to also use other font styles like italic, but you'll also need to change the caching mechanism of the letter width).

Note: You'll need to add references to the System.Windows.Forms and System.Drawing assemblies in your project in order for this code to work.

Example:

In your .aspx page
<%= TextTruncator.TruncateText("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut", 300, "Verdana", 12) %>
Of course in real use, the text will most likely come from your database, an xml file or another source.


A Note About Web Browsers

In many discussions on the web, you'll read that you shouldn't depend on the text width in pixels as there's no guarantee that different browsers will display the text in the same way (i.e. the width of the text may differ from one browser to another). While it's true that there's no guarantee, in my real life testing in all popular browsers (and less popular ones), I found that in all browsers the width of the text as displayed in the browser never exceeded the maximum width I specified for the text in TruncateText(), sometimes it was slightly smaller (by only a few pixels - that was in the older versions of Safari on Windows), but again it was never larger. If you are paranoid, like me, always use a 'safety margin'. For example, if the width of the container of the text (div, p, td or whatever element) is 500px, make the maximum width (that's passed to TruncateText()) less by 10px or 20px, that's 490 or 480.


Download code and demo project

Monday, August 30, 2010

Using Visual Studio Macros for Code Generation

Visual Studio Macros are great for automating repetitive tasks and can be real time savers. One thing I esp. like to use them for is code generation.

For example, this macro generates the code for C# class properties (I actually prefer this to using automatic properties as I don't have to change the code later):

Sub CreateProperties()

    Dim textSelection As EnvDTE.TextSelection
    textSelection = CType(DTE.ActiveDocument.Selection(), EnvDTE.TextSelection)

    Dim outputText As String
    Dim regExpression As New Regex("\s*(static)*\s*(\S+)\s*([a-zA-Z0-9_]+)", RegexOptions.Compiled)
    For Each line As String In textSelection.Text.Split(New Char() {vbNewLine})
    Dim m As Match = regExpression.Match(line)
        If m.Success Then
            Dim staticModifier As String = m.Groups(1).Value
            Dim propertyDataType As String = m.Groups(2).Value
            Dim propertyName As String = m.Groups(3).Value.Substring(0, 1).ToLower() & m.Groups(3).Value.Remove(0, 1)
            outputText &= String.Format("private {4}{0} _{1};{3}public {4}{0} {2}{3}{{{3}get{3}{{{3}return _{1};{3}}}{3}}}{3}{3}", _
            propertyDataType, _
            propertyName, _
            propertyName.Substring(0, 1).ToUpper() & propertyName.Remove(0, 1), _
            Environment.NewLine, _
            IIf(staticModifier = "", "", staticModifier & " "))
        Else
            If Not Regex.IsMatch(line, "^\s*$") Then
                MsgBox("Input mismatch")
                Exit Sub
            End If
        End If
    Next

    textSelection.Insert(outputText, 0)
    textSelection.SmartFormat()

End Sub

To use this macro, you type the data type of the property and its name (you can also use the static modifier), like this:

int count
string name
string description
static int globalCount

Then, you select the code and run the macro (most conveniently, using a keyboard shortcut), to get this code:

private int _count;
public int Count
{
    get
    {
        return _count;
    }
}

private string _name;
public string Name
{
    get
    {
        return _name;
    }
}

private string _description;
public string Description
{
    get
    {
        return _description;
    }
}

private static int _globalCount;
public static int GlobalCount
{
    get
    {
        return _globalCount;
    }
}

This is only a very simple use of VS Macros to demonstrate how they can be used for code generation. They are very powerful and can do much more complex things. If you do a lot of coding, it's certainly worth the time to learn more about this very powerful, yet underused, feature of Visual Studio.

Happy coding!

Saturday, August 8, 2009

How to change the background color of web pages in your browser

If you spend too much time browsing the web on your computer, you may start to notice that you're getting too much darkness around your eyes. It's because of this white background color on most web pages, it's slowly turning you into a raccoon. Luckily, this can be changed.

Most web browsers allow you to control the colors of web pages. Here's how to change the background and text colors in all the popular browsers:

Internet Explorer
  1. On the menu, go to Tools > Internet Options
  2. On the Internet Options dialog box, click the Colors button (at the bottom left corner).
  3. Uncheck the Use Windows colors check box, then change the text and background colors to the colors you want (I use black for the background and grey for the text), then click OK.
  4. Next, click the Accessibility button (this time at the bottom right corner).
  5. Check the Ignore colors specified on Web pages check box and click OK.
  6. Click OK on the Internet Options dialog box to close it and apply the new settings.
FireFox
  1. On the menu, go to Tools > Options
  2. On the Options dialog box, click the Content tab (at the top)
  3. Now, click the Colors button.
  4. Uncheck the Use system colors check box and select the colors you want for the background and text (black and grey for example)
  5. Uncheck the Allow pages to choose their own colors, instead of my selections above check box
  6. Click OK to close the Colors dialog box.
  7. Click OK to close the Options dialog box and apply the new settings
Opera

It's a little trickier with Opera but fortunately it's still possible.
  1. Create a new text file (using Notepad or your favorite text editor) and copy and paste the code below into it, save the file anywhere you want but remember where you saved it (you may name the file opera.css, but this is optional, you can give it any other name you like)
    *
    {
        background: transparent !important;
        color: #C0C0C0 !important;
        border-color: #C0C0C0 !important;
    }
    html, body
    {
        background: #000 !important;
    }
    a:link, a:hover, a:active
    {
        color: #00E !important;
    }
    a:visited
    {
        color: #551A8B !important;
    }
    
    
  2. Now in Opera, on the menu, go to Tools > Preferences
  3. Click the Advanced tab
  4. On the left side, click Content
  5. Click the Style Options button
  6. On the Style Options dialog box, under My style sheet, select the file you created in step #1 (you'll replace the value already in there but this is ok)
  7. Now click the Presentation Modes tab (also on the Style Options dialog box).
  8. By default, you should only have The Page style sheet and Page fonts and colors check boxes checked under both the Author mode and User mode (left and right), you should also have Default mode set to Author mode (the drop down list at the bottom right). Now, under the Author mode on the left, check the My style sheet check box and click OK.
  9. Click OK to close the Preferences dialog box and apply the new settings
Google Chrome

Unfortunately for Google Chrome, you can't control the page background or text colors (this is true as to the latest version at the time of writing this post, the version I have here is 2.0.172.39 which is the latest version). The only solution I found was to use a bookmarklet, this is a short piece of code that you add to your bookmarks, you click it everytime you want to change the colors of the page (actually you can also paste it into your browser's address bar then press Enter to make it work). This is not really practical but it's all what you've got for now, hope google will fix this soon. I created this little bookmarklet for you which will change the background color to black and the text color to light grey (copy the code and paste it into the address bar in Google Chrome then press Enter, you should add it to your bookmarks to make it easier to change the color of the page by only clicking it):

javascript:(function(){var allElements=document.getElementsByTagName("*");for(var i=0;i<allElements.length;i++){var element=allElements[i];element.style.background="#000 !important";element.style.color="#C0C0C0 !important";element.style.borderColor="#C0C0C0 !important";if(element.tagName.toLowerCase()=="a"){element.style.color="#00E !important";}}})()

Update

Google Chrome now supports user stylesheets. This means you don't need to use any bookmarklets. Assuming you use Windows, change the page colors by going to "C:\Documents and Settings\{your windows user}\Local Settings\Application Data\Google\Chrome\User Data\Default\User StyleSheets" (change the drive letter if you have Windows installed on a drive other than C). Now, open the Custom.css file then copy and paste the same code above used with Opera and save the file.

You may also now use one of many Google Chrome extensions that allow you to control the page colors. For example, see the Change Colors extension.

Friday, February 13, 2009

How to make a textbox and a textarea same width across browsers

Update: It's been a long while since I've written this post. For a better way to solve this problem, please see http://www.w3schools.com/cssref/css3_pr_box-sizing.asp (http://caniuse.com/#search=box-sizing)

Thanks to the *brilliant* CSS box model, very simple tasks have become a nightmare.

I've been struggling lately to do a very simple thing, which is to make a textbox (ie. <input type="text">) and a textarea same width so that they align nicely.

The problem with making a textbox and textarea same width is that all browsers add 1px padding to textboxes that can't be removed. In other words, setting the padding to 0px won't remove that 1px padding. A common trick to get rid of padding is to float the element but unluckily this doesn't work with textboxes. Well, this means we have extra 2px in the width of the textbox that we simply can't get rid of. Now, this wouldn't be a problem if the same thing applied to textreas as well but unfortunately this is not the case. In most browsers (IE, Safari and Chrome), setting the padding of a textrea to 0px *unfortunately* works. One exception to this is FireFox which applies the 1px non-removable padding to textareas as well. This is why the problem doesn't exist with FireFox. But, should we ignore IE, Safari and Chrome and assume everyone is using FireFox? I guess not, it definitely makes more sense to make it work in those browsers and find a hack for FireFox and actually this is what I did.

Having had a look on mozilla's website, I came across those two extensions to CSS:
-moz-padding-start and -moz-padding-end.

So, to make a textbox and a textrea same width across browsers you can use this code:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<style type="text/css">
*
{
    padding: 0px;
    margin: 0px;
}
#textbox
{
    width: 500px;
    -moz-padding-start: 1px;
    -moz-padding-end: 1px;
}
#textarea
{
    width: 500px;
    padding: 1px;
}
</style>
</head>
<body>
<input type="text" id="textbox" /><br />
<textarea id="textarea"></textarea>
</body>
</html>

Adding 1px to the textrea makes the text look nicer than with no padding at all (with 0px padding, the text sticks to the border which is not visually appealing). In FireFox, we will have 2px padding for the textarea and 2px left and right padding for the textbox, but, visually speaking, it almost looks the same (try to view the code in FireFox).

This code was tested in IE, Safari, Chrome, FireFox and SeaMonkey (which uses the same rendering engine as FF) and worked perfectly. Note that this hack is not perfect. For example, Opera will display the textarea 2px wider than the textbox. For me I don't care about Opera very much (not so many people use it), besides it's only 2px, so, it's not something that will make your site unusable. There could probably be a hack for Opera too but again I'm not interested in Opera, so for me it's not worth the time. It probably won't work with other less popular browsers too. I tested it in the ones I care most about (2 extra pixels in a not popular browser is not a big deal. I can live with that).

In terms of rendering engines (aka layout engines), this will work with Trident based browsers (Internet Explorer), WebKit based browsers (Safari, Google Chrome and other browsers) and Gecko based browsers (FireFox, SeaMonkey and other browsers).

Please note that this won't work if the visual styles are not enabled in Windows XP or Vista (with the exception of FireFox and the other browsers that are based on the same engine), but as very few people (almost none) disable the visual styles so I'm not so worried about that one too.

Finally, if you're a control freak and want it to work in every browser then your last resort is javascript (I was working on that but gave up on it after I came up with this simple hack), but even this won't work if javascript is disabled in the browser.

Saturday, December 27, 2008

How to align checkboxes and their labels consistently across browsers

I was trying today to get a checkbox and its label to look consistent across browsers and finally came up with this solution. I tested it in IE6, FF2 and Chrome and it rendered same pixel by pixel in all the three browsers (also browsershots.org confirmed the result for other browsers). Hopefully this will save time for someone else.

<style type="text/css">
*
{
    padding: 0px;
    margin: 0px;
}
#wb
{
    width: 15px;
    height: 15px;
    float: left;
}
#somelabel
{
    float: left;
    padding-left: 3px;
}
</style>

<div>
<input id="wb" type="checkbox" /><label for="wb" id="somelabel">Web Browser</label>
</div>

* If you don't want to set the padding and margin to zero for all elements as I did here, you could only set them for the label and checkbox.

Wednesday, October 15, 2008

A problem when setting the page title dynamically in ASP.NET

Today I encountered a weird problem with ASP.NET. What I wanted to do was really simple. I wanted to change the page title dynamically in code, that's all. In my scenario I had a page that inherits from a basepage class. In that base page class I append the site name to the page title, e.g. if the page title is 'page title' (without the quotes) and the site name is 'mysite.com', I add the site name to the page title so that it becomes 'page title - mysite.com'

In my page, I overrode OnLoad and set the page title in there, but for an unknown reason I always got an empty string for the page title in the basepage class. So, I created a test page, overrode OnLoad() and set the page title in there, surprisingly that worked. I couldn't see any difference between the two pages, I commented all my code and still got a blank title in the basepage class. Finally, I noticed that the only difference between the two pages is that in my page, the title is set to an empty string in the page directive (ie. Title=""). While in the test page, the title is set to the default value (which is "Untitled Page"). By not keeping the title set to an empty string (just put anything in there) it finally worked.

This seems like a bug, I doubt this behavior is by design. Anyway, to sum up. If you want to set the page title dynamically in your code, make sure you have a "non empty string" for the page title, i.e., if you have Title="" in your page directive, replace it with any other value, e.g. Title="some value" (even though you're going to change this value), otherwise, it will not work.