Step by Step: Writing Device-Independent Windows Mobile Applications with the .NET Compact Framework

4/7/2010

Microsoft Corporation

February 2007

Summary

Learn how to build Microsoft Windows Mobile applications that successfully support the wide variety of device capabilities and available displays. This HOL will take 1 hour and 15 minutes to complete. (42 printed pages)

Download code sample from the Microsoft Download Center.

Applies To

Microsoft .NET Compact Framework version 1.0

Microsoft Visual C#

Microsoft Visual Studio 2005

Windows Mobile devices

The following applications are required to run this HOL:

  • Microsoft Windows XP Professional
  • Visual Studio 2005
    This HOL requires Visual Studio 2005 Standard, Professional, or Team System Editions. It will not work with any of the Express Editions. If you do not have the correct edition of Visual Studio 2005, find out how you can acquire it from the Visual Studio 2005 Developer Center.
  • Microsoft ActiveSync 4.0
    ActiveSync 4.0 allows for connectivity between a Windows Mobile–based device and your computer.
    Download and install ActiveSync 4.0.
  • Windows Mobile 5.0 SDKs
    The Windows Mobile 5.0 SDKs for Pocket PC and Smartphone enable development for Windows Mobile–based devices in Visual Studio 2005:
    Download and install Windows Mobile 5.0 SDK for Pocket PC.
    Download and install Windows Mobile 5.0 SDK for Smartphone.
  • Set up your computer
    Follow the directions in the Additional Information section to set up your computer for this HOL.

Introduction

Lab 1: Writing Device-Independent Windows Mobile Applications with the .NET Compact Framework

Conclusion

Additional Information

Introduction

In this self-paced hands-on lab (HOL), you will learn to build Windows Mobile–based applications that successfully support the wide variety of device capabilities and available displays.

Lab 1: Writing Device-Independent Windows Mobile Applications with the .NET Compact Framework

The objective of this lab is to provide you with an understanding about how to build Windows Mobile–based applications that successfully support the wide variety of available displays.

In this HOL, you will perform the following exercises:

  • Identifying device characteristics
  • Adapting to device capabilities
  • Supporting both landscape and portrait displays
  • Adding high-resolution support
  • Supporting square and other displays

Exercise 1: Identifying Device Characteristics

In this exercise, you will learn how to determine the specific Windows Mobile version, whether the device is a Pocket PC or a Smartphone, and whether the device supports phone capabilities.

To open the WhoAmI.sln file in Visual Studio

  1. Open Microsoft Visual Studio 2005 by clicking Start | All Programs | Microsoft Visual Studio 2005 | Microsoft Visual Studio 2005.

  2. In Visual Studio 2005, click File | Open | Project/Solution.

  3. In the Open Project dialog box, browse to C:\Program Files\Windows Mobile Developer Samples\HOLs\MEDC06_311\Exercises\WhoAmI.

  4. Select WhoAmI.sln, and then click Open.

  5. In Solution Explorer, double-click Form1.cs to open Form1 in the form designer. The form should look like Figure 1. You will use the form to display platform and capabilities information for the current device.

    Note

    After you open WhoAmI.sln, the solution, project, and source files should be visible in Solution Explorer in Visual Studio 2005. If Solution Explorer doesn't automatically appear, click View | Solution Explorer to make it visible.

    Bb278110.b0c1bcd1-f986-430f-9b12-f4ece123226e(en-us,MSDN.10).gif

    Figure 1. The WhoAmI project Form1.cs form in the form designer

    The form contains four Label controls. The label1 and label2 Label controls contain the text OS Version and Device Type respectively. The labelOsVersion Label control is used at runtime to display the device's operating system version number. The labelDeviceType Label control is used to display the specific device type.

To determine the device type

  1. In Solution Explorer, double-click PlatformInfo.cs to open the file. The PlatformInfo class is used to encapsulate the details of retrieving the platform information.

  2. Notice that the PlatformInfo class contains a SystemParametersInfo method and a SPI_GETPLATFORMTYPE constant.

    The SystemParametersInfo method is actually a Microsoft Win32® function that has been mapped into this class by using the DLLImport attribute. The SystemParametersInfo function provides access to a wide variety of device-related information including a string that identifies the current device type. The SPI_GETPLATFORMTYPE constant is the value that indicates to the SystemParametersInfo function to return the device type string.

    To make retrieving the platform type string easier, provide a private property that encapsulates the details.

  3. Declare a private, static, read-only string property named SystemTypeString, as shown in the following code example.

    static string SystemTypeString
    {
        get
        {
        }
    }
    
  4. In the get method, declare a StringBuilder instance named platformTypeString that has an initial capacity of 255 characters, as shown in the following code example.

    StringBuilder platformTypeString = new StringBuilder(255); 
    

    Now you're ready to call the SystemParametersInfo function.

  5. When calling the SystemParametersInfo function, pass the SPI_GETPLATFORMTYPE constant as the first parameter, indicating that the platform type string is to be retuned. Pass the StringBuilder instance's capacity as the second parameter, indicating the size of the buffer to receive the platform type string. The function expects this parameter to be an unsigned integer (uint) whereas the StringBuilder.Capacity property is a signed integer (int), so the property must be cast to an unsigned integer. Pass the StringBuilder instance as the next parameter; this is the buffer that the platform type string is copied into. Finally, pass a zero as the last parameter, as shown in the following code example.

    SystemParametersInfo(SPI_GETPLATFORMTYPE, (uint)platformTypeString.Capacity, platformTypeString, 0);
    
  6. Return the contents of the platformTypeString StringBuilder instance as a string by calling its ToString method.

  7. Verify that the SystemTypeString property looks like the following code example.

    static string SystemTypeString
    {
        get
        {
            StringBuilder platformTypeString = new StringBuilder(255);
            SystemParametersInfo(SPI_GETPLATFORMTYPE,
               (uint)platformTypeString.Capacity, platformTypeString, 0);
            return platformTypeString.ToString();
        }
    }
    

    Note

    The focus of this exercise is how to identify and respond to differences among devices. As a result, the simplest implementation of the SystemTypeString property is used. If desired, the efficiency of the SystemTypeString property can be improved by caching the type string. Caching the type string eliminates the need to repeatedly call the SystemParametersInfo function. The easiest way to cache the type string is to declare a string member variable and store the returned string value in that member variable. The implementation would then be only to call the SystemParametersInfo function once. This first call stores the returned string in the member variable. In all successive calls, the member variable value is returned directly.

    With the SystemTypeString property complete, determining the device type is now quite simple. The Windows Mobile platform has only two device types: Pocket PC and Smartphone. With only these two choices, providing bool properties, such as IsSmartPhone and IsPocketPc, make it easy for an application to determine the specific device type without the application needing to deal with the details of examining the SystemTypeString.

  8. Declare a public, static read-only property named IsSmartPhone with a return type of bool to create the IsSmartPhone property, as shown in the following code example.

    public static bool IsSmartPhone
    {
        get {  }
    }
    
  9. Within the get method, use the SystemTypeString property to retrieve the device type string. If the SystemTypeString property contains the string "SmartPhone," the device is a Smartphone; therefore, the property should return true.

  10. Use the IndexOf method to determine if the SystemTypeString property contains the string "SmartPhone." The IndexOf method returns the starting index of the string if it is found. A valid starting index will always be greater than or equal to 0. The IndexOf method returns -1 if the string is not found.

  11. Verify that the IsSmartPhone property looks like the following code example.

    public static bool IsSmartPhone
    {
        get { return SystemTypeString.IndexOf("SmartPhone") >= 0; }
    }
    
  12. Create the IsPocketPc property in this same way that you did for the IsSmartPhone property, except this time test the SystemTypeString property for the string "PocketPC" (no spaces in PocketPC).

  13. Verify that the method looks like the following code example.

    public static bool IsPocketPc
    {
        get { return SystemTypeString.IndexOf("PocketPC") >= 0; ; }
    }
    

There are many cases where it may be useful to know if the current device has a phone. Obviously in the case of the Smartphone, a phone is always available but when it comes to Pocket PCs, some have phones and some don't. There also may be situations where a single application supports both Smartphones and Pocket PCs and may not be interested in the device type, but it may only be interested in whether or not a phone is available.

