Step by Step: Build a Custom Control for Visual Studio .NET 2003 by Using the .NET Compact Framework on Smartphone

 

Microsoft Corporation

February 2005

Applies to:
   Microsoft Visual Studio .NET 2003
   Microsoft .NET Framework version 1.1
   Microsoft .NET Compact Framework version 1.0
   ASP.NET version 1.1
   Windows Mobile-based Smartphones
   Microsoft eMbedded Visual C++ version 4.0
   Microsoft Windows CE .NET

Summary: The objective of this exercise is to learn how to create a reusable control that can be added to the Microsoft Visual Studio .NET 2003 Toolbox and dropped onto a form. This exercise will take approximately 30 minutes to complete. (23 printed pages)

Download Lab 1 Building a Custom Control for VSNET 2003.msi from the Microsoft Download Center.

Contents

Part 1: Creating a Custom Control for the Visual Studio .NET 2003 Toolbox
Part 2: Compiling the Design-Time Custom Control
Part 3: Creating the Client and Adding the Control to the Toolbox
Summary

To complete this exercise, you will need:

  • Smartphone 2003 emulator
  • Smartphone 2003 SE QVGA (Virtual Radio) – SDK emulator
  • Interop
  • Internet connection

Part 1: Creating a Custom Control for the Visual Studio .NET 2003 Toolbox

In this part, you will create a custom control that provides the functionality for a Snake game and allows design-time customization using the Microsoft Visual Studio .NET integrated development environment (IDE).

Some illustrations in this part of the exercise are thumbnails. You can click the thumbnails for larger images.

To add the custom control

  1. Create a folder on drive C, and then rename this folder Labs.

  2. Copy the Smartphone2003 folder from the download, and then paste it into the C:\Labs folder.

  3. In the C:\Labs\Smartphone2003 folder, open the Lab1 folder.

  4. Rename the SnakeControl folder to SnakeControl_DVD.

  5. Rename the SnakeSP folder to SnakeSP_DVD.

  6. Start Visual Studio .NET 2003.

  7. On the File menu, point to New, and then click Project.

  8. In the Project Types list, select Visual C# Projects.

  9. Under Templates, select Smart Device Application, as shown in the following illustration.

    Click here for larger image

  10. In the Name box, type SnakeControl.

  11. In the Location box, change the path to C:\Labs\Smartphone2003\Lab1.

    Important Be sure that file names and locations are correct throughout the exercise. If not, the exercise may not work.

  12. Click OK to start the Smart Device Application Wizard.

  13. Select the Smartphone platform and Class Library project type, and then click OK, as shown in the following illustration.

    Click here for larger image

    By default, Visual Studio creates a new class when you create a new class library project. For this part of the exercise, you will delete this new class and add your own.

  14. In the Solution Explorer, right-click Class1.cs, and then click Delete, as shown in the following illustration.

  15. Click OK to remove the file, as shown in the following illustration.

  16. On the File menu, click Add Existing Item.

  17. Browse to C:\Labs\Smartphone2003\Lab1\Code.

  18. Select the SnakeControl.cs file, and then click Open to add the file to the project. This will copy the file into your project directory.

  19. In the Solution Explorer, double-click SnakeControl.cs to open the file.

    This file contains the majority of the code for the Snake control.

    Note The control inherits from System.Windows.Forms.Control. This class provides the basic functionality — such as input and messaging — that is used for creating a Windows control. The class also contains the implementation for the Snake game. In the next task, you will extend the code to work as a custom control.

    Note If you try to compile the project now, it will fail, because it is missing a couple of assemblies used for drawing and winforms.

  20. On the Project menu, click Add Reference, and then click the .NET tab.

  21. Select the System.Windows.Forms component, and then click Select.

  22. Select the System.Drawing component, and then click Select.

  23. Click OK to add both references to the project, as shown in the following illustration.

    Click here for larger image

  24. On the Build menu, click Build Solution to build the control.

    The compiler output should generate a couple of warnings and no errors.

To enable the control to be added to the Visual Studio .NET Toolbox, you need to add an assembly-level attribute.

