Saturday, October 10, 2009

Programmers are Tiny Gods

Derek Powazek says Programmers are Tiny Gods. Maybe this explains why some people call me god? =P

Rasterizing a vector brush for fast scaling animation

When animating (scaling) a complex WPF vector brush, 100% of my CPU is used. The animation also looks jerky. To speed things up, I rasterized the vector brush into a bitmap brush. The CPU load decreases below 30% and the animation became much smoother.

So how do I create a bitmap brush? There is no meaningful properties or methods to override in the Brush class, as most of the workings of brush are marked as internal. To overcome the problem, the brush is implemented as a markup extension.

To use the code, pass your vector brush into the RasterizeBrush class.

<Button
    x:Name="helloButton"
    Background="{app:RasterizeBrush {StaticResource HelloBrush}}"
    >
        <TextBlock>Hello</TextBlock>
</Button>

And place the following code inside your project.

/-----------------------------------------------------------------------
// <copyright file="RasterizeBrushExtension.cs" company="Jeow Li Huan">
// Copyright (c) Jeow Li Huan. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
 
namespace Huan.Windows.Markup
{
    using System;
    using System.Windows;
    using System.Windows.Markup;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Shapes;
 
    /// <summary>
    /// Converts a vector brush into a bitmap brush to speed up scaling.
    /// </summary>
    [MarkupExtensionReturnType(typeof(ImageBrush))]
    public class RasterizeBrushExtension : MarkupExtension
    {
        /// <summary>
        /// Object used to synchronize access to the static properties <see cref="P:DefaultWidth"/> and <see cref="P:DefaultHeight"/>.
        /// </summary>
        private static object sync = new object();
 
        /// <summary>
        /// Backing field for the <see cref="P:DefaultWidth"/> property.
        /// </summary>
        private static int defaultWidth = 64;
 
        /// <summary>
        /// Backing field for the <see cref="P:DefaultHeight"/> property.
        /// </summary>
        private static int defaultHeight = 64;
 
        /// <summary>
        /// Backing field for the <see cref="P:OriginalBrush"/> property.
        /// </summary>
        private Brush originalBrush;
 
        /// <summary>
        /// The converted bitmap brush.
        /// </summary>
        private ImageBrush rasteredBrush;
 
        /// <summary>
        /// Backing field for the <see cref="P:Width"/> property.
        /// </summary>
        private int width;
 
        /// <summary>
        /// Backing field for the <see cref="P:Height"/> property.
        /// </summary>
        private int height;
 
        /// <summary>
        /// Initializes a new instance of the <see cref="RasterizeBrushExtension"/> class.
        /// </summary>
        public RasterizeBrushExtension()
        {
        }
 
        /// <summary>
        /// Initializes a new instance of the <see cref="RasterizeBrushExtension"/> class.
        /// </summary>
        /// <param name="originalBrush">The original brush that is to be converted into a bitmap brush.</param>
        public RasterizeBrushExtension(Brush originalBrush)
            : this()
        {
            this.originalBrush = originalBrush;
        }
 
        /// <summary>
        /// Gets or sets the default number of horizontal pixels for the bitmap to render on.
        /// </summary>
        /// <value>The default number of horizontal pixels for the bitmap to render on.</value>
        public static int DefaultWidth
        {
            get
            {
                lock (sync)
                    return defaultWidth;
            }
 
            set
            {
                lock (sync)
                    defaultWidth = value;
            }
        }
 
        /// <summary>
        /// Gets or sets the default number of vertical pixels for the bitmap to render on.
        /// </summary>
        /// <value>The default number of vertical pixels for the bitmap to render on.</value>
        public static int DefaultHeight
        {
            get
            {
                lock (sync)
                    return defaultHeight;
            }
 
            set
            {
                lock (sync)
                    defaultHeight = value;
            }
        }
 
        /// <summary>
        /// Gets or sets the original brush that is to be converted into a bitmap brush.
        /// </summary>
        /// <value>The original brush that is to be converted into a bitmap brush.</value>
        [ConstructorArgument("originalBrush")]
        public Brush OriginalBrush
        {
            get
            {
                return this.originalBrush;
            }
 
            set
            {
                if (this.originalBrush != value)
                {
                    this.rasteredBrush = null;
                    this.originalBrush = value;
                }
            }
        }
 