To determine phone support

  1. Like the IsSmartPhone and IsPocketPc properties, add an IsAPhone property to the PlatformInfo class. Just like the other properties, IsAPhone is a public, static, read-only property with a return type of bool, as shown in the following code example.

    public static bool IsAPhone
    {
        get {  }
    }
    

    Unlike the device type, there's no function available across the Windows Mobile family of devices that identifies whether a device has a phone. Even though no function exists, there is a trait common to all Windows Mobile powered devices with a phone. That common trait is the presence of a system file, phone.dll in the \Windows folder.

    Knowing that all Windows Mobile powered devices with a phone have the file \Windows\phone.dll, the implementation of the IsAPhone property simply uses the File.Exists method to determine if the file exists. If it does, the IsAPhone property returns true.

  2. Verify that the IsAPhone property looks like the following code example.

    public static bool IsAPhone
    {
        get { return File.Exists(@"\Windows\Phone.dll"); }
    }
    

    Note

    Windows Mobile 5.0 introduced the State and Notifications Broker API. The State and Notifications Broker API provides the Microsoft.WindowsMobile.Status.SystemState class. The SystemState class provides access to a great deal of information regarding the features and capabilities of a device. All of these capabilities are accessible as properties on the SystemState class. Apropos to this exercise is the SystemState,PhoneRadioPresent property. The PhoneRadioPresent property returns true if the device has a phone, much like the IsAPhone property you created in this exercise. The reason the SystemState,PhoneRadioPresent property is not used in this exercise is that it's not available on Windows Mobile powered devices prior to Windows Mobile 5.0; therefore, it does not work if the application is run on a Pocket PC 2002, Pocket PC 2003, Pocket PC 2003 Second Edition, Smartphone 2003, or Smartphone 2003 Second Edition.

    With the PlatformInfo class complete, you can now use it to display information about the current device using the Form1 form in this project. You can also modify the application to display operating system version information.

To display the device information

  1. In Solution Explorer, right-click Form1.cs, and then click View Code to open the Form1.cs file in Code view.

  2. Locate the Form1_Load method.

  3. In the Form1_Load method, add an if/else if/else statement block that tests the PlatformInfo.IsSmartPhone in the first if statement and tests PlatformInfo.IsPocketPc in the else if statement, as shown in the following code example.

    if (PlatformInfo.IsSmartPhone)
    {
    }
    else if (PlatformInfo.IsPocketPc)
    {
    }
    else
    {
    }
    
  4. Add the code that is necessary to assign the device type information to the Text property of the labelDeviceType control. When the IsSmartPhone property is true, assign "SmartPhone" to the labelDeviceType.Text property. When the IsPocketPc property is true, assign "Pocket PC" to the labelDeviceType.Text property. In all other cases assign "Unknown Device" to the labelDeviceType.Text property. (The situation where "Unknown Device" is assigned to the labelDeviceType.Text property should never happen.)

  5. Add the code to determine if the Pocket PC has a phone by adding an if statement immediately after the line that assigns "Pocket PC" to the labelDeviceType.Text property. The if statement tests the PlatformType.IsAPhone property. When the property returns true, concatenate the string "Phone Edition" onto the end of the labelDeviceType.Text property.

  6. Verify that the complete Form_Load method looks like the following code example.

    private void Form1_Load(object sender, EventArgs e)
    {
        if (PlatformInfo.IsSmartPhone)
        {
            labelDeviceType.Text = "SmartPhone";
        }
        else if (PlatformInfo.IsPocketPc)
        {
            labelDeviceType.Text = "Pocket PC";
            if (PlatformInfo.IsAPhone)
                labelDeviceType.Text += " Phone Edition";
        }
        else
        {
            labelDeviceType.Text = "Unknown Device";
        } 
    }
    

    The last thing to do is to assign the operating system version number to the labelOsVersion.Text property by using the Environment class. The Environment class provides basic information about the runtime environment. It has a static property named OSVersion that provides information about the operating system version number and the current Windows platform, such as Win32NT or WinCE. To get the operating system version number use the OSVersion.Version property. The Version property provides information about the operating system version number including the major, minor, build, and revision components. The ToString method returns the complete version number as a string.

    Assigning the version number to the labelOsVersion.Text property is the very last line in the function.

  7. Add the assignment statement outside the entire if/else if/else statement block. It should appear just before the function closing bracket (}), as shown in the following code example.

    labelOsVersion.Text = Environment.OSVersion.Version.ToString();
    

You've completed the application; now test the program to verify that it returns the information correctly.

To test the application

  1. In Visual Studio, locate the Target Device list, and then select Windows Mobile 5.0 Pocket PC Emulator, as shown in Figure 2.

    Bb278110.bc3b6d41-93d7-46a5-ba51-9ce8ff98f4d5(en-us,MSDN.10).gif

    Figure 2. Selecting the target device in Visual Studio

  2. Click Debug | Start Debugging to build and run the application.

  3. If the Deploy WhoAmI dialog box appears, verify that Windows Mobile 5.0 Pocket PC Emulator is selected in the Device list, and then click Deploy.

    Note

    The first time you deploy the application to the device emulator, it may take a few minutes while the emulator starts and the .NET Compact Framework runtime is installed.

  4. After the application starts, it should look like Figure 3. Notice that it correctly displays the OS Version as 5.1.117 and the Device Type as Pocket PC.

    Bb278110.82e5e3ee-f8e6-493c-b4e9-e55d44dc20ce(en-us,MSDN.10).gif

    Figure 3. The WhoAmI application running on the Windows Mobile 5.0 Pocket PC Emulator

  5. Close the application by clicking the left soft key under the text, Exit.

  6. In Visual Studio in the Target Device list, select Windows Mobile 5.0 Pocket PC Phone Emulator.

  7. Click Debug | Start Debugging to start the application.

    The application still displays the OS Version as 5.1.1700, but the Device Type displays Pocket PC Phone Edition, which is correct for this emulator.

  8. Close the application by clicking the left soft key under the text, Exit.

    As a final test, run the application using the Smartphone emulator. The easiest way to do this test is to change the project from a Windows Mobile 5.0 Pocket PC project to a Windows Mobile 5.0 Smartphone project.

To change the project target

  1. In Visual Studio, click Project | Change Target Platform.

  2. On the Change Target Platform dialog box, expand the drop-down list under Change To.

  3. Select Windows Mobile 5.0 SmartPhone SDK, and then click OK.

  4. When prompted by the Microsoft Visual Studio dialog box that asks, Do you want to continue?, click Yes.

    The project will briefly close then reopen.

  5. In Solution Explorer, double-click Form1.cs. Notice the form designer now displays the application as a Smartphone application.

  6. Verify that Windows Mobile 5.0 SmartPhone Emulator is selected in the Target Device list.

  7. Click Debug | Start Debugging to start the application.

  8. Notice that the application looks like Figure 4. It displays an OS Version of 5.1.117 and a Device Type of SmartPhone.

    Bb278110.d7574bbc-9ecc-4806-aa63-2c7e6bac10c4(en-us,MSDN.10).gif

    Figure 4. The WhoAmI application running on a Windows Mobile 5.0 SmartPhone emulator

  9. Close the application by clicking the left soft key under the text, Exit.

  10. Close the solution by selecting File | Close Solution. If prompted to save any items, click Yes.

Exercise 2: Adapting to Device Capabilities

In this exercise, you will apply the techniques that you used in the previous exercise to modify a program so it adapts to the capabilities of each device. The goal is to provide a high-quality user experience across a wide range of Windows Mobile powered devices and to take advantage of the capabilities provided by new or high-end devices. When complete, the capabilities of the application will increase as the capabilities of the device increases.

To become familiar with the existing application and its limitations

  1. In Visual Studio 2005, click File | Open | Project/Solution.

  2. In the Open Project dialog box, browse to C:\Program Files\Windows Mobile Developer Samples\HOLs\MEDC06_311\Exercises\RealEstateMgr.

  3. Select RealEstateMgr.sln, and then click Open.

  4. Click Debug | Start Debugging to start the application. The application appears, as shown in Figure 5.

    Bb278110.3dfcd49e-2b97-4379-91f7-ca5d04da0db2(en-us,MSDN.10).gif

    Figure 5. The RealEstateMgr application running in the Pocket PC 2003 emulator

    The application is a simple real estate application with a list of properties displayed in the data grid at the top of the form and a picture of the property displayed at the bottom of the form. This initial form is referred to as the Home View.

  5. To view the details of the currently selected property, tap the Details menu. The details appear, as shown in Figure 6.

    Bb278110.5f807431-29b8-4f18-8c2f-a22dafa43c1d(en-us,MSDN.10).gif

    Figure 6. The RealEstateMgr detail form

  6. Click Done to close the Details form.

  7. Notice on the main form of the RealEstateMgr application that the current property photo is a large, red brick building.

  8. On the emulator, click Menu | Select New Photo. The standard OpenFileDialog is displayed, as shown in Figure 7, showing the contents of the \My Documents\Business folder, which contains the list of property photo image files for the application.

    Bb278110.79aa3857-09c7-4087-8c90-3a75eab27991(en-us,MSDN.10).gif

    Figure 7. The OpenFileDialog showing the list of property photo image files

  9. Click Image005 in the OpenFileDialog. Notice that the main application form now displays a small white house.

    This is a case where the application provides the required functionality of allowing the user to select a new photo for the property, but the application falls short of providing a quality user experience. The user experience lacks in quality because users have no way of being sure that they're selecting the correct photo image file until after they make the selection. Only when the property photo image is displayed on the main form do users get any confirmation of having selected the right or wrong property photo image. If the device supports the ability to display image files such that users can browse thumbnails of the images rather than just the file names, the application should take advantage of that feature.

  10. On the emulator, click Menu | Call Home Owner. The application now looks like Figure 8.

    Bb278110.7bcb348e-9eb1-41cd-9f85-73e3a19167a1(en-us,MSDN.10).gif

    Figure 8. The RealEstateMgr displaying the home owner phone number

    The Pocket PC 2003 emulator does not provide phone support; therefore, the application displays the phone number and lets the user type it into their separate mobile phone manually. Of course, many Windows Mobile powered devices provide phone support, so the application should take advantage of that support if it is available and automatically place the call.

  11. Click OK to close the Owner Phone Number message.

  12. Click Menu | Exit to close the application.

    Windows Mobile 5.0 introduced a new feature that allows users to browse a list of image files by viewing thumbnails of the images rather than viewing the list of file names. You will now modify the application to take advantage of this capability, so the application will still function properly on pre-Windows Mobile 5.0 devices. The updated application provides the browse-by-thumbnail feature on devices running Windows Mobile 5.0 or later. For all other devices, the application continues to use the standard OpenFileDialog that lists the files by name.