To enhance the custom control

  1. Open the SnakeControl.cs file.

  2. Find the following line near the top of the file.

    //TODO: Add assembly level attribute to enable plugin to toolbox
    
  3. Immediately below this line, add the following lines.

    #if NETCFDESIGNTIME
    [assembly: System.CF.Design.RuntimeAssemblyAttribute("SnakeControl, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")]
    #endif
    

    Note The attribute lives in the System.CF.Design namespace, which is not a part of this project. This location is necessary because two assemblies will be created: one to run on the device and another with extra design-time functionality. The #if NETCFDESIGNTIME and #endif statements provide conditional compilation only if the symbol NETCFDESGINTIME exists, which allows us to create one assembly for the device and another for the designer. This arrangement helps to reduce the amount of the code on the device.

    Next, you will add attributes to some of the accessors, so that you can modify them during design time.

  4. Find the following accessor.

    // TODO: ADD ATTRIBUTES FOR DESIGN PROPERTIES WINDOW
    public int BlockSize {
    get {return blockSize;}
        set {blockSize = value; DesignView(); this.Invalidate();}
    }
    
  5. Above the accessor declaration, add the code so it looks like the following.

    #if NETCFDESIGNTIME
        // Show this property in the property grid.
        [System.ComponentModel.Category("Snake")]
        [System.ComponentModel.Description("The snake block size")]
    #endif
    public int BlockSize { 
    get {return blockSize;}
        set {blockSize = value; DesignView(); this.Invalidate();}
    }
    

    These attributes control how the property is displayed in the Visual Studio .NET Properties window. The Category attribute sets the category in which the property appears. The Description attribute is displayed at the bottom of the Properties window. The property name, BlockSize, is the name given in the Properties window, as shown in the following illustration.

    Next, you will link in two more accessors to control the color of the snake.

  6. Find the following accessor.

    // TODO: ADD ATTRIBUTES FOR DESIGN PROPERTIES WINDOW
    public Color Head {
        get {return snakeHeadColor;}
        set {snakeHeadColor = value; this.Invalidate();}
    }
    
  7. Above the accessor declaration, add the following lines.

    #if NETCFDESIGNTIME
        // Show this property in the property grid.
        [System.ComponentModel.Category("Snake")]
        [System.ComponentModel.Description("The color used for the snake's head")]
    #endif
    
  8. Find the following accessor.

    // TODO: ADD ATTRIBUTES FOR DESIGN PROPERTIES WINDOW
    public Color Body {
        get {return snakeBodyColor;}
        set {snakeBodyColor = value; this.Invalidate();}
    }
    
  9. Above the accessor declaration, add the following lines.

    #if NETCFDESIGNTIME
        // Show this property in the property grid.
        [System.ComponentModel.Category("Snake")]
        [System.ComponentModel.Description("The color used for the snake's body")]
    #endif
    

    Note   When any of the set accessors are called to change a value, the code calls Invalidate to repaint the control.

    Next, you will add event support to the control, so that you can notify the parent control when the snake length increases and when the game is over.

  10. Locate the following comment.

    // TODO: ADD THE EVENTS HERE
    
  11. Add the following event declarations.

    public event EventHandler SnakeLengthChanged;
    public event EventHandler GameFinished;
    

    The EventHandler allows the host parent control to provide a method signature that you can call within the custom control. You will see this method signature in operation in the next exercise. For now, you will add code to raise events accordingly.

  12. Locate the following comment in the OnTick method.

    //TODO: Raise event that snake is now longer
    
  13. Immediately after this line, add the following lines.

    if(this.SnakeLengthChanged != null)
        this.SnakeLengthChanged(this, EventArgs.Empty);
    

    This code checks to see if an event handler has been registered, in which case it will be called.

  14. Locate the following comment in the DoDead method.

    //TODO: raise game finished event
    
  15. Immediately after this line, add the following lines.

    if(this.GameFinished != null)
        this.GameFinished(this, EventArgs.Empty);
    

    Finally, you need to add code to play sounds when the Snake eats a snack or when the timer interval shortens (to indicate a rise in level). Unfortunately, the .NET Compact Framework does not provide a direct mechanism to play sounds. Instead, you need to use Platform Invoke (P/Invoke) calls to a native application programming interface (API). Interop allows the control to easily communicate with COM components and the Win32 API. The .NET Compact Framework version 1.0 does not provide COM Interop, but does provide P/Invoke calls to the WinCE API.

  16. Locate the following comment in the SnakeControl class declaration.

    // TODO: ADD INTEROP DECLARATION AND CONSTANTS
    
  17. Immediately after this comment, add the following lines.

    [DllImport("CoreDll.DLL", EntryPoint="PlaySound", SetLastError=true)]
    private extern static int PlaySound(string szSound, IntPtr hMod, int flags);
    

    The attribute and method signature declaration allows you to use P/Invoke to make a WinCE API call. The method signature int PlaySound(string szSound, IntPtr hMod, int flags) maps to the WinCE PlaySound API function. The extern keyword modifier notifies the compiler that the method is implemented externally. The DllImport attribute specifies that the external location is CoreDll.dll and the entry point to the library is the PlaySound method. So, when you call the PlaySound method, it maps to the exported PlaySound method in CoreDll.dll.

    For more information about the PlaySound method, see Help for the Smartphone 2003 SDK. (Click Start, point to All Programs, point to Microsoft Smartphone 2003 SDK, and then click Smartphone 2003 Help.)

  18. Click the Index tab, and then in the Type in the keyword to find box, type PlaySound.

  19. In the list of keywords, double-click PlaySound or click Display. You can use this procedure to find out more information about calling a native function.

    Note The PlaySound function has a third parameter, fdwSound, which includes a list of flags that specify the type of sound and how it is played.