        /// <summary>
        /// Gets or sets the number of horizontal pixels for the bitmap to render on.
        /// </summary>
        /// <value>The number of horizontal pixels for the bitmap to render on.</value>
        public int Width
        {
            get
            {
                return this.width;
            }
 
            set
            {
                ifthis.width != value)
                {
                    this.rasteredBrush = null;
                    this.width = value;
                }
            }
        }
 
        /// <summary>
        /// Gets or sets the number of vertical pixels for the bitmap to render on.
        /// </summary>
        /// <value>The number of vertical pixels for the bitmap to render on.</value>
        public int Height
        {
            get
            {
                return this.height;
            }
 
            set
            {
                if (this.height != value)
                {
                    this.rasteredBrush = null;
                    this.height = value;
                }
            }
        }
 
        /// <summary>
        /// Returns the converted bitmap brush.
        /// </summary>
        /// <param name="serviceProvider">Not used.</param>
        /// <returns>
        /// The converted bitmap brush.
        /// </returns>
        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            int width = (this.width == 0) ? RasterizeBrushExtension.defaultWidth : this.width;
            int height = (this.height == 0) ? RasterizeBrushExtension.defaultHeight : this.height;
            if (this.originalBrush == null || width == 0 || height == 0)
                return null;
 
            if (this.rasteredBrush == null)
            {
                RenderTargetBitmap targetBitmap = new RenderTargetBitmap(width, height, 96.0, 96.0, PixelFormats.Default);
                Rectangle rectangle = new Rectangle();
                rectangle.Width = width;
                rectangle.Height = height;
                rectangle.Fill = this.originalBrush;
 
                rectangle.Measure(new Size(width, height));
                rectangle.Arrange(new Rect(0, 0, width, height));
 
                targetBitmap.Render(rectangle);
                targetBitmap.Freeze();
                this.rasteredBrush = new ImageBrush(targetBitmap);
 
                TileBrush tileBrush = this.originalBrush as TileBrush;
                if (tileBrush != null)
                {
                    this.rasteredBrush.AlignmentX = tileBrush.AlignmentX;
                    this.rasteredBrush.AlignmentY = tileBrush.AlignmentY;
                    this.rasteredBrush.Stretch = tileBrush.Stretch;
                    this.rasteredBrush.TileMode = tileBrush.TileMode;
                    this.rasteredBrush.Viewbox = tileBrush.Viewbox;
                    this.rasteredBrush.ViewboxUnits = tileBrush.ViewboxUnits;
                    this.rasteredBrush.Viewport = tileBrush.Viewport;
                    this.rasteredBrush.ViewportUnits = tileBrush.ViewportUnits;
                }
            }
 
            return this.rasteredBrush;
        }
    }
}

Wednesday, September 23, 2009

Creating Shortcut to Application Inside Windows XP Mode

The easiest way to get a Windows 7 shortcut to an application inside XP Mode is to load up the Virtual Machine and within the guest Windows XP, create a shortcut in C:\Documents and Settings\All Users\Start Menu.

This method can be used to create shortcut for Internet Explorer 6 and Outlook Express. However, when I use the same method to create a shortcut to Pinball Space Cadet, the Windows 7 shortcut isn’t created.

I found out that there is a manual way to create shortcut. The steps are as follows

  1. In Windows XP, create the registry entries for the Terminal Services Application Allowed List
  2. In Windows 7, create a shortcut to the application in XP Mode.

Creating the registry entries

I will attempt to create a shortcut to Pinball. The following is the registry entry I created.

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Terminal Server\TsAppAllowList\Applications\1234567]
"CommandLineSetting"=dword:00000000
"IconIndex"=dword:00000000
"IconPath"="%SYSTEMDRIVE%\\Program Files\\Windows NT\\Pinball\\PINBALL.EXE"
"Name"="Pinball"
"Path"="C:\\Program Files\\Windows NT\\Pinball\\PINBALL.EXE"
"RequiredCommandLine"=""
"ShortPath"="C:\\PROGRA~1\\WINDOW~1\\Pinball\\PINBALL.EXE"
"ShowInTSWA"=dword:00000000
"VPath"="%SYSTEMDRIVE%\\Program Files\\Windows NT\\Pinball\\PINBALL.EXE"