The project currently targets the Pocket PC 2003 SDK. To take advantage of the Windows Mobile 5.0 features, the project must target the Windows Mobile 5.0 Pocket PC SDK. Targeting Windows Mobile 5.0 does not prevent the application from running on pre-Windows Mobile 5.0 devices such as Pocket PC 2003.

To add photo image browsing

  1. In Visual Studio, click Project | Change Target Platform to change the project target.

  2. In the Change Target Platform dialog box, expand the drop-down list under Change To, click Windows Mobile 5.0 Pocket PC SDK, and then click OK.

  3. When prompted by the Microsoft Visual Studio dialog box that asks, Do you want to continue?, click Yes.

    The project will briefly close, and then will reopen.

  4. In Solution Explorer, double-click Form1.cs to open it in the form designer.

  5. Click Menu to expand the Form1 menu. The menu appears, as shown in Figure 9.

    Bb278110.364bcfd9-954f-4ef1-90fe-5065923230f5(en-us,MSDN.10).gif

    Figure 9. The Form1 menu

  6. Double-click the Select New Photo command. This command opens Form1.cs in Code view and places the cursor in the Select New Photo click event handler, menuSelectNewPhoto_Click.

  7. Review the current implementation of the menuSelectNewPhoto_Click method. Using the standard OpenFileDialog class, the menuSelectNewPhoto_Click method sets the directory and file filter (*.jpg) information and then displays the OpenFileDialog. It stores the DialogResult (OK or Cancel) in the dlgResult variable and the user's selected file in the selectedFile variable. The menuSelectNewPhoto_Click method finishes by checking the dlgResult variable to verify that the user did select a file; if so, the new property photo image file is associated with the property record.

    As was noted at the beginning of this exercise, the browse-by-thumbnail feature is available only to devices running Windows Mobile 5.0 and later; therefore, the application must verify that the device is running Windows Mobile 5.0 or later before using the browse-by-thumbnail feature. To check the operating system version number in this case, you don't need the entire version number, but you need only the major version number because that's what indicates whether the device is running Windows Mobile 5.0 versus an earlier version.

  8. Immediately following the selectedFile variable declaration, add an if/else statement block that checks the Major property of the Environment.OSVersion.Version instance. The if statement condition checks whether the Major property is less than 5. The true part of the if block should include the existing six lines from the SelectPictureByNameDialog variable declaration through the assignment of the SelectPictureByNameDialog.FileName property to the selectedFile variable, as shown in the following code example.

    if (Environment.OSVersion.Version.Major < 5)
    {
        OpenFileDialog SelectPictureByNameDialog = new OpenFileDialog();
        SelectPictureByNameDialog.InitialDirectory = _imageFolderShortName;
        SelectPictureByNameDialog.Filter = _imageFilter;
        SelectPictureByNameDialog.FilterIndex = 1;
        dlgResult = SelectPictureByNameDialog.ShowDialog();
        selectedFile = SelectPictureByNameDialog.FileName;
    }
    else
    {
    }
    

    For devices with Windows Mobile 5.0 or later, the application will use the new SelectPictureDialog class. The SelectPictureDialog class is not part of the .NET Compact Framework library, but rather it is part of the Windows Mobile 5.0 managed library.

    Using any of the classes in the Windows Mobile 5.0 managed library requires you to reference one of the Microsoft.WindowsMobile assemblies.

  9. In Visual Studio, click Project | Add Reference to add the assembly reference.

  10. In the Add Reference dialog box, select Microsoft.WindowsMobile.Forms, and then click OK.

    Note

    The Add Reference dialog box includes several assemblies whose name ends in Forms. Be sure to select the Microsoft.WindowsMobile.Forms assembly and not the Microsoft.WindowsCE.Forms or System.Windows.Forms assemblies.

  11. Locate the existing using declarations at the top of the Form1.cs file, and then add a using reference for the "Microsoft.WindowsMobile.Forms" namespace.

    Now you're ready to add the code to use the SelectPictureDialog class.

  12. In the else block of the if/else statement block in the menuSelectPhoto_Click method, declare and create a new instance of the SelectPictureDialog class named selectPictureDialog, as shown in the following code example.

    else
    {
        SelectPictureDialog selectPictureDialog = new SelectPictureDialog();
    }
    

    The program includes the constants _imageFolder and _imageFilter that contain the folder and filter values that are required to select the property picture image file.

  13. Immediately following the selectPictureDialog variable declaration and still within the else block, set the InitialDirectory and Filter properties of the selectPictureDialog variable to the _imageFolder and _imageFilter constants respectively. There is one filter value (*.jpg), so set the FilterIndex property to 1, as shown in the following code example.

    selectPictureDialog.InitialDirectory = _imageFolder;
    selectPictureDialog.Filter = _imageFilter ;
    selectPictureDialog.FilterIndex = 1;
    

    Note

    Be sure to set the selectPictureDialog.InitialDirectory property to the _imageFolder constant and not the _imageFolderShortName constant, as you did for the selectPictureByNameDialog.InitialDirectory property. The selectPictureByNameDialog variable is an instance of the OpenFileDialog class. The OpenFileDialog class only supports opening folders within the My Documents folder; therefore, the InitialDirectory property is set to the subfolder name within the My Documents folder (Business in this case) rather than the full path (\My Documents\Business). The selectPictureDialog variable is an instance of the SelectPictureDialog class. The SelectPictureDialog class supports opening files anywhere in the file system; therefore, the InitialDirectory property must be set to the full path.

  14. As the last two statements in the else block, display the selectPictureDialog instance by calling its ShowDialog method and assigning the return value to the dlgResult variable. Then, assign the selectPictureDialog.FileName property to the selectedFile variable.

  15. Verify that the complete menuSelectPhoto_Click method looks like the following code example.

    private void menuSelectNewPhoto_Click(object sender, EventArgs e)
    {
        DialogResult dlgResult;
        string selectedFile;
    
        if (Environment.OSVersion.Version.Major < 5)
        {
            OpenFileDialog SelectPictureByNameDialog = new OpenFileDialog();
            SelectPictureByNameDialog.InitialDirectory = _imageFolderShortName;
            SelectPictureByNameDialog.Filter = _imageFilter;
            SelectPictureByNameDialog.FilterIndex = 1;
            dlgResult = SelectPictureByNameDialog.ShowDialog();
            selectedFile = SelectPictureByNameDialog.FileName;
        }
        else
        {
            SelectPictureDialog selectPictureDialog = new SelectPictureDialog();
            selectPictureDialog.InitialDirectory = _imageFolder;
            selectPictureDialog.Filter = _imageFilter;
            selectPictureDialog.FilterIndex = 1;
            dlgResult = selectPictureDialog.ShowDialog();
            selectedFile = selectPictureDialog.FileName;
        }
    
        if (dlgResult == DialogResult.OK)
        {
            HouseViewDataSet.PropertyRow row = GetCurrentPropertyRow();
            row.ImageFileName = Path.GetFileName(selectedFile);
        }
    }
    

You're now ready to test the new code that you've added. With these changes, it's no surprise that the application will run on a device that runs Windows Mobile 5.0. You may be wondering what will happen when the application is run on devices that are running earlier software, such as a Pocket PC 2003 or Pocket PC 2003 Second Edition. The good news is that the application runs fine on these earlier devices. Using the Windows Mobile 5.0 features do not prevent the application from running on these earlier devices as long as the application never tries to use the Windows Mobile 5.0 library. Like all .NET assemblies, the Microsoft.WindowsMobile.Forms assembly is not loaded until one of the types contained in the assembly is used. In this case that would be the SelectPictureDialog class. The version number check in the menuSelectNewPhoto_Click method prevents the SelectPictureDialog class from being used on pre-Windows Mobile 5.0 devices. As result, the application never attempts to load the Microsoft.WindowsMobile.Forms assembly on these earlier devices.

