Welcome to MSDN Blogs Sign in | Join | Help

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="http://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="http://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>
            <Type>bool</Type>
            <Value>false</Value>
          </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.

Posted by BlueCollar | 1 Comments

Setting Alignment in TextBoxes...No Looking Glass Required

In yesterday's post I stated that "non-character keys do not trigger KeyPress events." While that is perfectly true, I misunderstood what was meant by a non-character key. In the non-managed Win32 world, pressing a key generates a WM_KEYDOWN, one or more WM_CHAR messages, and a WM_KEYUP message. The managed KeyPress event corresponds to the WM_CHAR message. Windows generates WM_CHAR messages for keys that are mapped to ASCII characters by the keyboard driver. Since pressing the Enter key generates an ASCII character code (0xA, 0xD, or both), a KeyPress event is generated for it.

So, while the code I posted yesterday works, it can be simplified by eating the Enter key in the textbox's KeyPress event handler rather than using the form's KeyPreview.


        private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
        {
            if ('\r' == e.KeyChar)
            {
                // Supress enter key
                e.Handled = true;
            }
        }

In addition, since the textbox is no longer dependent on the form for functionality, I can package it up in it's own class.


    public class TextBoxWithAlignment : TextBox
    {
        // Remember whether user wants the textbox to act like a 
        // single-line or multiline textbox.
        //
        private bool multiline = false;

        public TextBoxWithAlignment()
        {
            // The underlying control will always be a multiline textbox
            //
            base.Multiline = true;
        }

        protected override void OnKeyPress(KeyPressEventArgs e)
        {
            // When the user wants a single-line textbox,
            // eat carriage returns
            //
            if (!this.multiline && '\r' == e.KeyChar)
            {
                e.Handled = true;
            }
            base.OnKeyPress(e);
        }

        // Override the Multiline property to use the internal multiline flag
        //
        public override bool Multiline
        {
            get
            {
                return this.multiline;
            }
            set
            {
                this.multiline = value;
            }
        }
    }

Pretty nifty, IMHO.

Cheers
Dan
Posted by BlueCollar | 0 Comments

Setting Alignment in TextBoxes, or Through the Looking Glass

 

With a couple of exceptions, the UI controls provided by the .NET Compact Framework are thin wrappers around the controls provided by the operating system.  Perhaps I'm too sanguine, but I expect the basic controls (button, check box, radio button, edit control and list box) to have the same capabilities on both Windows and Windows CE.  For the most part, they do; however, there are occasions where the differences are just plain annoying.

 

I came across one of these this morning.  A customer needed a textbox that kept the text on a single line but allowed text alignment to be set.  The Windows CE textbox control only allows text to be left-aligned unless the control is multiline.  I don't know why, but there it is.  Since the  normal Compact Framework textbox is just a thin wrapper around the CE edit control, it met the first criteria, but failed the second.  Setting the textbox's Multiline property to true met the second criteria, but failed the first.  Or did it?

 

I noticed that the TextBox class has a property called AcceptReturn.  At first glance, this appeared to be just what I wanted.  However, I soon discovered that this property didn't allow me to supress line breaks: it determined whether the Enter key is sent to the textbox or the form's default button.  Back to the drawing board.

 

My next thought was to try eating the Enter key in the textbox's KeyPress, KeyDown or KeyUp event handlers.  I soon found that non-character keys do not trigger KeyPress events [see my next post for a revision].  And a quick test showed that though I could detect the Enter key in the KeyDown and KeyUp events, setting  the KeyEventArgs.Handled property to true didn't supress the line break.

 

In between yanking my hair out and beating my head on the wall, I stumbled across the Form.KeyPreview property.  When this property is set to true, all key events are sent to the form before being dispatched to a control.  By setting KeyEventArgs.Handled in the form's KeyUp event handler, I was able to suppress the Enter key and thus the line break.

 