image

The “1234567” part in [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Terminal Server\TsAppAllowList\Applications\1234567] is some random numbers that I had come up with. This number will be used later in the Windows 7 shortcut.

The "CommandLineSetting"=dword:00000000 "IconIndex"=dword:00000000 "RequiredCommandLine"="" "ShowInTSWA"=dword:00000000 are some default values that I have copied over from the 5664112 entry, which is the entry to Internet Explorer 6.

"IconPath"="%SYSTEMDRIVE%\\Program Files\\Windows NT\\Pinball\\PINBALL.EXE" determines what icon will be used. Windows 7 will extract the icon at this location and store it in Windows 7’s folder at %USERPROFILE%\AppData\Local\Microsoft\Windows Virtual PC\Virtual Applications\Windows XP Mode

"Name"="Pinball" is used for naming the icon that is extracted in the previous step. It will be used in the Windows 7 shortcut later.

"Path"="C:\\Program Files\\Windows NT\\Pinball\\PINBALL.EXE" and "VPath"="%SYSTEMDRIVE%\\Program Files\\Windows NT\\Pinball\\PINBALL.EXE" are the path to Pinball.

"ShortPath"="C:\\PROGRA~1\\WINDOW~1\\Pinball\\PINBALL.EXE" is the 8.3 path to Pinball.

We can get the 8.3 filenames step-by-step. To get the 8.3 filename of c:\Program files, type cd \ dir "Program files*" /x You will see something similar to Volume in drive C has no label. Volume Serial Number is 24FE-A31E

Directory of C:\

07/26/2009 05:57 PM <DIR> PROGRA~1 Program Files 0 File(s) 0 bytes 1 Dir(s) 134,463,721,472 bytes free

PROGRA~1 is hence the 8.3 filename for Program files.

image

Creating the Windows 7 shortcut

Create a shortcut in C:\Users\<username>\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Windows Virtual PC

image

Set the Target to %SystemRoot%\system32\rundll32.exe %SystemRoot%\system32\VMCPropertyHandler.dll,LaunchVMSal "Windows XP Mode" "||1234567" "Pinball"

"||1234567" "Pinball" correspond to the random number and the Name that I have specified in the Create the registry entries step.

Tuesday, September 22, 2009

Office 2010 CTP compatibility with Nokia PC Suite and Visual Studio 2008

I use the Nokia PC Sync feature to get my handphone calendar and Outlook calendar in sync.

A clean install of Windows 7 Professional, Office 2010 64-bit edition and Nokia PC Suite 7.1.30.9 didn’t work out well—the PC Sync could not detect Outlook and thus cannot synchronize.

Apparently, Nokia PC Sync can only detect the 32-bit version of Office 2010. So I uninstalled the 64-bit version and installed the 32-bit version and everything works fine.

Then I installed Visual Studio 2008 Team Suite. It hung when I load up the designer for WebForms. Editing the ASPX file is OK though.

The solution I found was to reinstall

C:\Program Files (x86)\Common Files\microsoft shared\OFFICE12\Office Setup Controller\Setup.exe

There are incompatible programs that I have not found a solution to. Office Live Workspace Addin cannot be installed and Acrobat Professional Addin needs to be disabled or it crashes Office. Please comment if you have the solution. Thanks!

Monday, September 21, 2009

Getting Chinese Handwriting Recognition on Windows 7 Professional

Installing Language Pack

To have Chinese Handwriting Recognition, the Chinese Language Pack needs to be installed. According to Microsoft Help and Support, Windows 7 language packs are available for computers that are running Windows 7 Ultimate. Implicitly, it means that other editions of Windows do not have language packs.

To verify that, the instruction from Windows 7 Center showed the following UI in Control Panel>Regional and Language that can be used to install additional language packs.