To test property photo image selection on a Windows Mobile 5.0-based Pocket PC

  1. In the Target Device list, verify that Windows Mobile 5.0 Pocket PC Emulator is selected to test the application's behavior on Windows Mobile 5.0.

  2. Click Debug | Start Debugging.

  3. When the application starts, click Menu | Select New Photo. Notice that the application shows the thumbnails of the property pictures, as shown in Figure 10.

    Bb278110.de7d0688-8e99-40a2-a161-e048858ebd14(en-us,MSDN.10).gif

    Figure 10. The Windows Mobile 5.0 SelectPictureDialog dialog

  4. Select one of the thumbnails. The selected image is now associated with the property.

    The application now allows the user to select the appropriate picture unlike the earlier version of the application that required the user to know the file name of the desired image.

  5. Click Menu | Exit to close the application.

    In the Target Device list, you'll notice that there are no Pocket PC 2003 emulators displayed. The Pocket PC 2003 emulators are not displayed because Visual Studio 2005 only shows those emulators that are associated with the SDK the project is targeting. To test the application with an emulator that is not part of the targeted SDK, you must treat the emulator like an actual device.

Conclusion

Note

The technique you will use to test the application with the Pocket PC 2003 emulator could have been used at the end of Exercise 1 to run the WhoAmI application on the Windows Mobile 5.0 Smartphone emulator as an alternative to changing the project to target the Windows Mobile 5.0 Smartphone SDK. In Exercise 1, both techniques are equally valid because the WhoAmI application has no features that are specific to the Windows Mobile 5.0 Smartphone SDK or the Windows Mobile 5.0 Pocket PC SDK. In this exercise, retargeting the project is not an option. The application is being verified to run on Pocket PC 2003 but must maintain all of the Windows Mobile 5.0-related features. Retargeting the project to Pocket PC 2003 would interfere with the need to include Windows Mobile 5.0 features.

To test property photo image selection on Pocket PC 2003 Second Edition

  1. In Visual Studio, click Tools | Device Emulator Manager to open the Device Emulator Manager.

  2. In the Device Emulator Manager, locate Pocket PC 2003 SE Emulator; you may need to expand the Pocket PC 2003 node. Right-click Pocket PC 2003 SE Emulator, and then select Connect.

    The emulator starts and a green arrow appears to the left of Pocket PC 2003 SE Emulator in the Device Emulator Manager.

  3. After the emulator starts, right-click Pocket PC 2003 SE Emulator in the Device Emulator Manager, and then select Cradle.

    Cradling the emulator starts ActiveSync. ActiveSync may take a few moments to start, so be patient until you see the New Partnership dialog box displayed.

    Note

    The ActiveSync New Partnership dialog box has the text Set Up a Partnership displayed in large letters near the top of the emulator. This is the dialog box that you are looking for. Even though the dialog box is technically the New Partnership dialog box, the Set Up a Partnership text is much more easily seen.

  4. When prompted by the New Partnership dialog box, select Guest partnership, and then click the Next button.

    Choosing the Guest partnership establishes an ActiveSync connection without attempting to synchronize content between the emulator and your desktop computer. After the emulator is cradled and the ActiveSync connection is established, the Pocket PC 2003 SE emulator appears on your desktop computer (or virtual PC) as a real device.

  5. In the Target Device list, select Windows Mobile 5.0 Pocket PC Device.

  6. Click Debug | Start Debugging. The application starts in the Pocket PC 2003 SE emulator.

  7. On the emulator, click Menu | Select New Photo. Notice the application lists the property photo image file names as it originally did, as shown in Figure 7.

  8. Select one of the files. The selected property photo image contained in the file is now associated with the property.

  9. Click Menu | Exit to close the application.

  10. Disconnect the Pocket PC 2003 SE emulator from ActiveSync by right-clicking Pocket PC 2003 SE Emulator in the Device Emulator Manager, and then selecting Uncradle.

Another opportunity to provide the user with a higher quality experience is to take advantage of phone capabilities. You will now modify the application so it automatically dials the phone if the device provides phone capability. If the device does not have a phone, the application displays the phone number in a message box just as it did originally.

To add automatic phone dialing

  1. In Solution Explorer, right-click Form1.cs, and then select View Code.

  2. Locate the Call Home Owner menu and click event handler, menuCallHomeOwner_Click.

    Notice that the current implementation is very simple with only one line—a call to the MessageBox.Show method to display the phone number.

    Note

    For simplicity, the application displays the same phone number for each property owner.

  3. In Solution Explorer, double-click PlatformInfo.cs to open the file. Notice that the file contains an implementation of the PlatformInfo class. This implementation is identical to the PlatformInfo class that you created in Exercise 1 and contains the same three public properties: IsSmartPhone, IsPocketPc, and IsAPhone. Using this class, you will modify the Form1.menuCallHomeOwner_Click method to adapt its behavior to the device capabilities.

    Prior to Windows Mobile 5.0, there was no .NET Compact Framework class that provided phone dialing ability. To make a call, a .NET Compact Framework application would instead use the platform invoke facility to call the Win32 PhoneMakeCall function.

  4. In Solution Explorer, double-click NativeFunctionHelper.cs to open the file.

  5. Locate the MakeCall method.

    The MakeCall method is a .NET Compact Framework friendly method that encapsulates the details of using the Win32 PhoneMakeCall function. The MakeCall method takes two parameters: a string parameter that contains the phone number to dial and a bool parameter that indicates whether to prompt the user before placing the call. You will use the MakeCall method to add automatic dialing to the Form1.menuCallHomeOwner_Click method.

  6. Again, open Form1.cs in Code view, and then locate the menuCallHomeOwner_Click method.

  7. Add an if/else statement block to the menuCallHomeOwner_Click method, as shown in the following code example. The if condition tests the device phone capability using the PlatformInfo.IsAPhone property. The existing MessageBox.Show method call is in the else block.

    if (PlatformInfo.IsAPhone)
    {
    }
    else
    {
        MessageBox.Show(PropertyPhoneNumber, @"Owner Phone Number");
    }
    
  8. In the true part of the if block, call the NativeFunctionHelper.MakeCall method passing the Form1.PropertyPhoneNumber property as the first parameter and false as the second parameter, as shown in the following code example. Passing false as the second parameter indicates that the call is to be placed without prompting the user.

    NativeFunctionHelper.MakeCall(PropertyPhoneNumber, false);
    

    At this point, the application provides the required functionality of automatically placing the phone call if the device has a phone. There is one more step you can take to make the application even better.

    The Windows Mobile 5.0 managed library provides a Phone class. The Phone class has a Talk method that, when called, automatically places the call. Because the Phone.Talk method is currently implemented, it provides no additional features over the NativeFunctionHelper.MakeCall method; however, this fact may not remain true indefinitely. As the Windows Mobile platform evolves, factors like security may make avoiding calls directly to native Win32 functions beneficial.

    The Phone class is implemented in the Microsoft.WindowsMobile.Telephony assembly.

  9. Add a reference to the Microsoft.WindowsMobile.Telephony assembly following the same steps that you used to add a reference to the Microsoft.WindowsMobile.Forms assembly previously in this exercise.

  10. At the top of Form1.cs, add a using declaration for the "Microsoft.WindowsMobile.Telephony" namespace.

    Whether using the Win32 function or the managed method, placing the call requires that the device has a phone; therefore, the logic to determine whether to use the native function or managed method will be in the true side of the existing if block that checks the PlatformInfo.IsAPhone property.

  11. In the true side of the existing if block, add another if/else statement block, as shown in the following code example. The condition of the new if statement tests whether the Windows Mobile version is less then 5 using the Environment.OSVersion.Version.Major property. The existing call to the NativeFunctionHelp.MakeCall method is in the true side of the new if block.

    if (Environment.OSVersion.Version.Major < 5)
    {
        NativeFunctionHelper.MakeCall(PropertyPhoneNumber, false);
    }
    else
    {
    }
    
  12. In the else side of the statement block, declare and create an instance of the Phone class named p, and then call the p.Talk method passing the Form1.PropertyPhoneNumber property.

  13. Verify that the complete menuCallHomeOwner_Click method looks like the following code example.

    private void menuCallHomeOwner_Click(object sender, EventArgs e)
    {
        if (PlatformInfo.IsAPhone)
        {
            if (Environment.OSVersion.Version.Major < 5)
            {
                NativeFunctionHelper.MakeCall(PropertyPhoneNumber, false);
            }
            else
            {
                Phone p = new Phone();
                p.Talk(PropertyPhoneNumber);
            }
        }
        else
        {
            MessageBox.Show(PropertyPhoneNumber, @"Owner Phone Number");
        }
    }
    

To test the automatic dialing feature on Windows Mobile 5.0 Pocket PC Phone Emulator

  1. In the Target Device list, select Windows Mobile 5.0 Pocket PC Phone Emulator.

  2. Click Debug | Start Debugging.

  3. When the application starts, click Menu | Call Home Owner. Notice that the device displays the phone application and dials the call, as shown in Figure 11.

    Bb278110.0369c475-c0a9-499c-a812-205bb48fcf90(en-us,MSDN.10).gif

    Figure 11. The phone interface dialing a call that was initiated by the application

  4. End the phone call by clicking the End button on the phone application, and then minimize the phone application by clicking the X in the upper-right corner.

  5. Close the application by clicking Menu | Exit.