This wasn't an obvious solution, so let me recap:

  1. To allow textbox alignment to be set, the textbox's Multiline property must be set to true.
  2. To keep the text on a single line in a multiline textbox:
    1. Set the form's KeyPreview property to true.  This will route keys events to the form prior to sending them to the control.
    2. Add a handler for the form's KeyUp event.  In this handler, disallow the enter key if the textbox has focus.
       

        private void Form1_KeyUp(object sender, KeyEventArgs e)

        {

            if (this.textBox1.Focused && e.KeyCode == Keys.Enter)

            {

                e.Handled = true;

            }

        }

 

Cheers

Dan

 

[Edited 02/07/2007.  Added link to next article.]

Posted by BlueCollar | 1 Comments

Attachment(s): TextBoxWithAlignment.zip

Using a hardware key to tab through controls

The question of using a hardware button to tab through the controls on a form came up on the Compact Framework forum.  The answer, while simple, still had a couple of gotcha's, so I thought I'd give you the benefit of my experience. 

If you're not familiar with the HardwareButton control (Microsoft.WindowsCE.Forms.HardwareButton), it's a control that allows an application to override the functionality of Pocket PC directional pad (D-Pad) buttons by sending KeyUp and KeyDown events to the active control when one of the buttons is pressed.  The KeyDown/KeyUp events are the same as those used by the keyboard with the exception that the KeyEventArgs.KeyCode field may only contain one of five values:  Keys.Up, Keys.Down, Keys.Left, Keys.Right, or Keys.Enter.

To tab through the controls on a form, we need to set focus to the next control in the tab order when one of the D-Pad keys is pressed.  For this example we'll use the Enter button (usually the large button in the middle of the D-Pad).   First create a form with several controls and a HardwareButton control.  Add the following handler to the form:


        private void control_KeyUp(object sender, KeyEventArgs e)
        {
            if ((e.KeyCode == System.Windows.Forms.Keys.Enter))
            {
                Control control = sender as Control;
                if (null != control)
                {
                    bool result = control.Parent.SelectNextControl(control, true, true, true, true);
                }
            }
        }

Finally, set the KeyUp event of each control to the handler.  This was my first point of confusion.  I originally expected that the KeyUp events would be routed to the form.  That is not the case: they are routed to the active control.  My second point of confusion was the correct use of the SelectNextControl method.  I originally called it like this:

bool result = control.SelectNextControl(control, true, true, true, true);

 

However, focus remained on the same control.  I was confused until I found Subhag Oak's article.  SelectNextControl does not find the next sibling of a control as I expected.  Instead it finds the next child control in tab order of the control that calls it.  (One could wish that it had been named SelectNextChildControl, but c'est la vie).   In order to find the next sibling of the current control, SelectNextControl must be called by the current control's parent, using the current control as the starting point of the search.  SelectNextControl's other parameters allow you to set the direction of the search, search all controls or only those with the TabStop property set to true, whether to search nested controls, and whether the search should wrap when the end of the Controls collection is reached.

I hope this saves someone out there some time.

Cheers
Dan

Posted by BlueCollar | 1 Comments

A Trip Down Memory Lane

While waiting for Visual Studio to repair whatever was causing deploys to the emulator to fail, I came across this post containing images of Windows boot screens over time.  I know I'm giving my age away, but the only one I don't remember is the first.  If you're interested you can find a few more here.  For those of you who think Windows 95 was the first version of Windows, take a look at the Windows 2.0 UI.  Imagine...Windows and applications running in 640K.  Of course, we didn't have Solitaire then either.  One of the first things I remember doing after coming to work for MS was running Excel in Windows.  The experience was unique because you typed excel.exe at the command prompt and it booted Windows 2.11 prior to starting Excel. 

Good times, good times.

Posted by BlueCollar | 0 Comments

Who'da thunk it? #1

This is the first in an ongoing series on the non-obvious (to me, at least).

This question is from an internal mailing list. Should the following C# code compile?

    
using System;
using System.Collections.Generic;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            goto exit;
            exit:
        }
    }
}
    
    

My intial reaction was "Of course!" However, I was wrong. The compiler reports the following errors:
Error 1 Invalid expression term '}' line 13 column 9
Error 2 ; expected line 13 column 10