image

However, as I am running Professional Edition, there is no Display language group.

image

Does this really mean I can’t get language pack into my Windows 7 Professional? My previous experience says that it is possible to turn Windows XP into Windows Server 2000, and also enable RAID-5 on Windows XP Pro. There must be some way to get language pack.

Sure enough, I discovered that there is another way for me to install a language pack. On WinMatrix forum, the instruction given is as follows

MUI can be also installed on Professional edition avaliable at MSDNAA: Run CMD as administrator and type: DISM /Online /Add-Package /PackagePath:(path to lp) then: bcdedit /set {current} locale (your locale) and: bcdboot %WinDir% /l (your locale) Then in registry: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\MUI\UILanguages remove key en-US Reboot and it's done

To get Simplified Chinese and Traditional Chinese language pack into my system, I typed for the first part of the instruction

DISM /Online /Add-Package /PackagePath:D:\langpacks\zh-cn\lp.cab

and

DISM /Online /Add-Package /PackagePath:D:\langpacks\zh-hk\lp.cab

image

Installing Handwriting Recognition

Go to Control Panel>Regional and Language

Click on Change keyboards…

Click on Add…

Check Chinese (Simplified) – Microsoft Pinyin New Experience Input St and Chinese (Traditional) – New Phonetic in the following dialog

image

Once added, you will see the following dialog

image

and on your task bar, you will see a new EN button added

image

Turning the Input Panel on and switching to Chinese, I can start writing.

image

Edit: The language pack seems to un-install itself. Anyone knows how to get it to stay?

Edit (April 25, 2010): Read the comments for the method to stop the "uninstall".

Edit (March 27, 2010): I managed to get both English and Simplified Chinese to stay installed some time ago. The method will most likely not work with more than two language packs though.

  1. We need to continue with the next step of deleting registry keys as mentioned in the WinMatrix forum. So launch the registry editor, navigate to HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlMUIUILanguages and delete the "en-US"
  2. Restart computer. Now, everything should be displayed in the language you installed previously.
  3. Install the en-US language pack using DISM. Launch the registry editor, navigate to HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlMUIUILanguages , export the registry entry for the language you had installed before restarting and delete the registry entry.
  4. Restart computer. Now, everything should be in English again.
  5. Import the registry entry back.
  6. The two language packs should stay even after restarting.

PS: Although this method seems to work, on examining the registry entries that are changed, they are in a wreck, with some settings referring to en-US and others zh-CN. So far, I did not notice any consequence. Try it at your own risk!

Wednesday, July 01, 2009

Virtual Box Seamless Mode

Just discovered Virtual Box had seamless mode. Virtual PC has a lot to catch up! The latest version with seamless mode is still in beta… Here, I’m running Fedora 11 in virtual machine, sharing the same desktop as Windows 7 RC. Cool, isn’t it?

Some minor problems though. All the Linux applications, including the panels, appear as a window on Windows, so clicking on any Linux app will bring all Linux app to the foreground. Not too nice. Resizing windows doesn’t seem to work after a while of usage.

I wish Virtual PC can play well with Virtual Box. Launching IE 6 (Virtual Window), with Virtual Box running, froze the computer, requiring a hard reset. It could have given me a warning that I shouldn’t be running 2 VMs at the same time…

Monday, June 29, 2009

LUA bug for Windows 7 XP Mode setup

There are 2 files that need to be installed for XP mode. Installation of Windows6.1-KB958559-x64.msu went OK, but VirtualWindowsXP.msi gave an error – your administrator prevented the installation.

To install correctly, I had to Shift+Right Click on the VirtualWindowsXP.msi, and click Run as a different user.

Once it is installed, clicking on Start>Programs>Windows Virtual PC>Virtual Windows XP will bring up this window.

And after some time, we’ve got XP in the virtual machine.

A differencing disk is used. If your computer has several users using XP mode, this could turn out to be a substantial saving on disk space, since only changes to the base XP image is saved in the each user’s virtual hard disk.

Tuesday, June 23, 2009

ASP.NET Label with DropDownList functions to reference another data source