To test the automatic dialing feature on Windows Mobile 5.0 Pocket PC

  1. In the Target Device list, select Windows Mobile 5.0 Pocket PC Emulator.

  2. Click Debug | Start Debugging.

  3. When the application starts, click Menu | Call Home Owner. Notice that the application displays the phone number in a message box—just as it originally did, as shown in Figure 8. This is the correct behavior because the Windows Mobile 5.0 Pocket PC Emulator does not have a phone.

  4. Click OK to close the message box, and then close the application by clicking Menu | Exit.

Exercise 3: Supporting Both Landscape and Portrait Displays

An increasing number of Windows Mobile powered devices have landscape displays. Some devices may be landscape all of the time; others may switch dynamically between portrait and landscape. To maximize the opportunity for your Windows Mobile–based applications and to minimize undesirable support calls, your applications should support both portrait and landscape display. The application that you used in Exercise 2, RealEstateMgr currently supports portrait display. In this exercise, you will modify the RealEstateMgr application to support both portrait and landscape displays.

To view the existing application in landscape

  1. In Visual Studio 2005, click File | Open | Project/Solution.

  2. In the Open Project dialog box, browse to C:\Program Files\Windows Mobile Developer Samples\HOLs\MEDC06_HOL311\Exercises\RealEstateMgr.

  3. Select RealEstateMgr.sln, and then click Open.

  4. In Solution Explorer, double-click Form1.cs to open the file in the form designer.

  5. Review the Form1.cs application form, as shown in Figure 12. Notice that the controls all use absolute positioning and assume a screen size of 240 x 320.

    Bb278110.d0d84191-54f8-4f38-bcc2-6529f106de7f(en-us,MSDN.10).gif

    Figure 12. Main application form using absolute positioning displayed as portrait

  6. In Visual Studio, open PropertyDetailForm.cs in the form designer.

  7. Review the PropertyDetailForm.cs application form, as shown in Figure 13. Notice that as with Form1.cs, the controls all use absolute positioning and assume a screen size of 240 x 320.

    Bb278110.c912bc41-8a2d-49a5-b54b-ea29d60d53c8(en-us,MSDN.10).gif

    Figure 13. Application detail form using absolute positioning displayed as portrait

  8. In the Target Device list, select Windows Mobile 5.0 Pocket PC Emulator if it's not already selected.

  9. Click Debug | Start Debugging to start the application.

  10. While viewing the application's Home View screen, rotate the emulator to landscape by clicking the emulator's App Launch 2 button, which is below the screen and to the right of the navigation pad. It has a symbol that looks like a calendar, as shown in Figure 14.

    Bb278110.a95180f9-e1a8-473f-80dc-8b69eef61263(en-us,MSDN.10).gif

    Figure 14. The Windows Mobile 5.0 Emulator's App Launch 2 button that rotates the emulator display

    Figure 15 shows the application running on a landscape display. Notice in Figure 15 that the application immediately becomes difficult to use because the property photo is obscured. Notice also that the application isn't making good use of available space. There is room on the screen to display both the grid and the property photo; the controls just need to be rearranged a little.

    Bb278110.d3d33880-e6f7-476a-b6b2-772fccbc526c(en-us,MSDN.10).gif

    Figure 15. The main application form displaying incorrectly in landscape

  11. Rotate the emulator back to portrait by clicking the emulator's App Launch 2 button.

  12. Click Details. The Details screen appears, as shown in Figure 16.

    Bb278110.65be548f-4ae0-4062-8fc9-7b4fcafdab0a(en-us,MSDN.10).gif

    Figure 16. Application detail form displayed in portrait

  13. Rotate the emulator again to landscape, and then notice that the Details screen is also obscured when the application is in landscape.

  14. Return the emulator to portrait, and then click Done to close the Details screen.

  15. Click Menu | Exit to close the application.

    Handling both landscape and portrait displays requires that the application support some form of dynamic layout. One possible solution is to design separate portrait and landscape versions of the application forms. This technique works, but keeping the two sets of forms synchronized can be challenging. A more common technique for handling both landscape and portrait displays is to have one set of forms with the forms dynamically moving the controls on the form between a portrait-friendly and a landscape-friendly layout.

To simplify managing the different display layouts, the project includes the DisplayInfo class that is in the DisplayInfo.cs file.

To update the application to support landscape

  1. Open DisplayInfo.cs, and then locate the GetDisplayMode method.

    The GetDisplayMode method encapsulates the details of determining whether the current display is portrait or landscape. As you can see, the determination is rather simple. The method simply compares the screen's height and width. The GetDisplayMode method's return value is an enumeration, DisplayMode, that contains two values: Portrait and Landscape.

  2. In Solution Explorer, double-click Form1.cs to open the file in the form designer.

  3. Ensure that the form is the currently selected control by single-clicking anywhere within the form where no controls appear.

  4. In the Properties pane, select the events view by clicking the Events (lightning bolt) button on the toolbar, as shown in Figure 17.

    Bb278110.792e4ac6-a4cd-4015-953c-cac5796e1bf2(en-us,MSDN.10).gif

    Figure 17. List of events for the Form1 class

  5. Scroll down the list of events until the Resize event is visible, and then double-click Resize. This step adds the Resize event handler for Form1, Form1_Resize; displays Form1 in Code view; and positions the cursor within the Form1_Resize method.

  6. In the Form1_Resize function, call the AdjustFormLayout method, as shown in the following code example.

    Private void Form1_Resize(object sender, EventArgs e)
    {
      AdjustFormLayout();
    }
    
  7. Locate and review the AdjustFormLayout method.

    The AdjustFormLayout method handles the details of moving between a portrait and landscape layout. Notice that the implementation is simply to call the DisplayInfo.GetDisplayMode method and then modify the size and position of the controls as appropriate for the current display mode (portrait or landscape).

To test the application's support of both landscape and portrait displays

  1. In Visual Studio, click Debug | Start Debugging. Notice that the application still displays correctly when displayed in portrait.

  2. While viewing the application's Home View screen, click the emulator's App Launch 2 button to rotate the emulator to landscape. Figure 18 shows the application now displaying correctly in landscape.

    Bb278110.fb48970a-9dd5-4033-84f7-0cff29bfe69a(en-us,MSDN.10).gif

    Figure 18. The application displaying correctly in landscape

  3. Return the emulator to portrait by clicking the App Launch 2 button again.

  4. Close the application by clicking Menu | Exit.

    Note

    You may notice that when you view the Property Detail form in landscape, some of the display is cut off. The Property Detail form can be updated to properly support landscape display by repositioning the controls, just as the AdjustFormLayout method did in the main form. Alternatively, you can make the PropertyDetailForm form more orientation-aware by setting the PropertyDetailForm form AutoScroll property to true. With the AutoScroll property set to true, the PropertyDetailForm form automatically adds scroll bars to the form in the event that the form controls do not fit on the screen—as is the case when the PropertyDetailForm form is displayed in landscape.

Exercise 4: Adding High-Resolution Support

Another trend in Windows Mobile powered devices is high-resolution displays. Historically, Windows Mobile Pocket PCs have had a screen resolution of 96 dpi, and many devices still have this resolution. Other devices with high-resolution displays have 192 dpi. The purpose of a high-resolution display is not to put more information on the screen; rather it is to provide a higher quality display. These high-resolution displays provide a very sharp display and allow applications to render rich, high-quality graphics and smooth text. When your application is run on a device with a high-resolution display, the controls on your application forms must be scaled appropriately.

To view the existing application in high resolution

  1. In the Target Device list, select Windows Mobile 5.0 Pocket PC VGA Emulator.

  2. Click Debug | Start Debugging to start the application.

  3. Notice in Figure 19 that all of the controls are crowded into the top-left corner of the form. To properly handle the different screen resolutions, each control on the form must have its size and position adjusted to match the display.

    Bb278110.58fef99a-57b4-4613-b11e-c78214a41551(en-us,MSDN.10).gif

    Figure 19. The application displaying incorrectly on a high-resolution display

  4. Click Menu | Exit to close the application.