To find out the values for the flags, it requires investigation. They may be in the header file listed at the end of the Help page; if they are not, you should create a native Microsoft eMbedded Visual C++ version 4.0 project that includes the type, and then view the type declaration. This method will open up the relevant header file that includes all of the flag definitions.

To view the header file

  1. Open a Command Prompt window.
  2. Change to the directory where the header is located by typing CD C:\Program Files\Windows CE Tools\wce420\SMARTPHONE 2003\Include\Emulator.
  3. To open the mmsystem.h header file in Notepad, type notepad mmsystem.h.
  4. Search for the text SND_FILENAME.

Now you can see the constants that are used for the third parameter, fdwSound.

To add these constants to the SnakeControl.cs file

  1. Locate the Interop declaration.

    private extern static int PlaySound(string szSound, IntPtr hMod, int flags);
    
  2. After the declaration, add the following enumeration.

    private enum Flags {
        SND_ASYNC = 0x1,  /* play asynchronously */
        SND_FILENAME = 0x20000, /* name is file name */
    }
    

    Finally, add code to play sounds when the snake increases in length or when the timer interval decreases.

  3. Locate the following comment in the OnTick method.

    //TODO: Play snackSound
    
  4. Immediately after this comment, add the following line.

    PlaySound(snackSound, IntPtr.Zero, (int) (Flags.SND_ASYNC | Flags.SND_FILENAME));
    

    Note You are combining the asynchronous and filename flags with a bitwise OR operator. This combination ensures that the application doesn't pause during the sound.

  5. Locate the following comment in the OnTick method.

    //TODO: play levelSound to indicate now level up
    
  6. Immediately after this comment, add the following line.

    PlaySound(levelSound, IntPtr.Zero, (int) (Flags.SND_ASYNC | Flags.SND_FILENAME));
    

    Now, you have added all of the code to the custom control.

  7. On the Build menu, click Build Solution to build the custom control.

  8. On the File menu, click Exit to close Visual Studio .NET.

This concludes Part 1.

Part 2: Compiling the Design-Time Custom Control

In this part, you will compile the SnakeControl.cs file into a new assembly that contains the extra code that will run in the Visual Studio .NET Toolbox.

Some illustrations in this part of the exercise are thumbnails. You can click the thumbnails for larger images.

To compile the design-time DLL

  1. Click Start, and then click Run.
  2. In the Open box, type C:\Labs\Smartphone2003\Lab1\Code.
  3. Double-click BuildDesignTime.cmd.
  4. Check that the compilation worked and that the code looks like the following screenshot. If there are any errors, check your code.

The command you ran calls with the C# compiler and then copies the DLL to the Visual Studio .NET NETCF Designer directory. This directory has assemblies that are used in the design-time environment. The actual compiler command is:

csc /noconfig /define:NETCFDESIGNTIME /target:library /out:design.SnakeControl.dll ..\SnakeControl\SnakeControl.cs /r:"C:\Program Files\Microsoft Visual Studio .NET 2003\CompactFrameworkSDK\v1.0.5000\Windows CE\Designer\System.CF.Design.dll" /r:"C:\Program Files\Microsoft Visual Studio .NET 2003\CompactFrameworkSDK\v1.0.5000\Windows CE\Designer\System.CF.Windows.Forms.dll" /r:"C:\Program Files\Microsoft Visual Studio .NET 2003\CompactFrameworkSDK\v1.0.5000\Windows CE\Designer\System.CF.Drawing.dll" /r:System.Windows.Forms.dll /r:System.Drawing.dll /r:System.dll /r:System.XML.dll /r:System.Web.Services.dll /r:System.Data.dll /nowarn:1595

copy design.SnakeControl.dll "C:\Program Files\Microsoft Visual Studio .NET 2003\CompactFrameworkSDK\v1.0.5000\Windows CE\Designer"