I often encounter the following situation in ASP.NET when using a DetailsView to display a row that contains a reference to a table

<asp:DetailsView ID="detailsView" runat="server" AutoGenerateRows="False"

    DataSourceID="dataSource" DataKeyNames="Id">

    <Fields>

        <asp:TemplateField HeaderText="Name" SortExpression="Id">

            <EditItemTemplate>

                <asp:DropDownList ID="dropDownList" runat="server"

                    DataSourceID="referenceDataSource" DataTextField="Name" DataValueField="Id"

                    SelectedValue='<%# Bind("Id") %>' />

            </EditItemTemplate>

            <ItemTemplate>

                <asp:Label ID="label" runat="server" Text='<%# Eval(" What do I bind to here? ") %>' />

            </ItemTemplate>

        </asp:TemplateField>

    </Fields>

</asp:DetailsView>

I need the DetailsView to display in 3 modes, namely Insert, Edit and ReadOnly. For Insert and Edit modes, a DropDownList takes care of getting the values from the referenceDataSource. However, in ReadOnly mode, I do not want to introduce the DropDownList, for it would have unnecessarily bloat the ViewState and post-back values.

A label should be all that is needed to display, but what do I type to data bind the label to the referenceDataSource? Most of the time, I end up casting the Eval to a DataRowView, getting the Row, and calling GetParentRow. However, that is not a general solution that can be applied everywhere. By changing the data source to use DataReader instead of DataSet, the code in the label will break.

So I decided to roll out my own server control. Taking reference from Creating a Databound Label Control, I created a label control that takes similar attributes as DropDownList, so that all that is needed is to copy the DropDownList from the EditItemTemplate, paste it in ItemTemplate, and changing the tag to ReferenceLabel.

<asp:DetailsView ID="detailsView" runat="server" AutoGenerateRows="False"

    DataSourceID="dataSource" DataKeyNames="Id">

    <Fields>

        <asp:TemplateField HeaderText="Name" SortExpression="Id">

            <EditItemTemplate>

                <asp:DropDownList ID="dropDownList" runat="server"

                    DataSourceID="referenceDataSource" DataTextField="Name" DataValueField="Id"

                    SelectedValue='<%# Bind("Id") %>' />

            </EditItemTemplate>

            <ItemTemplate>

                <huan:ReferenceLabel ID="label" runat="server"

                    DataSourceID="referenceDataSource" DataTextField="Name" DataValueField="Id"

                    SelectedValue='<%# Bind("Id") %>' />

            </ItemTemplate>

        </asp:TemplateField>

    </Fields>

</asp:DetailsView>

Neat huh? Below is the code for the ReferenceLabel. Place it in App_Code and add the following into Web.config.

<configuration>

  <system.web>

    <pages>

      <controls>

        <add tagPrefix="huan" namespace="Huan.Web.UI.WebControls" />

      </controls>

    </pages>

  </system.web>

</configuration>

//-----------------------------------------------------------------------

// <copyright file="ReferenceLabel.cs" company="Jeow Li Huan">

// Copyright (c) Jeow Li Huan. All rights reserved.

// </copyright>

// <remarks>See <see href="http://aspnet.4guysfromrolla.com/articles/081308-1.aspx">Creating a Databound Label Control</see>

// for original implementation.</remarks>

//-----------------------------------------------------------------------

 

namespace Huan.Web.UI.WebControls

{

    using System;

    using System.Collections;

    using System.ComponentModel;

    using System.Web;

    using System.Web.UI;

    using System.Web.UI.WebControls;

 

    /// <summary>

    /// Represents a label control, which displays text referenced from another data source, on a Web page.

    /// </summary>