According to section 8.9.3 of the C# spec, "the target of a goto identifier statement is the labeled statement with a given label." Note that the target is a labeled statement, not an identifier. Since the smallest statement that qualifies as a labeled statement is the empty statement (a semicolon by itself), the label must be followed by at least a semi-colon to compile.  So the correct code would be: 

    
using System;
using System.Collections.Generic;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            goto exit;
            exit:;
        }
    }
}
    
    

Who'da thunk it?
~Dan

Posted by BlueCollar | 0 Comments

Compact Framework OpCode Support in a Nutshell

While the stated goal of this blog is to address issues that affect the day-to-day work of most developers, from time to time I'll publish something for the propeller-heads in the audience. This is one of those times.

Introduction

The instruction set for a CLI compliant execution engine is described by ECMA's CLI Partition III (CIL Instruction Set). The Compact Framework execution engine implementation supports most, but not all, of these instructions. In general, the decision to support an opcode was based on whether it could be generated by one of the compilers supported by CF (C# and Visual Basic, at this time).

Unsupported instructions for v2

An unsupported instruction or prefix is one that will cause an InvalidProgramException if it is encountered by the execution engine. This is in contrast to an instruction or prefix that is simply ignored by the execution engine. CF 1.x had 7 unsupported instructions and 3 unsupported prefixes. CF 2.0 has 7 unsupported instructions and 2 unsupported prefixes. In addition, CF 2.0 has 1 new instruction and 3 new prefixes.

The unsupported instructions in CF 2.0 fall into four categories:

[Edited to fix table layout]

Category Instructions
indirect function calls
  • calli
  • jmp
variable-length argument list support
  • arglist
  • mkrefany
  • refanyval
  • refanytype
local dynamic memory allocation
  • localloc
prefixes
  • unaligned.
  • tail.

The calli, arglist, mkrefany, refanyval, and refanytype instructions are used to support features of Managed C++. calli supports the closely coupled native/managed interop done by managed C++. The other four instructions are primarily used in the MC++ varargs implementation. Since CF doesn't support MC++, these instructions were left out.

The localloc instruction is used to allocate bytes from the local memory pool. It's use is similar to that of the standard C runtime alloca() function. It wasn't implemented because, while it can be generated by the C# compiler, it isn't widely used and can be easily avoided.

The unaligned. and tail. prefixes were omitted because CF doesn't have any customer scenarios that require them.

New instructions for v2

The unbox.any instruction and the constrained. and readonly. prefixes were added to support generics. unbox.any extracts the value contained in a boxed type and is the equivalent of an unbox followed by ldobj. readonly. optimizes access to elements of a generic array by avoiding a type check when fetching an array element by guaranteeing that the element has not been modified. constrained. was introduced to allow the callvirt instruction to work in a uniform way with both value types and references types.

The no. prefix indicates that the execution engine need not check for type, range, and/or null exceptions when the next instruction is executed. CF 2.0 ignores this instruction because it is designated as optional in the ECMA specification.

Conclusion

A quick trip through any subject must naturally leave out a lot of information. If you have questions about opcode specifications, the ECMA spec is the place to start. If you want to see how the compiler translates high-level code into IL opcodes, use ILDasm.exe to disassemble your executable. You can find a tutorial here. Most of what I know about IL, I've learned from ILDasm. If you still have questions, feel free to ask.

~Dan


    Legend
  • yes: instruction is supported in given version
  • no: instruction is not supported in given version
  • X: instruction did not exist in given version