The C# compiler options reveal that you have specified supplemental arguments:

  • /noconfig tells the compiler not to automatically reference the full .NET Framework.
  • /define adds the preprocessor symbol NETCFDESIGNTIME, which is used in the SnakeControl code to determine if you are in design-time mode.
  • /target:library specifies that you are creating a DLL assembly and not an executable.
  • /out:design.SnakeControl.dll specifies the output file name for the assembly.
  • /r specifies the references for the assembly. This argument is repeated for each reference.
  • /nowarn:1595 suppresses the compiler warning number 1595, which warns of multiple definitions of the same namespace — for example, System.Windows.Forms, which exists in the full .NET Framework and Compact .NET Framework libraries. Because you are using /noconfig, the .NET Compact Framework libraries are automatically referenced instead of the full desktop libraries.

Finally, the assembly design.SnakeControl.dll is copied to the Windows CE Designer directory, which contains other assemblies that will be used during design time.

Part 3: Creating the Client and Adding the Control to the Toolbox

In this part, you will create the Smartphone client and add the SnakeControl to the Visual Studio .NET Toolbox.

You will perform the following tasks:

  • Create the Smartphone client
  • Add the custom control to the Visual Studio .NET Toolbox

Some illustrations in this part of the exercise are thumbnails. You can click the thumbnails for larger images.

Creating the Smartphone Client

In this task, you will create a Smartphone client and add a menu structure for the application.

To create the Smartphone client

  1. Start Visual Studio .NET 2003.

  2. On the File menu, point to New, and then click Project.

  3. In the Project Types list, click Visual C# Projects.

  4. Under Templates, click Smart Device Application, as shown in the following illustration.

    Click here for larger image

  5. In the Name box, type SnakeSP.

  6. In the Location box, type C:\Labs\Smartphone2003\Lab1.

  7. Click OK to start the Smart Device Application Wizard, as shown in the following illustration.

    Click here for larger image

  8. Select the Smartphone platform and Windows Application project type, and then click OK.

This will create a basic application with a form and empty menu, as shown in the following illustration.

Click here for larger image

Now you will add the menu structure for the application. A traditional Microsoft Windows or Pocket PC application can have multiple top-level and nested menus. The Smartphone user interface has been simplified to include two top-level menu items, which map to hardware keys below the screen. These top-level menus are called softkeys. If the softkey does not have a pop-up menu, pressing the softkey will run the top-level menu item. If the softkey does have submenu items, pressing the softkey will open the submenu. The user can then use the directional keypad to navigate and choose the menu item, as shown in the following illustration.

Figure 1. Smartphone directional keypad and the accompanying softkey menu

To achieve the Designed for Windows Smartphone logo certification, the left softkey should not have associated submenus — for example, it is a default action. The right softkey can have multiple nested submenu items, if required.

Note If you create a .NET Compact Framework Smartphone application that includes a pop-up menu for the left softkey, the application will compile; however, the runtime will throw a "not supported" exception at run time.

To create the Smartphone menu

  1. In Visual Studio .NET 2003, in the lower-left corner of the Designer view, click the mainMenu1 control. This will extend the form to include the menu. It is now ready for editing, as shown in the following illustration.

  2. Click Type Here, and then type Start, as shown in the following illustration. This step will create the left softkey menu item.

A placeholder has been created for a new menu item to the right of the left menu item.

  1. Click the rightmost Type Here, and then type Done, as shown in the following illustration.

    The menu structure is now complete, but it does not respond to user input. You need to raise events when the user chooses a menu item.

  2. Double-click the Start menu item to create a new click handler.

    This step will switch the Code view to the code behind the form and will add the following method signature:

  3. Press SHIFT+F7 to switch the view back to form design view.

  4. Double-click the Done menu item to create a new click handler.

  5. Press SHIFT+F7 to switch back to form design view. The menu design and handlers are now complete.

Now you need to add the custom control to the Toolbox.

Adding the Custom Control to the Toolbox

In this task, you will add a custom control to the Toolbox, add the custom control to the form, and test the form.

To add the custom control to the Toolbox

  1. If the Toolbox window is not visible, on the View menu, click Toolbox, as shown in the following illustration.

    The Toolbox defaults to the Device Controls tab, showing the controls that are compatible with Smart Device projects.

  2. Right-click the Toolbox, and then click Add/Remove Items, as shown in the following illustration.

    Click here for larger image

The list of available components on the computer appears, as shown in the following illustration.

Click here for larger image

  1. To choose the SnakeControl custom control, click Browse, and then browse to the C:\Program Files\Microsoft Visual Studio .NET 2003\CompactFrameworkSDK\v1.0.5000\Windows CE\Designer directory, as shown in the following illustration.

  2. Select the design.SnakeControl.dll file, and then click Open. The view will switch to the Customize Toolbox window, which will display SnakeControl in the list of components.

  3. Click OK to close the window. This step will add the design-time custom control, which you built in Part 2, with extra design-time functionality to the Toolbox.

