Adding Compact Framework Design-Time Attributes, or More Fun With TextBoxes

A common user-interface feature is to select the contents of a text-box when the textbox gets focus. The CF textbox control doesn't do this by default but it's not difficult to add with managed code. While I'm at it, I'll add a SelectTextOnFocus property to the textbox's property window so the developer can determine at design-time whether or not to use this functionality.

The code is trivial: I simply override the OnGotFocus() method and add a call to SelectAll(). However, there is one interesting point: the call to SelectAll() must come after the call to the base class's OnGotFocus() method. Otherwise, the base class will undo the selection.

 
    public class DerivedTextBox : TextBox
    {
        protected override void OnGotFocus(EventArgs e)
        {
            base.OnGotFocus(e);
            this.SelectAll();
        }
    }

Adding the SelectAllOnFocus property is trivial as well.

 
    public class DerivedTextBox : TextBox
    {
        protected override void OnGotFocus(EventArgs e)
        {
            base.OnGotFocus(e);
            if (this.selectAllOnFocus)
            {
                this.SelectAll();
            }
        }

        private bool selectAllOnFocus = false;
        public bool SelectAllOnFocus
        {
            get { return this.selectAllOnFocus; }
            set { this.selectAllOnFocus = value; }
        }
    }

Unfortunately, adding design-time properties for the Compact Framework is not. (I could devote an entire article to the things I dislike about the Compact Framework designer support, probably entitled Compact Should NOT Mean More Difficult).

In the full .NET framework, design-time behavior is added by decorating properties with design-time attributes. For instance, to make the property appear in the Properties window, the Browsable attribute is added to the property like this:

 
        [Browsable(true)]
        public bool SelectAllOnFocus
        {
            get { return this.selectAllOnFocus; }
            set { this.selectAllOnFocus = value; }
        }

I like this because it keeps everything I need to know about a property in a single place. However, the classes that support the design-time attributes were removed from the Compact Framework because design-time work is not done on devices. As a result, the code above would no longer compile. This meant that an alternate method was needed for specifying design-time attributes for device applications. The solution chosen was to put the attributes in...drum roll, please...a separate XML file. And, naturally, a new XML file means a new schema (XMTA). For purposes of this article, I will use the term 'XMTA file' to refer to this type of file.

There are two ways that I know of to create an XMTA file. The first is described in Walkthrough: Adding a Simple Attribute to a User Control. Since there is no documentation (other than %vsinstalldir%\Xml\Schemas\xmta.xsd) describing the schema, this was how I created my first XMTA file. The second method is to edit the XMTA file directly in the Visual Studio editor. I prefer this method because Intellisense can give some guidance as to what tags are valid. To do this, I

  1. create an XML file with a .xmta extension

  2. add the following boiler-plate code:

    
        <?xml version="1.0" encoding="utf-16"?>
        <Classes xmlns="https://schemas.microsoft.com/VisualStudio/2004/03/SmartDevices/XMTA.xsd">
          <Class Name="MyNamespace.MyClass">
            <Property Name="MyProperty">
              <!-- TODO: enter attributes -->
            </Property>
          </Class>
        </Classes>
    
  3. change the Class and Property names to match those in my project

  4. type an open bracket (<) under the TODO comment and Intellisense will display a list of attributes that can be applied to the property.

After adding the Browsable attribute to my SelectAllOnFocus property, the XMTA file looks like this:

 
    <?xml version="1.0" encoding="utf-16"?>
    <Classes xmlns="https://schemas.microsoft.com/VisualStudio/2004/03/SmartDevices/XMTA.xsd">
      <Class Name="DeviceApplication1.DerivedTextBox">
        <Property Name="SelectAllOnFocus">
          <Browsable>true</Browsable>
        </Property>
      </Class>
    </Classes>

After I compile my project, return to the design window, and drag a new DerivedTextBox object onto my form, I should see the SelectAllOnFocus property in the control's property window.

If I view the properties in categories, I'll see that SelectAllOnFocus is listed in the Misc category. It would be nice to have it show up in the Behavior category. This is done by adding a Category attribute to the property in the XMTA file.

 
        <Property Name="SelectAllOnFocus">
          <Browsable>true</Browsable>

           <Category>Behavior</Category> 

        </Property>

Adding a Description attribute will cause descriptive text to be displayed below the Properties list.

 
        <Property Name="SelectAllOnFocus">
          <Browsable>true</Browsable>
          <Category>Behavior</Category>

           <Description>Determines whether the contents of the control should be selected when focus is moved to the control.</Description> 

        </Property>

Finally, the default value for the property can be specified by adding a DefaultValue attribute. This attribute requires that the value and its type be specified by inner tags:

 
        <Property Name="SelectAllOnFocus">
          <Browsable>true</Browsable>
          <Category>Behavior</Category>
          <Description>Determines whether the contents of the control should be selected when focus is moved to the control.</Description>

           <DefaultValue><br>            <Type>bool</Type><br>            <Value>false</Value><br>          </DefaultValue> 

        </Property>

Though the process of adding my property to the Properties list was more convoluted than I'd like, I can now set the value of the SelectAllOnFocus property at design time and Visual Studio will generate code like this to initialize it.

 
        private void InitializeComponent()
        {
          ...
            this.derivedTextBox1 = new DeviceApplication1.DerivedTextBox();
          ...
            // 
            // derivedTextBox1
            // 
            this.derivedTextBox1.Location = new System.Drawing.Point(33, 109);
            this.derivedTextBox1.Name = "derivedTextBox1";

            this.derivedTextBox1.SelectAllOnFocus = false; 

            this.derivedTextBox1.Size = new System.Drawing.Size(100, 21);
            this.derivedTextBox1.TabIndex = 1;
            this.derivedTextBox1.Text = "derivedTextBox1";
          ...
            this.Controls.Add(this.derivedTextBox1);
          ...
        }

I hope this takes a little of the mystery out of adding design-time support to Compact Framework controls.

Cheers
Dan

Disclaimer: This posting is provided "AS IS" with no warranties, and confers no rights.