Opcode Instruction V1 Support V2 Support   Opcode Instruction V1 Support V2 Support   Opcode Instruction V1 Support V2 Support
0x00 nop yes yes   0x50 ldind.ref yes yes   0xA0 stelem.r4 yes yes
0x01 break yes yes   0x51 stind.ref yes yes   0xA1 stelem.r8 yes yes
0x02 ldarg.0 yes yes   0x52 stind.i1 yes yes   0xA2 stelem.ref yes yes
0x03 ldarg.1 yes yes   0x53 stind.i2 yes yes   0xA3 ldelem yes yes
0x04 ldarg.2 yes yes   0x54 stind.i4 yes yes   0xA4 stelem yes yes
0x05 ldarg.3 yes yes   0x55 stind.i8 yes yes   0xA5 unbox.any X yes
0x06 ldloc.0 yes yes   0x56 stind.r4 yes yes   0xB3 conv.ovf.i1 yes yes
0x07 ldloc.1 yes yes   0x57 stind.r8 yes yes   0xB4 conv.ovf.u1 yes yes
0x08 ldloc.2 yes yes   0x58 add yes yes   0xB5 conv.ovf.i2 yes yes
0x09 ldloc.3 yes yes   0x59 sub yes yes   0xB6 conv.ovf.u2 yes yes
0x0A stloc.0 yes yes   0x5A mul yes yes   0xB7 conv.ovf.i4 yes yes
0x0B stloc.1 yes yes   0x5B div yes yes   0xB8 conv.ovf.u4 yes yes
0x0C stloc.2 yes yes   0x5C div.un yes yes   0xB9 conv.ovf.i8 yes yes
0x0D stloc.3 yes yes   0x5D rem yes yes   0xBA conv.ovf.u8 yes yes
0x0E ldarg.s yes yes   0x5E rem.un yes yes   0xC2 refanyval no no
0x0F ldarga.s yes yes   0x5F and yes yes   0xC3 ckfinite yes yes
0x10 starg.s yes yes   0x60 or yes yes   0xC6 mkrefany no no
0x11 ldloc.s yes yes   0x61 xor yes yes   0xD0 ldtoken yes yes
0x12 ldloca.s yes yes   0x62 shl yes yes   0xD1 conv.u2 yes yes
0x13 stloc.s yes yes   0x63 shr yes yes   0xD2 conv.u1 yes yes
0x14 ldnull yes yes   0x64 shr.un yes yes   0xD3 conv.i yes yes
0x15 ldc.i4.m1 yes yes   0x65 neg yes yes   0xD4 conv.ovf.i yes yes
0x16 ldc.i4.0 yes yes   0x66 not yes yes   0xD5 conv.ovf.u yes yes
0x17 ldc.i4.1 yes yes   0x67 conv.i1 yes yes   0xD6 add.ovf yes yes
0x18 ldc.i4.2 yes yes   0x68 conv.i2 yes yes   0xD7 add.ovf.un yes yes
0x19 ldc.i4.3 yes yes   0x69 conv.i4 yes yes   0xD8 mul.ovf yes yes
0x1A ldc.i4.4 yes yes   0x6A conv.i8 yes yes   0xD9 mul.ovf.un yes yes
0x1B ldc.i4.5 yes yes   0x6B conv.r4 yes yes   0xDA sub.ovf yes yes
0x1C ldc.i4.6 yes yes   0x6C conv.r8 yes yes   0xDB sub.ovf.un yes yes
0x1D ldc.i4.7 yes yes   0x6D conv.u4 yes yes   0xDC endfinally yes yes
0x1E ldc.i4.8 yes yes   0x6E conv.u8 yes yes   0xDD leave yes yes
0x1F ldc.i4.s yes yes   0x6F callvirt yes yes   0xDE leave.s yes yes
0x20 ldc.i4 yes yes   0x70 cpobj yes yes   0xDF stind.i yes yes
0x21 ldc.i8 yes yes   0x71 ldobj yes yes   0xE0 conv.u yes yes
0x22 ldc.r4 yes yes   0x72 ldstr yes yes   0xFE 0x00 arglist no no
0x23 ldc.r8 yes yes   0x73 newobj yes yes   0xFE 0x01 ceq yes yes
0x25 dup yes yes   0x74 castclass yes yes   0xFE 0x02 cgt yes yes
0x26 pop yes yes   0x75 isinst yes yes   0xFE 0x03 cgt.un yes yes
0x27 jmp no no   0x76 conv.r.un yes yes   0xFE 0x04 clt yes yes
0x28 call yes yes   0x79 unbox yes yes   0xFE 0x05 clt.un yes yes
0x29 calli no no   0x7A throw yes yes   0xFE 0x06 ldftn yes yes
0x2A ret yes yes   0x7B ldfld yes yes   0xFE 0x07 ldvirtftn yes yes
0x2B br.s yes yes   0x7C ldflda yes yes   0xFE 0x09 ldarg yes yes
0x2C brfalse.s yes yes   0x7D stfld yes yes   0xFE 0x0A ldarga yes yes
0x2D brtrue.s yes yes   0x7E ldsfld yes yes   0xFE 0x0B starg yes yes
0x2E beq.s yes yes   0x7F ldsflda yes yes   0xFE 0x0C ldloc yes yes
0x2F bge.s yes yes   0x80 stsfld yes yes   0xFE 0x0D ldloca yes yes
0x30 bgt.s yes yes   0x81 stobj yes yes   0xFE 0x0E stloc yes yes
0x31 ble.s yes yes   0x82 conv.ovf.i1.un yes yes   0xFE 0x0F localloc no no
0x32 blt.s yes yes   0x83 conv.ovf.i2.un yes yes   0xFE 0x11 endfilter yes yes
0x33 bne.un.s yes yes   0x84 conv.ovf.i4.un yes yes   0xFE 0x12 unaligned. no no
0x34 bge.un.s yes yes   0x85 conv.ovf.i8.un yes yes   0xFE 0x13 volatile. no yes
0x35 bgt.un.s yes yes   0x86 conv.ovf.u1.un yes yes   0xFE 0x14 tail. no no
0x36 ble.un.s yes yes   0x87 conv.ovf.u2.un yes yes   0xFE 0x15 initobj yes yes
0x37 blt.un.s yes yes   0x88 conv.ovf.u4.un yes yes   0xFE 0x16 constrained. X yes
0x38 br yes yes   0x89 conv.ovf.u8.un yes yes   0xFE 0x17 cpblk yes yes
0x39 brfalse yes yes   0x8A conv.ovf.i.un yes yes   0xFE 0x18 initblk yes yes
0x3A brtrue yes yes   0x8B conv.ovf.u.un yes yes   0xFE 0x19 no. X yes
0x3B beq yes yes   0x8C box yes yes   0xFE 0x1A rethrow yes yes
0x3C bge yes yes   0x8D newarr yes yes   0xFE 0x1C sizeof yes yes
0x3D bgt yes yes   0x8E ldlen yes yes   0xFE 0x1D refanytype no no
0x3E ble yes yes   0x8F ldelema yes yes   0xFE 0x1E readonly. X yes
0x3F blt yes yes   0x90 ldelem.i1 yes yes
0x40 bne.un yes yes   0x91 ldelem.u1 yes yes
0x41 bge.un yes yes   0x92 ldelem.i2 yes yes
0x42 bgt.un yes yes   0x93 ldelem.u2 yes yes
0x43 ble.un yes yes   0x94 ldelem.i4 yes yes
0x44 blt.un yes yes   0x95 ldelem.u4 yes yes
0x45 switch yes yes   0x96 ldelem.i8 yes yes
0x46 ldind.i1 yes yes   0x97 ldelem.i yes yes
0x47 ldind.u1 yes yes   0x98 ldelem.r4 yes yes
0x48 ldind.i2 yes yes   0x99 ldelem.r8 yes yes
0x49 ldind.u2 yes yes   0x9A ldelem.ref yes yes
0x4A ldind.i4 yes yes   0x9B stelem.i yes yes
0x4B ldind.u4 yes yes   0x9C stelem.i1 yes yes
0x4C ldind.i8 yes yes   0x9D stelem.i2 yes yes
0x4D ldind.i yes yes   0x9E stelem.i4 yes yes
0x4E ldind.r4 yes yes   0x9F stelem.i8 yes yes
0x4F ldind.r8 yes yes