Now you need to add the custom control to the form, and then test the control.

To finish the basic client

  1. Select the form in the Designer view.

  2. Change the BackColor to DarkGray.

  3. Change the Text to Snake, as shown in the following illustration.

    Click here for larger image

    Before you drag SnakeControl onto the form, you need to add a reference to the run-time version of the control.

  4. To add the assembly, on the Project menu, click Add Reference.

  5. Click Browse to look for the assembly.

  6. Browse to the C:\Labs\Smartphone2003\Lab1\SnakeControl\bin\Debug directory.

  7. Select the SnakeControl.dll file, and then click Open.

  8. Click OK to close the Add Reference dialog box, as shown in the following illustration.

    Click here for larger image

  9. Click OK to add the assembly to the application. Now that you have a reference to the run-time control, you can add the design-time control to the form.

  10. In the Toolbox, scroll until you find SnakeControl.

  11. Drag SnakeControl onto the form.

  12. Change the BackColor of the snakeControl1 object to White.

  13. Change the upper-left Location to 8, 8.

  14. Change the Size to 160,160.

    ****Note   SnakeControl has rendered an example of the Snake at design time. The Properties window also contains a Snake category with the accessors that you exposed by using the attributes in Task 1.

  15. Change the BlockSize to 8.

    Note Keep the final BlockSize set at 8 to ensure that all games and high scores are consistent.

  16. Try changing the Body and Head colors.

    Note The changes are immediate and rendered at design time, as shown in the following illustration.

    Click here for larger image

    Now that you have added SnakeControl and modified its properties, you can program against the control.

  17. Press F7 to change to code view for the form.

  18. Locate the following lines in the Form1 constructor.

    //
    // TODO: Add any constructor code after InitializeComponent call
    //
    
  19. Immediately below these lines, add the following lines.

    this.snakeControl1.Width = this.ClientRectangle.Width-(2*this.snakeControl1.Left);
    this.snakeControl1.Height = this.ClientRectangle.Height-(2*this.snakeControl1.Top);
    this.snakeControl1.BlockSize = (int)this.snakeControl1.Width/20;
    

    This code will ensure that SnakeControl fills the form and that the blocksize is a twentieth of the game area for standard and high-resolution devices.

    Important If this code were not added, the user control would not size correctly on a Quarter Video Graphics Array (QVGA) device, as shown in the following illustration.

  20. Locate the following line in the Form1 class.

    private void menuItem1_Click(object sender, System.EventArgs e) {
    
  21. Immediately below this line, add the following line.

    this.snakeControl1.Start();
    
  22. Locate the following line in the Form1 class.

    private void menuItem2_Click(object sender, System.EventArgs e) {
    
  23. Immediately below this line, add the following line.

    Application.Exit();
    
  24. Press CTRL+SHIFT+B to build the solution. The solution should compile with no errors or warnings.

  25. To test the application, press F5. Visual Studio .NET will present the options for deploying the application, as shown in the following illustration.

  26. Select Smartphone 2003 Emulator (Virtual Radio), and then click Deploy.

    The Virtual Radio emulator provides emulation functionality for radio hardware; the Radio Required emulator image is designed to be used with a hardware board over a serial connection.

    When Visual Studio .NET deploys the application to the emulator (or device), it will determine if the string resources for the System namespace are installed. If the emulator has been started from a cold boot, Visual Studio .NET will automatically copy a cabinet (.cab) file to the device and install the resources.

    If you see the following screen on your emulator, click the left softkey or press F1 to continue deploying your application. If the application stops responding during execution, or if you cannot exit the application, press SHIFT+F5 to stop debugging.

  27. In the Smartphone 2003 emulator, wait for the application to load.

  28. Press the left softkey or press F1 to select the Start menu item.

  29. Use the cursor keys to control the snake, as shown in the following illustration.

  30. Use the softkeys to quit the game. Try redeploying the game to the high-resolution emulator.

  31. In Visual Studio .NET, press F5.

  32. Select WWE SP 2003 SE QVGA (Virtual Radio) - SDK Emulator, and then click Deploy, as shown in the following illustration.

  33. Play the game again by using the QVGA device, as shown in the following illustration.

Congratulations! You have completed the Smartphone Custom Controls exercise.

Summary

In this exercise, you performed the following:

  • Created a custom control for the Visual Studio .NET 2003 Toolbox
  • Compiled the design-time custom control
  • Created the client and added the control to the Toolbox