To update the application to support high-resolutions

  1. Open the DisplayManager.cs file, and then locate the GetDisplayRatios method.

    Notice that the method has three parameters; the first is a reference to a control in the application. This parameter can be any control, but in this application, it will always be an instance of the Form class. The second and third parameters are values returned by this method that contains scaling ratios for the application display. The widthRatio parameter indicates how much to scale the control's size and position on the X axis (left-to-right). The heightRatio parameter is the same except it represents the Y axis (up-and-down).

    Notice the two constants just above the GetDisplayRatio method: designDpiX and DesignDpiY. These both have the value 96, which represents the resolution of the form designer in Visual Studio. These constants serve as the basis for the scaling ratios.

    Note

    The forms designer supports designing in both 96 dpi and 192 dpi. The Form Factor property allows you to select the device that you would like to design for. The Form Factor is a property available on the Form class when working in Visual Studio; you can select a desired Form Factor such as Windows Mobile 5.0 Pocket PC or Windows Mobile Pocket PC VGA. The design-time form will use the characteristics of the selected Form Factor. The default Form Factor for projects that target the Windows Mobile 5.0 Pocket PC SDK is Windows Mobile 5.0 Pocket PC, which has a resolution of 96 dpi.

  2. In the GetDisplayRatios method, locate the call to the CreateGraphics method on the ctrl parameter.

    This call returns an instance of the Graphics class. The Graphics class contains a wide variety of properties and methods that provide information about the current display and allow you to interact with the display. The two properties required for this method are DpiX and DpiY, which contain the dots per inch of the X and Y axes for the current device display.

    By knowing the dpi values that the form was designed for and knowing the dpi of the current display, determining the scaling ratios for the X and Y axes is a simple matter of division, as is done in the last two lines of the GetDisplayRatios method. These values will be used to adjust the size and position of the application controls.

    The program will use the scaling ratios from the GetDisplayRatios method anytime it needs to handle the layout of the form controls. The form control layout is normally handled at the beginning of the application when controls are first being created and then when the user rotates the device display between portrait and landscape. Rather then call the GetDisplayRatios method each time the layout needs to be updated; the method can be called once when the program first starts. To avoid repeatedly calling the GetDisplayRatios method, the width and height ratios returned from the GetDisplayRatios method need to be stored in class member variables. The two class member variables, _widthRatio and _heightRatio, are already declared and are intended for this purpose.

  3. Open Form1.cs in Code view, and then locate the Form1_Load method.

  4. As the first line in the method, call DisplayInfo.GetDisplayRatios passing the Form1 class's this reference, followed by the _widthRatio and _heightRatio member variables. Remember to pass _widthRatio and _heightRatio as reference parameters, as shown in the following code example.

    private void Form1_Load(object sender, EventArgs e)
        { 
            DisplayInfo.GetDisplayRatios(this, ref _widthRatio, ref _heightRatio);
    
  5. Locate the ScaleFormControls method within the Form1 class.

    The purpose of the ScaleFormControls method is to reposition and resize the form's controls correctly for the current display resolution. To do this, the method uses the _widthRatio and _heightRatio class member variables that are populated by the call to the GetDisplayRatios method that you added to Form1_Load method. You'll notice that the declarations for both of these class member variables are just a couple lines above the ScaleFormControls method.

    The ScaleFormControls method starts by calling the DisplayInfo.GetDisplayMode method to determine if the current display is portrait or landscape. It then scales the size of the DataGrid and PictureBox controls. You'll notice that the scaling of the PictureBox location varies depending on the orientation. The location changes because the PictureBox control must be positioned relative to the DataGrid control. In Portrait orientation, the PictureBox is under the DataGrid and has to move down to avoid being covered by the DataGrid control. In landscape, the PictureBox is right of the DataGrid and must move to the right to avoid being covered by the DataGrid control. The last thing the ScaleFormControls method does is call the ScaleGridMembers method that scales the columns within the DataGrid using a similar technique to that used in the ScaleFormControls method to scale the control.

    The ScaleFormControls method handles the details of scaling the controls appropriately for the display resolution, and the AdjustFormControls method handles the details of repositioning and resizing the controls appropriately for the current display orientation. To handle both issues correctly, the scaling must be done after the controls' positions and sizes for the current orientation are determined but before the controls are actually repositioned and resized.

  6. Locate the AdjustFormLayout method (it's just above the ScaleFormControls method). Notice that most of the code in this method handles the repositioning and resizing of the controls. The last three lines, however, assign the new size and location information to the controls.

  7. Add a call just above these three lines to ScaleFormControls, as shown in the following code example.

        ScaleFormControls(ref gridSize, ref pictureBoxLocation, ref pictureBoxSize);
        propertyDataGrid.Size = gridSize;
        propertyPictureBox.Location = pictureBoxLocation;
        propertyPictureBox.Size = pictureBoxSize;
    }
    

To test that the application supports both high-resolution and standard-resolution displays

  1. In the Target Device list, select Windows Mobile 5.0 Pocket PC VGA Emulator.

  2. Click Debug | Start Debugging to start the application.

  3. While viewing the application's Home View screen, rotate the emulator orientation to landscape, as shown in Figure 20. In most cases, you'll be able to rotate the emulator by clicking the emulator's App Launch 2 button as you did in previous exercises.

    Bb278110.216a90df-278c-409c-9cc4-a7fb26b130c2(en-us,MSDN.10).gif

    Figure 20. The application correctly displayed in landscape on a high-resolution display

    In some cases, the screen resolution of your desktop computer (or virtual PC) may not provide enough space to allow you to access the emulator App Launch 2 button. If you find this to be the case, change the display resolution to 1280 x 1024. To change the display resolution, right-click the computer (or virtual PC) desktop and select Properties. On the Display Properties dialog box, click the Settings tab, slide the Screen resolution slider bar to 1280 x 1024, and then click OK. Now click the App Launch 2 button on the emulator to rotate the emulator to landscape.

    If the desktop computer (or virtual PC) that you are using does not support a resolution of 1280 x 1024 (or any other resolution that provides enough screen space to access the emulator App Launch 2 button), you will need to rotate the emulator by modifying the emulator properties. You can access the emulator properties by clicking File | Configure on the Windows Mobile 5.0 Pocket PC VGA Emulator menu. In the Emulator Properties dialog box, click the Display tab, select 90 from the Orientation list, and then click OK. You'll notice that the emulator rotates to landscape, but the application is still displayed as portrait. The emulator App Launch 2 button is now accessible. Click the App Launch 2 button, and the application now redraws as landscape. Both the emulator and application are properly displayed in landscape.

    Note

    The application doesn't redraw in the appropriate orientation when you change the emulator orientation by using the Emulator Properties dialog box. When changing the orientation by using the Emulator Properties dialog box, the emulator does not notify Windows Mobile of the orientation change; therefore, Windows Mobile cannot notify the application of the change in orientation. This is true for all applications running in the emulator.

  4. Click the App Launch 2 button again to return the emulator to portrait, as shown in Figure 21.

    Bb278110.a1623a5b-39cb-4473-b892-319c5bdaa4af(en-us,MSDN.10).gif

    Figure 21. The application correctly displayed in portrait on a high-resolution display

    Note

    If you had to rotate the emulator by changing the emulator properties in Step 3 because the App Launch 2 button was not accessible in your desktop computer's (or virtual PC) supported screen resolutions, reverse Steps 4 and 5—close the application before rotating the emulator to portrait. If you rotate the emulator to portrait before closing the application, your desktop computer's (or virtual PC's) screen resolution may prevent you from accessing the application menu to close the application.

  5. Close the application by clicking Menu | Exit.

  6. Repeat the preceding steps using the Windows Mobile 5.0 Pocket PC Emulator to verify that the application still displays correctly in both portrait and landscape on a 96 dpi display.

Exercise 5: Supporting Square and Other Displays

In addition to the portrait and landscape displays, Windows Mobile also supports devices with square displays. Adding support for these devices requires a little more flexibility in the application. In this exercise, you will modify the application to work with square displays and any other displays that the application may encounter. Adding this flexibility ensures that the application provides a reasonable viewing experience on all Windows Mobile powered device displays—even those that may not exist yet.

To test the application on a square display

  1. In Visual Studio, select Windows Mobile 5.0 Pocket PC Square Emulator in the Target Device list.

  2. Click Debug | Start Debugging to start the application.

  3. Notice that the property picture is cut off, as shown in Figure 22.

    Bb278110.fd525460-5001-4b3c-8656-0dde1f60282e(en-us,MSDN.10).gif

    Figure 22. The application on a square display with part of the property picture cut off

  4. Click Menu | Exit to close the application.

    All of the application's display modifications so far have maintained the application's fundamental display format of having two forms. The main application form displays the list of properties and a picture of the currently selected property. The other form displays the property details when the user requests to see these details by clicking the Details command on the main form. The two-form format works well for portrait and landscape displays.

    The space available on a square display makes maintaining the layout of the main form difficult because there isn't room on a square display to proportionally fit both the property listing and photo. In the case of the square display, allowing the property grid to occupy the entire main form and moving the property photo to a separate form creates a much more aesthetic and functional display.

In this exercise, that's exactly what you're going to do—modify the application so, in the case of a square display, the application uses three forms rather than the two forms that are used for portrait and landscape displays. In this three-form layout, the property listing occupies the entire main form, and the main form now has an additional command, View Photo, that displays the form containing the property photo. The property photo is now on its own form and displays the photo corresponding to the property that is currently selected on the main form when the user selects the View Photo command. The process of viewing the property detail information is unchanged; property detail information is still on its own form and displays the details for the property that is currently selected on the main form when the user selects the Details command.

To update the application to support square-display devices

  1. Open DisplayInfo.cs in Code view.

  2. Locate the DisplayMode enumeration, and then add a Simple value, as shown in the following code example.

    The DisplayMode.Simple enumeration identifies that neither the portrait or landscape layouts are appropriate. A DisplayMode value of Simple indicates that the application is using the new three form layout.

    enum DisplayMode
    {
        Portrait,
        Landscape,
        Simple
    }
    
  3. Locate the GetDisplayMode method in the DisplayInfo class.

    This method will be modified to return the DisplayMode.Simple value in addition to Portrait and Landscape. One way to implement GetDisplayMode is to return Portrait when the screenBounds.Height property is greater than the screenBounds.Width property, Landscape when the reverse is true, and Simple when they are equal. The shortcoming in this implementation is that it assumes that either the portrait or landscape layout will work for all display types other than those that are exactly square. This is not a totally safe assumption.

    Although none exist today, there may be a time when devices have a nearly square display—where the display height is just a little greater than the display width or vice versa. In these cases, a square-like layout is more appropriate than portrait or landscape. To address this situation, the GetDisplayMode method identifies ranges that each layout is appropriate for. In the case of the portrait layout, the display must have a width-to-height display ratio less than 0.8. For the landscape layout, the width-to-height display ratio must be greater than 1.3. Any display with a width-to-height ratio between 0.8 and 1.3 inclusive uses the Simple layout.

    Note

    There is no absolute rule about the appropriate width-to-height ratio for each of the display layouts, but the width-to-height ratios used in this exercise serve as a good guideline. The display layouts used in this exercise are based on the width-to-height display ratios used by currently available Pocket PCs. Today, all portrait display Pocket PCs have a display size of 240 x 320 or 480 x 640, both of which are a width-to-height display ratio just under 0.8 (0.75). Similarly, all landscape display Pocket PCs that are available today have a display size of 320 x 240 or 640 x 480, both of which are a width-to-height display ratio are greater than 1.3 (1.3333…). Although the portrait and landscape width-to-height display ratios are based on common Pocket PC display sizes, the ratios work equally well when determining layout requirements for Smartphones.

  4. Rather than hardcode the display ratio boundaries, declare two constants with a data type of float just before the GetDisplayMode method. Name the constants minSimpleDisplayRatio and maxSimpleDisplayRatio. Assign the constants values of 0.8F and 1.3F respectively, as shown in the following code example.

    const float minSimpleDisplayRatio = 0.8F;
    const float maxSimpleDisplayRatio = 1.3F;
    
  5. Within the GetDisplayMode method just after the declaration of the screenBounds variable, calculate the width-to-height display ratio. First declare a variable named widthToHeightRatio with a data type of float. Calculate the width-to-height display ratio by dividing the screenBounds.Width property by the screenBounds.Height property, and then assign the result to the widthToHeightRatio variable. In the calculation, cast the Width and Height properties to float or the calculation will use integer division (whole numbers only, no fractional values), as shown in the following code example.

    float widthToHeightRatio = (float)screenBounds.Width / (float)screenBounds.Height;
    
  6. Modify the condition of the existing if/else statement block so the mode variable is assigned the DisplayMode.Portrait value when the widthToHeightRatio variable is less than the minSimpleDisplayRatio constant. Change the existing else to an else if statement with a condition that assigns the DisplayMode.Landscape value to the mode variable when the widthToHeightRatio is greater than the maxSimpleDisplayRatio constant. Add an else block that sets the mode variable to the DisplayMode.Simple value if the other two conditions test false.

  7. Verify that the complete GetDisplayMode method looks like the following code example.

    public static DisplayMode GetDisplayMode()
    {
        Rectangle screenBounds = Screen.PrimaryScreen.Bounds;
        DisplayMode mode = DisplayMode.Simple ;
        float widthToHeightRatio = (float)screenBounds.Width / (float)screenBounds.Height;
        if (widthToHeightRatio < minSimpleDisplayRatio)
        {
            mode = DisplayMode.Portrait;
        }
        else if (widthToHeightRatio > maxSimpleDisplayRatio)
        {
            mode = DisplayMode.Landscape;
        }
        else
        {
            mode = DisplayMode.Simple;
        }
        return mode;
    
    }
    
  8. With the DisplayInfo.GetDisplayMode method complete, open ImageForm.cs in the form designer.

    Notice that the form contains a PictureBox control whose Dock property is set to Fill. Other than being set to fill the form, this PictureBox control has the same properties as the PictureBox control on the main form. The ImageForm form is used only when the DisplayInfo.GetDisplayMode method returns a DisplayMode.Simple value.

  9. Open ImageForm.cs in Code view.

    Notice that the implementation of the ImageForm class has only one public member: the SetBindingSource method. The SetBindingSource method is used to set the BindingSource for the ImageForm class to the same BindingSource as the Form1 and PropertyDetailForm forms. The private members of the ImageForm class are primarily housekeeping methods that are used to display the property photo each time the BindingSource's current record changes.

  10. Open Form1.cs in Code view, and then locate the PropertyDetailForm member variable declaration near the beginning of the class.

  11. Immediately following the _propertyDetailForm class member declaration, declare a class member variable menuViewPhoto with a data type of MenuItem. Following the menuViewPhoto class member declaration, add a class member declaration for the ImageForm form named _imageForm, as shown in the following code example.

    Bitmap _imageBitmap;
    PropertyDetailForm _propertyDetailForm;
    MenuItem menuViewPhoto;
    ImageForm _imageForm;
    
  12. Add a new method to the Form1 class named menuViewPhoto_Click. The menuViewPhoto_Click method handles the click event for a command that you will be adding soon, so it must conform to the delegate signature that is required for menu Click event handlers, and it must have a return type of void and two parameters: an object parameter and an EventArgs parameter.

  13. In the menuViewPhoto_Click method, call the ShowDialog method on the _imageForm member variable, but only do so when the _imageForm member variable does not have a value of null, as shown in the following code example.

    private void menuViewPhoto_Click(object sender, EventArgs e)
    {
        if (_imageForm != null)
            _imageForm.ShowDialog();
    }
    
  14. Locate the Form1_Load method. In the Form1_Load method, add an if statement block immediately following the call to DisplayImage. In the if condition, call the DisplayInfo.GetDisplayMode method and test to see if the return value is DisplayMode.Simple, as shown in the following code example.

    if (DisplayInfo.GetDisplayMode() == DisplayMode.Simple)
    {
    }
    
  15. In the if block, create the _imageForm instance, and then call the _imageForm.SetBindingSource method passing in the propertyBindingSource property, as shown in the following code example. The propertyBindingSource is the same BindingSource used by Form1 and the PropertyDetailForm class. By binding to the propertyBindingSource, the property photo on the ImageForm automatically stays synchronized with the property selection on Form1.

    _imageForm = new ImageForm();
    _imageForm.SetBindingSource(propertyBindingSource);
    

    In the DisplayMode.Simple layout, the property photo is displayed on the ImageForm; therefore, the property PictureBox on Form1 should not be shown.

  16. In the if block, hide the PictureBox by setting the propertyPictureBox.Visible property to false, as shown in the following code example.

    propertyPictureBox.Visible = false;
    
  17. The property DataGrid can now occupy all of Form1, so set the propertyDataGrid.Dock property to DockStyle.Fill in the if block, as shown in the following code example.

    propertyDataGrid.Dock = DockStyle.Fill;
    

    When the application is using the DisplayMode.Simple layout, all of Form1 is occupied by the property listing; therefore, the user needs a command to display the ImageForm form.

  18. In the if block, create a new MenuItem instance, and then assign it to the menuViewPhoto class member variable. Set the menuViewPhoto.Text property to View Photo, as shown in the following code example.

    menuViewPhoto = new MenuItem();
    menuViewPhoto.Text = "View Photo";
    
  19. Associate the menuViewPhoto_Click method that you created earlier with the menuViewPhoto.Click event. Finally, add the menuViewPhoto class member to the menuPopup.MenuItems collection.

  20. Verify that the complete Form1_Load method looks like the following code example.

    private void Form1_Load(object sender, EventArgs e)
    {
        // Get values for control scaling
        DisplayInfo.GetDisplayRatios(this, ref _widthRatio, ref _heightRatio);
        PopulateHouseViewDataSet();
        _propertyDetailForm = new PropertyDetailForm();
        _propertyDetailForm.SetBindingSource(propertyBindingSource);
        DisplayImage();
        // In Simple mode, picture is displayed on a separate form
        if (DisplayInfo.GetDisplayMode() == DisplayMode.Simple)
        {
            // Create the image form and hook up data binding
            _imageForm = new ImageForm();
            _imageForm.SetBindingSource(propertyBindingSource);
            // Hide the picture box on Form1 and set the grid
            // to occupy the whole form
            propertyPictureBox.Visible = false;
            propertyDataGrid.Dock = DockStyle.Fill;
            // Add a command to display image
            menuViewPhoto = new MenuItem();
            menuViewPhoto.Text = "View Photo";
            menuViewPhoto.Click += menuViewPhoto_Click;
            menuPopup.MenuItems.Add(menuViewPhoto);
        }
    }
    

    Note

    For simplicity, the Form1_Load method and the rest of the lab code focuses on providing easy-to-read code that is related to managing device differences rather than focusing on code optimization at the cost of readability. The Form1_Load method is one such case where this is true. If desired, you can add a slight resource optimization to the Form1_Load method by adding an else block to the existing if statement block, so the DisplayImage method only gets called when the layout is not DisplayMode.Simple.

    The DisplayImage method updates the PictureBox on the main form, so the image that is displayed corresponds to the currently selected property. When the application is using the DisplayMode.Simple layout, the PictureBox on the main form is hidden; therefore, the call to the DisplayImage method has no visible effect on the application, but it consumes some resources by creating a BitMap instance. Although changing the Form1_Load method to not call the DisplayImage method saves some resources, the savings is not that significant because the Form1_Load method is only called once at the beginning of the application. An even better optimization is to modify the DisplayImage method itself, so it only updates the image displayed in the PictureBox when the layout is DisplayMode.Simple.

  21. Locate the AdjustFormLayout method.

    Locate the last three lines in the AdjustFormLayout method. They set the size of the propertyDataGrid and propertyPictureBox controls and the location of the propertyPictureBox control. These assignments are only necessary for the DisplayMode.Portrait and DisplayMode.Landscape layouts because, in the DisplayMode.Simple layout, the propertyPictureBox is hidden and the propertyDataGrid is set to fill the form.

  22. To prevent these assignments from happening when using the simple layout, place the three statements in an if statement block that checks that the dispMode variable is not set to DisplayMode.Simple.

    With the new if statement block added, the last several lines of the AdjustFormLayout method look like the following code example.

        ScaleFormControls(ref gridSize, ref pictureBoxLocation, ref pictureBoxSize);
        if (dispMode != DisplayMode.Simple)
        {
            propertyDataGrid.Size = gridSize;
            propertyPictureBox.Location = pictureBoxLocation;
            propertyPictureBox.Size = pictureBoxSize;
        }
    }
    
  23. Locate the ScaleFormControls method in the Form1 class.

    You need to modify the ScaleFormControls method so the DataGrid and PictureBox are only scaled when the application is using a DisplayMode.Portrait or DisplayMode.Landscape layout.

  24. In the ScaleFormControls method, add an if/else statement block immediately following the call to DisplayInfo.GetDisplayMode.

    The if condition tests whether the dispMode variable has a value of DisplayMode.Simple. The else block contains all of the remaining code in the method except the call to the ScaleGridMembers method. The call to the ScaleGridMembers method is just after the end of the else block because the columns within the DataGrid need to be appropriately sized for all display layouts.

  25. Add a line to the true part of the if block that divides the propertyDataGrid's width by the gridDesignWidth constant and assigns the result to the gridWidthRatio variable.

    The completed ScaleFormControls method looks like the following code example.

    void ScaleFormControls(ref Size gridSize, ref Point pictureBoxLocation, ref Size pictureBoxSize)
    {
        float gridWidthRatio = 0;
        DisplayMode dispMode = DisplayInfo.GetDisplayMode();
        if (dispMode == DisplayMode.Simple)
        {
            // In default layout, the size of the grid is not
            // adjusted but
            // the grid may still be a different size then at 
            // design-time
            // because in default layout, the grid is set to fill 
            // the form
            gridWidthRatio = propertyDataGrid.Size.Width / gridDesignWidth;
    
        }
        else
        {
            gridSize.Width = (int)(gridSize.Width * _widthRatio);
            gridSize.Height = (int)(gridSize.Height * _heightRatio);
            pictureBoxSize.Width = (int)(pictureBoxSize.Width * _widthRatio);
            pictureBoxSize.Height = (int)(pictureBoxSize.Height * _heightRatio);
            gridWidthRatio = gridSize.Width / gridDesignWidth;
            if (dispMode == DisplayMode.Portrait)
                // portrait
                pictureBoxLocation.Y = (int)(pictureBoxLocation.Y * _heightRatio);
            else if (dispMode == DisplayMode.Landscape)
                // landscape
                pictureBoxLocation.X = (int)(pictureBoxLocation.X * _widthRatio);
        }
    
        // Must always adjust grid members to correspond
        // to the grid's current width
        ScaleGridMembers(propertyDataGrid, gridWidthRatio);
    }
    

To test the final application on the square-screen emulator

  1. In the Target Device list in Visual Studio, select Windows Mobile 5.0 Pocket PC Square Emulator if it isn't already selected.

  2. Click Debug | Start Debugging to start the application.

  3. After the application starts, notice that only the data grid appears on the Home View screen, as shown in Figure 23.

    Bb278110.b8b92b53-6386-4381-86be-171fe3f8f0ac(en-us,MSDN.10).gif

    Figure 23. The application displaying the Home View in simple mode

  4. Click Menu | View Photo to display the selected property's picture on the ImageForm, as shown in Figure 24.

    Bb278110.bb4b6687-f265-4f4d-aeb2-79954002412e(en-us,MSDN.10).gif

    Figure 24. The application ImageForm

  5. Click Done to close the ImageForm screen and return to the Home View screen.

  6. Click Menu | Exit to close the application.

To test the final application on other emulators

  1. In the Target Device list, select Windows Mobile 5.0 Pocket PC Emulator.

  2. Click Debug | Start Debugging to start the application.

  3. Observe the behavior of the application in both portrait and landscape modes as you did in previous exercises. Notice that the property listing and property photo are still displayed on the Home View screen as before. Also notice that the Menu | View Image command is not available in these modes because it's not needed.

  4. If time allows, try running the application on the various other emulators and note that it behaves appropriately each case.

Conclusion

In this lab, you performed the following exercises:

  • Identifying device characteristics
  • Adapting to device capabilities
  • Supporting both landscape and portrait displays
  • Adding high-resolution support
  • Supporting square and other displays

Terminating an Application That Is Running on a Device or Emulator

This section describes how to terminate an application that is running on a device or emulator. This is useful for cases where an application has been started without having the debugger attached and the application needs to be terminated so a new copy of the application can be deployed. You will terminate the application by using the Remote Process Viewer remote tool in Visual Studio.

Before you can terminate a process, you need to know the name of the executable file. In most cases, this is the same name as the Visual Studio project. If you are uncertain about the name of the executable file, you can find it in the project's properties.

To terminate an application that is running on a device or emulator by using the Remote Process Viewer remote tool

  1. In Visual Studio, click Project | xxx Properties, where xxx represents the name of the current project.

  2. In the Project Properties dialog box, click Configuration Properties | Linker | General | Output File field. Note the value of the Output File field. This is the name that the executable file will be running on the device or emulator.

  3. Close the Properties dialog box.

    Now, you can terminate the process.

  4. On the desktop computer, click Start | Microsoft Visual Studio 2005 | Visual Studio Remote Tools | Remote Process Viewer.

  5. When prompted by the Select a Windows CE Device dialog box, select the emulator or device where the application is running, as shown in Figure 25, and then click OK.

    Bb278110.8d91cc80-a91e-4045-a484-0e4c06ba9034(en-us,MSDN.10).gif

    Figure 25. Select a Windows CE Device dialog box

  6. After the connection to the emulator or device completes, select the application that you want to terminate in the top pane of the Remote Process Viewer, as shown in Figure 26.

    Bb278110.1757bf84-8815-47f1-9130-2b1b0a82708d(en-us,MSDN.10).gif

    Figure 26. Selecting the application to terminate

    You may need to widen the Process column (the leftmost column) to fully view the process names.

  7. In Windows CE Remote Process Viewer, click File | Terminate Process to terminate the process.

    Note

    Be certain that the correct process is selected before you click Terminate Process. Terminating the incorrect process may render the device or emulator unusable, requiring it to be reset before you can use it again.

  8. Verify that the process is terminated by selecting Target | Refresh. Scroll to the top pane of Windows CE Remote Process Viewer again. If the application name still appears, the process was not terminated, and you need to repeat these steps.

Note

Most processes terminate in one try; however, depending on the state of the application, terminating a process occasionally takes two attempts.

Setting Up Your Computer

The following lab files are required to set up your computer to run this HOL:

  • Pre-existing Visual Studio 2005 project, code, and data files.

Note

Only if you are working on this HOL outside of a Microsoft conference will you need to install the data files.

To install these files to the correct path, download and run the following installation program:

MEDC06_HOL311.msi

Note

If you used an emulator in a previous HOL, you should do a hard reset on the emulator before starting this HOL. (On the emulator, click File | Reset | Hard.)

Note

If you receive an error during deployment that indicates that the process or file is in use, this means that the program is still running on the emulator and must be closed before a new copy can be deployed and run. This error may appear anytime in the lab that you deploy the emulator. For more information about exiting a running application, see Terminating an Application That Is Running on a Device or Emulator in this HOL.