[Edited to fix table layout]

Posted by BlueCollar | 5 Comments

Extending .Net Compact Framework Controls

Hello, and welcome to blue collar.

My name is Dan Elliott and I'm a software development engineer in the test organization for the .Net Compact Framework. In my nearly 14 years at Microsoft I've worked in a variety of roles in user education, testing, and development. For the past six years I've tested the execution engine for the Compact Framework (CF). I'm currently starting a new challenge: testing the GUI components of the CF base class libraries.

So why blue collar? I want this blog to address solutions for developers who are in the trenches, attempting to deliver software to end-users on time and under budget. I've worked on end-user applications both inside and outside of Microsoft. In my experience, the single most time consuming task has been determining why the system and framework software that I was relying on didn't work the way I expected. The .NET Framework has provided a great foundation on which developers can build their applications. The .NET Compact Framework has done a good job of providing a useful subset of that functionality for mobile developers. However, to be perfectly honest, no software platform is perfectly designed or engineered, particularly those as ambitious as .Net and .Net CF. I hope to use my experience with software in general and the Compact Framework in particular to provide solutions for those of you doing the real work.

So I'll jump right in. One of the comments I've heard frequently since I began working on the CF GUI base class libraries (BCLs) is "The .Net Framework has an X property (or method or event) on control Y. I really need it, but the CF version doesn't have it. Why?"

