Developing Orientation and dpi Aware Applications for the .NET Compact Framework

 

Microsoft Corporation

March 2004

Applies to:
   Windows Mobile™ 2003 Second Edition software for Pocket PCs
   Microsoft® .NET Compact Framework
   Microsoft Visual Studio® .NET 2003

Summary: This document describes the techniques used to create orientation and dpi aware applications for the .NET Compact Framework. (6 printed pages)

Download Webcrawler.msi from the Microsoft Download Center.

Contents

Introduction
Adding Orientation Awareness
Adding High-Resolution Awareness
Conclusion

Introduction

This document describes the techniques used to create orientation and dpi aware applications for the Microsoft® .NET Compact Framework. To understand the design issues involved in addressing these new scenarios, it is important to read the Developing Screen Orientation Awareness and Developing DPI-Aware Applications whitepapers available in the Developer Resources for Windows Mobile 2003 Second Edition which offer general user interface guidelines.

Adding Orientation Awareness

Introduction

Windows Mobile™ 2003 Second Edition software enables devices with landscape and square screen displays. This is an addition to the traditional portrait-only displays that have been on earlier Pocket PCs. When an application designed for portrait displays is run on a square or landscape display, the operating system will automatically add a vertical scroll bar to the form which enables the user to view the entire form. This happens only when there is a control which would be hidden from the user because of the move to landscape. In most scenarios, this is not the ideal way for a user interface to appear on a landscape display. The two alternatives are to either create a single interface layout that works well under both orientations or to create a separate interface layout for each one.

To work well under all three using a single layout, arrange the form so that all controls are in a square 240x240 region, as shown in Figure 1. This is the easiest solution to implement however it may not be sufficient for complex forms.

Click here for larger image

Figure 1. Portrait and landscape displays with controls set as a square region. Click the thumbnail for a larger image.

To fully take advantage of both portrait and landscape orientations, a separate layout must be defined for each one. Additionally, the Form.Resize event must be used to determine the current orientation and reposition controls as needed.

Note In most scenarios it is not necessary to create a custom layout for square screens. Instead, the portrait layout can be used and the system will automatically add a vertical scrollbar to it.

Create Separate Layout Code for Each Orientation

Define a function for each of the two orientations. In these functions, set the Size and Location properties for each control on the form.

Below is an example of how the WebCrawler sample included in Microsoft Visual Studio® .NET 2003 has been updated to be orientation aware. The same steps can be applied to any existing or new application in order to make it orientation aware.

The Visual Studio .NET 2003 form designer does not explicitly support the creation of multiple layouts but it can still be leveraged as a tool for helping to create this code using this process:

  1. Use the designer to create a layout for Portrait displays.
  2. View the code generated by the designer. This is in a function called InitializeComponent and can be found by expanding the "Windows Form Designer generated code" region.
  3. Create a new function for positioning controls in portrait mode called Portrait() and copy the code that sets the Location and Size properties for controls from InitializeComponent into Portrait().
  4. Use the designer to reposition controls in the layout desired for landscape mode.
  5. Repeat step 3 with the positioning code generated by the designer for landscape mode.
protected void Portrait()
{
   this.crawlTime.Location = new System.Drawing.Point(88, 216);
   this.crawlTime.Size = new System.Drawing.Size(136, 16);
   this.crawlTimeLabel.Location = new System.Drawing.Point(10, 216);
   this.crawlTimeLabel.Size = new System.Drawing.Size(64, 16);
   this.crawlStartTime.Location = new System.Drawing.Point(88, 200);
   this.crawlStartTime.Size = new System.Drawing.Size(136, 16);
   this.crawlStartedLabel.Location = new System.Drawing.Point(10, 200);
   this.crawlStartedLabel.Size = new System.Drawing.Size(64, 16);
   this.light1.Location = new System.Drawing.Point(208, 66);
   this.light1.Size = new System.Drawing.Size(16, 16);
   this.light0.Location = new System.Drawing.Point(192, 66);
   this.light0.Size = new System.Drawing.Size(16, 16);
   this.linkCount.Location = new System.Drawing.Point(88, 182);
   this.linkCount.Size = new System.Drawing.Size(136, 16);
   this.linkCountLabel.Location = new System.Drawing.Point(10, 182);
   this.linkCountLabel.Size = new System.Drawing.Size(64, 16);
   this.currentPageBox.Location = new System.Drawing.Point(10, 84);
   this.currentPageBox.Size = new System.Drawing.Size(214, 90);
   this.currentPageLabel.Location = new System.Drawing.Point(10, 68);
   this.currentPageLabel.Size = new System.Drawing.Size(100, 16);
   this.addressLabel.Location = new System.Drawing.Point(10, 4);
   this.addressLabel.Size = new System.Drawing.Size(214, 16);
   this.noProxyCheck.Location = new System.Drawing.Point(10, 48);
   this.noProxyCheck.Size = new System.Drawing.Size(214, 20);
   this.startButton.Location = new System.Drawing.Point(8, 240);
   this.startButton.Size = new System.Drawing.Size(216, 20);
   this.addressBox.Location = new System.Drawing.Point(10, 24);
   this.addressBox.Size = new System.Drawing.Size(214, 22);
}