    [DataBindingHandler("System.Web.UI.Design.WebControls.ListControlDataBindingHandler, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]

    [ParseChildren(false)]

    [ToolboxData("<{0}:ReferenceLabel runat=\"server\" />")]

    public class ReferenceLabel : DataBoundControl

    {

        /// <summary>

        /// Gets or sets the field of the data source that provides the text content of the list items.

        /// </summary>

        /// <value>

        /// A <see cref="T:System.String"/> that specifies the field of the data source that provides the text content of the list items. The default is <see cref="F:System.String.Empty"/>.

        /// </value>

        [Category("Data")]

        [DefaultValue("")]

        [Description("The field in the data source that provides the text.")]

        [Themeable(false)]

        public virtual string DataTextField

        {

            get

            {

                return (string)this.ViewState["DataTextField"] ?? string.Empty;

            }

 

            set

            {

                this.ViewState["DataTextField"] = value;

                this.OnDataPropertyChanged();

            }

        }

 

        /// <summary>

        /// Gets or sets the formatting string used to control how data bound to the label control is displayed.

        /// </summary>

        /// <value>

        /// The formatting string for data bound to the control. The default value is <see cref="F:System.String.Empty"/>.

        /// </value>

        [Category("Data")]

        [DefaultValue("")]

        [Description("The formatting applied to the text. For example, {0:d}.")]

        [Themeable(false)]

        public virtual string DataTextFormatString

        {

            get

            {

                return (string)this.ViewState["DataTextFormatString"] ?? string.Empty;

            }

 

            set

            {

                this.ViewState["DataTextFormatString"] = value;

                this.OnDataPropertyChanged();

            }

        }

 

        /// <summary>

        /// Gets or sets the field of the data source that provides the value of each

        /// list item.

        /// </summary>

        /// <value>

        /// A <see cref="T:System.String"/> that specifies the field of the data source that provides

        /// the value of each list item. The default is <see cref="System.String.Empty"/>.</value>

        [Category("Data")]

        [DefaultValue("")]

        [Description("The field in the data source which provides the item value.")]

        [Themeable(false)]

        public string DataValueField

        {

            get { return (string)this.ViewState["DataValueField"] ?? string.Empty; }

            set { this.ViewState["DataValueField"] = value; }

        }

 

        /// <summary>

        /// Gets or sets the value of the selected item in the list control.

        /// </summary>

        /// <value>

        /// The value of the selected item in the label control. The default is an empty string ("").

        /// </value>

        [Bindable(true, BindingDirection.TwoWay)]

        [Browsable(false)]

        [DefaultValue("")]

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]

        [Themeable(false)]

        public object SelectedValue

        {

            get { return this.ViewState["SelectedValue"]; }

            set { this.ViewState["SelectedValue"] = value; }

        }

 

        /// <summary>

        /// Gets or sets the identifier for a server control that the <see cref="T:Huan.Web.UI.WebControls.ReferenceLabel"/> control is associated with.

        /// </summary>

        /// <value>

        /// A string value corresponding to the <see cref="P:System.Web.UI.Control.ID"/> for a server control contained in the Web form. The default is an empty string (""), indicating that the <see cref="T:Huan.Web.UI.WebControls.ReferenceLabel"/> control is not associated with another server control.

        /// </value>

        [Category("Accessibility")]

        [DefaultValue("")]

        [Description("The ID of the control associated with the Label.")]

        [IDReferenceProperty]

        [Themeable(false), TypeConverter(typeof(AssociatedControlConverter))]

        public virtual string AssociatedControlID

        {

            get { return (string)this.ViewState["AssociatedControlID"] ?? string.Empty; }

            set { this.ViewState["AssociatedControlID"] = value; }

        }

 

        /// <summary>

        /// Gets the text content of the label.

        /// </summary>

        /// <value>

        /// The text content of the control. The default value is <see cref="System.String.Empty"/>.

        /// </value>

        [Category("Appearance")]

        [DefaultValue("")]

        [Description("The text to be shown for the label.")]

        [Localizable(true)]

        public virtual string Text

        {

            get { return (string)this.ViewState["Text"] ?? string.Empty; }

        }

 

        /// <summary>

        /// Gets the HTML tag that is used to render the label.

        /// </summary>

        /// <value>

        /// The <see cref="T:System.Web.UI.HtmlTextWriterTag"/> value used to render the label.

        /// </value>

        protected override HtmlTextWriterTag TagKey

        {

            get

            {

                if (this.AssociatedControlID.Length != 0)

                {

                    return HtmlTextWriterTag.Label;

                }

 

                return base.TagKey;

            }

        }

 

        /// <summary>

        /// When overridden in a derived class, binds data from the data source to the control.

        /// </summary>

        /// <param name="data">The <see cref="T:System.Collections.IEnumerable"/> list of data returned from a <see cref="M:System.Web.UI.WebControls.DataBoundControl.PerformSelect"/> method call.</param>

        /// <exception cref="T:System.ArgumentOutOfRangeException">

        /// The selected value is not in the list of avaliable values.

        /// </exception>

        protected override void PerformDataBinding(IEnumerable data)

        {

            base.PerformDataBinding(data);

 

            if (this.DesignMode)

            {

                this.SetText(string.IsNullOrEmpty(this.ID) ? "abc" : this.ID);

                return;

            }

 

            if (data != null && this.SelectedValue != null)

            {

                // Clear out the Text property

                this.ClearText();

 

                string formatString = this.DataTextFormatString.Length == 0 ? "{0}" : this.DataTextFormatString;

 

                // Get the DataTextFormatString field value for the FIRST record

                foreach (object obj in data)

                {

                    if (this.DesignMode)

                    {

                        this.SetText(string.Format(formatString, "Databound"));

                        return;

                    }

                    else

                    {

                        bool hasDataValueField = this.DataValueField.Length != 0;

                        object dataValue = hasDataValueField ? DataBinder.GetPropertyValue(obj, this.DataValueField) : obj;

                        if (this.SelectedValue.Equals(dataValue))

                        {

                            bool hasDataTextField = this.DataTextField.Length != 0;

                            if (hasDataTextField)

                                this.SetText(DataBinder.GetPropertyValue(obj, this.DataTextField, formatString));

                            else if (hasDataValueField)

                                this.SetText(DataBinder.GetPropertyValue(obj, this.DataValueField, formatString));

                            else

                                this.SetText(string.Format(formatString, obj.ToString()));

 

                            return;

                        }

                    }

                }

 

                throw new ArgumentOutOfRangeException("SelectedValue", string.Format("'{0}' has a SelectedValue which is invalid because it does not exist in the list of items.", this.ID));

            }

        }

 

        /// <summary>

        /// Adds HTML attributes and styles that need to be rendered to the specified <see cref="T:System.Web.UI.HtmlTextWriter"/> object.

        /// </summary>

        /// <param name="writer">An <see cref="T:System.Web.UI.HtmlTextWriter"/> that represents the output stream that renders HTML contents to the client.</param>

        protected override void AddAttributesToRender(HtmlTextWriter writer)

        {

            if (this.AssociatedControlID.Length != 0)

            {

                Control control = this.FindControl(this.AssociatedControlID);

                if (control == null && !this.DesignMode)

                    throw new HttpException(string.Format("The ReferenceLabel '{0}' cannot find associated control ID '{1}'.", this.ID, this.AssociatedControlID));

                else

                    writer.AddAttribute(HtmlTextWriterAttribute.For, control.ClientID);

            }

 

            base.AddAttributesToRender(writer);

        }

 

        /// <summary>

        /// Renders the items in the <see cref="T:System.Web.UI.WebControls.ListControl"/> control.

        /// </summary>

        /// <param name="writer">The <see cref="T:System.Web.UI.HtmlTextWriter"/> that represents the output stream used to write content to a Web page.</param>

        protected override void RenderContents(HtmlTextWriter writer)

        {

            HttpUtility.HtmlEncode(this.Text, writer);

        }

 

        /// <summary>

        /// Sets the text in the view state.

        /// </summary>

        /// <param name="text">The text to be stored in the view state.</param>

        protected virtual void SetText(string text)

        {

            this.ViewState["Text"] = text;

        }

 

        /// <summary>

        /// Clears the text stored in the view state.

        /// </summary>

        protected virtual void ClearText()

        {

            this.ViewState.Remove("Text");

        }

    }

}

Limitations:

Due to the aim of reducing ViewState, only the initial selected text is stored in the ViewState. Changing the SelectedValue after the control has been data bound will not change the displayed text.