When working on devices, memory is limited. The CF provides roughly 28% of the functionality of the .NET Framework in 8% of the space. The only way to achieve that was to leave out everything that wasn't absolutely essential. That's all well and good, but you still need the functionality. Do you have to write your own control from scratch? Thankfully, the answer is usually no.

To take a specific instance, the Compact Framework version of the ListView control (System.Windows.Forms.ListView) doesn't fire the Resize event when the control's size changes. If you want to adjust the width of columns in the control when the control is resized, it would be nice to be notified. It only takes a few lines of code to remedy the problem.

Here's what we need to do:

  1. Add New PocketPC 2003 Control Library project called ListViewThatFiresResizeEvent.
  2. Rename UserControl1.cs to ListViewThatFiresResizeEvent.cs. When prompted, choose to rename all references to the code element UserControl1 .
  3. Change base class of ListViewThatFiresResizeEvent from Control to ListView :
    
      public partial class ListViewThatFiresResizeEvent : ListView
    
  4. In ListViewThatFiresResizeEvent.InitializeComponent method remove the following line because System.Windows.Forms.ListView doesn't contain an AutoScaleMode property:
     
      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
    
    
  5. Remove the OnResize event handler in UserControl1.Designer.cs if one was generated.
  6. In the the ListViewThatFiresResizeEvent namespace, declare an event handler delegate:
    
      public delegate void ResizeEventHandler(object sender, EventArgs e);
    
  7. In the ListViewThatFiresResizeEvent class, declare the Resize event:
    
      public new event ResizeEventHandler Resize;
    

    The new keyword is required because System.Control.Resize is not virtual.
  8. Override the ListView.OnResize event handler in the ListViewThatFiresResizeEvent class:
    
      protected override void OnResize(EventArgs e) 
      { 
        if (null != this.Resize) 
        { 
          this.Resize(this, new EventArgs()); 
        }
      }
    
  9. Implement new handlers for the properties that change the size of the ListView control ( Size , Width , Height ). In each of the property setters, fire the Resize event. (Again the new keyword is required because the properties are not declared virtual in the base class.)
           
      public new Size Size 
      {
        get { return base.Size; }
        set 
        {
          base.Size = value; 
          this.OnResize(new EventArgs()); 
        }
      }
    
      public new int Width 
      {
        get { return base.Width; }
        set 
        {
          base.Width = value; 
          this.OnResize(new EventArgs()); 
        }
      }
    
      public new int Height 
      {
        get { return base.Height; }
        set 
        {
          base.Height = value; 
          this.OnResize(new EventArgs()); 
        }
      }
        
    
  10. Finally, drag a ListViewThatFiresResizeEvent object from the toolbox to your form and add an event handler function in your application.

That's it. A similar process can be used to extend any CF control. Let me know you find this useful.

~Dan

[edited to fix formatting]

Posted by BlueCollar | 17 Comments
 
Page view tracker