protected void Landscape()
{
   this.crawlTime.Location = new System.Drawing.Point(216, 136);
   this.crawlTime.Size = new System.Drawing.Size(96, 16);
   this.crawlTimeLabel.Location = new System.Drawing.Point(160, 136);
   this.crawlTimeLabel.Size = new System.Drawing.Size(48, 16);
   this.crawlStartTime.Location = new System.Drawing.Point(64, 120);
   this.crawlStartTime.Size = new System.Drawing.Size(248, 16);
   this.crawlStartedLabel.Location = new System.Drawing.Point(8, 120);
   this.crawlStartedLabel.Size = new System.Drawing.Size(48, 16);
   this.light1.Location = new System.Drawing.Point(296, 48);
   this.light1.Size = new System.Drawing.Size(16, 16);
   this.light0.Location = new System.Drawing.Point(280, 48);
   this.light0.Size = new System.Drawing.Size(16, 16);
   this.linkCount.Location = new System.Drawing.Point(80, 136);
   this.linkCount.Size = new System.Drawing.Size(72, 16);
   this.linkCountLabel.Location = new System.Drawing.Point(8, 136);
   this.linkCountLabel.Size = new System.Drawing.Size(64, 16);
   this.currentPageBox.Location = new System.Drawing.Point(10, 64);
   this.currentPageBox.Size = new System.Drawing.Size(302, 48);
   this.currentPageLabel.Location = new System.Drawing.Point(10, 48);
   this.currentPageLabel.Size = new System.Drawing.Size(100, 16);
   this.addressLabel.Location = new System.Drawing.Point(10, 4);
   this.addressLabel.Size = new System.Drawing.Size(50, 16);
   this.noProxyCheck.Location = new System.Drawing.Point(168, 16);
   this.noProxyCheck.Size = new System.Drawing.Size(152, 24);
   this.startButton.Location = new System.Drawing.Point(8, 160);
   this.startButton.Size = new System.Drawing.Size(304, 20);
   this.addressBox.Location = new System.Drawing.Point(10, 20);
   this.addressBox.Size = new System.Drawing.Size(150, 22);
}

Handle the Changing of Orientations

To put these two layout functions to use, create an event handler for the form's Resize event. In this event handler, the new orientation invokes the corresponding layout function.

private void MainForm_Resize(object sender, System.EventArgs e)
{
      if(Screen.PrimaryScreen.Bounds.Width>Screen.PrimaryScreen.Bounds.Height)
      {
           Landscape();
      }
      else
      {
            Portrait();
      }
}

Testing It Out

To test a program's ability to handle changes in orientation, assign a hardware button to the Rotate Screen function. You can do this in the Control Panel under the Buttons item. Choose the button you want to reassign, and then choose Rotate Screen in the Button Assignment drop down menu. You can then run your program in one orientation, and then use the button to switch orientations on the fly.

You can also change the Screen properties in the Control Panel; it's under the System tab.

Adding High-Resolution Awareness

Introduction

Windows Mobile 2003 Second Edition software will automatically resize the controls on .NET Compact Framework forms to take advantage of high-dpi displays. In almost all cases, this is sufficient. However, it is sometime necessary to customize how an application responds to high-dpi. For example, applications that involve custom graphics or require a large number of controls to appear on a single screen would benefit from supporting high-dpi.

Note In creating high-dpi layouts, the ability to make controls smaller must be used with caution. Smaller controls can be harder to read and difficult for a user to interact with using a stylus.

You use a method similar to adding orientation awareness to create separate layouts for high-dpi displays. You create new functions for all four combinations of high-dpi/low-dpi and landscape/portrait. Additionally, expand the Form.Resize event handler to invoke each one as appropriate.

Note The Visual Studio .NET 2003 form designer does not generate layout code for high-dpi displays and developers must create this code by hand.

After doing this, the executable must be marked as dpi-aware to prevent the operating system from resizing things automatically. This is done by adding a Win32 resource file to the executable after it has been compiled using the res2exe tool.

Using Res2exe

  • Put res2exe.exe and hidpi.res in Visual Studio .NET 2003's command line path. These files are installed with the Developer Resources for Windows Mobile 2003 Second Edition, by default into the directory C:\Program Files\Developer Resources for Windows Mobile 2003 Second Edition\tools.
  • Compile your application using Visual Studio .NET 2003
  • From the command line, run res2exe on the output executable using this syntax:
    • res2exe -c -r hidpi.res [EXE file]

Note If you are using signcode to sign your exe file — for example if you are targeting Smartphone 2003 — you need to use res2exe BEFORE you sign your application. If you use res2exe after you sign your application the signature will be rendered invalid.

Testing It Out

  • Build the output file using Visual Studio.NET 2003 (Build, and then Build Solution)
  • Use res2exe on the version of the executable in the project's \obj\Debug subdirectory.

Choose one of the "VGA" emulators in Visual Studio.NET 2003 (as shown in Figure 2), and then start the debugger.

Click here for larger image

Figure 2. Choosing a VGA emulator in Visual Studio .NET. Click the thumbnail for a larger image.

Conclusion

Supporting orientation awareness in the .NET Compact Framework is a simple matter of creating Portrait and Landscape layout code. Although most .NET Compact Framework applications do not need to implement high-dpi support, you can also add resolution awareness using a similar method.