Detecting Screen Orientation and Screen Rotation in Tablet PC Applications

 

Eliot Graff
Microsoft Corporation

April 2003

Applies to:
    Microsoft® Tablet PC Platform SDK

Summary: Learn how to detect whether a user is viewing a Tablet PC in portrait or landscape orientation and adjust an application accordingly. This article describes ways to detect when a user switches between portrait and landscape orientation on a Tablet PC. These C#, Visual Basic .NET, C++ and Visual Basic version 6.0 descriptions and examples use the Microsoft Tablet PC Platform SDK version 1.5 API. Readers should be familiar with the Microsoft Tablet PC Platform SDK and managed code. (10 printed pages)

Contents

Introduction
How to Detect Current Screen Orientation
How to Detect Screen Rotation

Introduction

One of the compelling features for users of a Tablet PC is the ability to work in either portrait or landscape orientation. Currently, Microsoft® has no definitive information about how often users change screen orientation. Usability research does show that the orientation users choose depends on the tasks they are performing or have just performed.

To optimize your application for use on a Tablet PC, your application should be designed for both portrait and landscape orientation. You are able to detect which screen orientation is used when your application opens, and you can detect when a user switches screen orientation. This enables you to display your application accordingly.

To detect current screen orientation in either managed or Automation code, you call the method that describes the current screen state. To detect screen rotation in either managed or Automation code, you listen for the event that indicates the display has changed and then call the method that describes the current screen state. In all cases, by comparing the height and width of the screen, you determine whether the user is in portrait or landscape orientation. Examples are given in C#, Microsoft Visual Basic® .NET, C++ and Visual Basic 6.0.

This article discusses how to detect:

  • The current screen orientation on a Tablet PC
  • When a user switches between portrait and landscape orientation on a Tablet PC

This article does not discuss:

  • Optimizing an application for either portrait or landscape viewing
  • Determining if a screen orientation is primary or secondary
  • Visual design guidelines
  • Multiple monitors

For more information about optimizing an application for either screen orientation or determining if a screen orientation is primary or secondary, see Designing for Portrait and Landscape Screen Displays in the Microsoft Tablet PC Platform SDK version 1.5.

For more information about visual design guidelines, see Visual Design Guidelines in the Microsoft Tablet PC Platform SDK version 1.5.

For more information about multiple monitors, see Multiple Display Monitors in the MSDN® Library.

How to Detect Current Screen Orientation

To detect the current orientation, call the appropriate properties for the screen height and width. Compare the height and width to determine whether the user is viewing the application in portrait or landscape orientation.

Detecting Screen Orientation by Using Managed Code

In managed code, you look for the Bounds property of the Primary Screen member of the Screen object, which returns a type Rectangle. You then compare the height and width of that rectangle.

C#

The following C# example determines whether the user is viewing the application in portrait or landscape orientation.

using System.Windows.Forms;
//. . .
//Set event handler
private void Form1_Load(object sender, System.EventArgs e)
{
    int theScreenRectHeight = Screen.PrimaryScreen.Bounds.Height;
    int theScreenRectWidth = Screen.PrimaryScreen.Bounds.Width;
    //Compare height and width of screen and act accordingly.
    if (theScreenRectHeight > theScreenRectWidth)
    {
        // Run the application in portrait, as in:
    MessageBox.Text = "Run in portrait.";
    }
    else
    {
        // Run the application in landscape, as in:
        MessageBox.Text = "Run in landscape.";
    }
}

Visual Basic .NET

The following Visual Basic .NET example determines whether the user is viewing the application in portrait or landscape orientation.

Public Class Form1
    Inherits System.Windows.Forms.Form
'. . . 

'Set event handler
Private Sub Form1_Load(ByVal sender As System.Object,
   ByVal e As System.EventArgs) Handles MyBase.Load

    'Compare height and width of screen and act accordingly.
    Dim theScreenHeight As Integer
    Dim theScreenWidth As Integer
    theScreenHeight = Screen.PrimaryScreen.Bounds.Height
    theScreenWidth = Screen.PrimaryScreen.Bounds.Width
    If (theScreenHeight > theScreenWidth) Then
        'Run the application in portrait, as in:
        MsgBox("Run in portrait", MsgBoxStyle.OKOnly, "Portrait")
    Else
        'Run the application in landscape, as in:
        MsgBox("Run in landscape", MsgBoxStyle.OKOnly, "Landscape")
    End If

Detecting Screen Orientation by Using C++ and Visual Basic 6.0

In Automation code, use the appropriate function to retrieve the height and width of the screen. Compare the height and width to determine whether the user is viewing the application in portrait or landscape orientation.

C++

The following C++ example determines whether the user is viewing the application in portrait or landscape orientation by using the GetSystemMetrics function to retrieve the height and width of the full screen.

#include <windows.h>
//. . .

//Compare the height and width of the screen and act accordingly.
int theScreenWidth = GetSystemMetrics(SM_CXFULLSCREEN);
int theScreenHeight = GetSystemMetrics(SM_CYFULLSCREEN);
if (theScreenWidth > theScreenHeight) 
    //Run the application in landscape, as in:
    MessageBox(NULL,"Run in landscape.","Landscape",MB_OK);
else
    //Run the application in portrait, as in:
    MessageBox(NULL,"Run in portrait.","Portrait",MB_OK);

Visual Basic 6.0

The following Visual Basic 6.0 example determines whether the user is viewing the application in portrait or landscape orientation by returning the Width and Height properties of the Screen object.

Private Sub Form_Load()
    Dim theScreenWidth As Integer
    Dim theScreenHeight As Integer
    Dim iReturn As Integer
    theScreenWidth = Screen.Width
    theScreenHeight = Screen.Height
    If (theScreenWidth > theScreenHeight) Then
        'Run the application in landscape, as in:
        iReturn = MsgBox("Run in landscape.", vbOKOnly, "Landscape")
    Else
        'Run the application in portrait, as in:
        iReturn = MsgBox("Run in portrait.", vbOKOnly, "Portrait")
    End If
End Sub

How to Detect Screen Rotation

Both the Microsoft .NET Framework API and the Win32 API enable you to detect changes in screen orientation. In managed code, you assign an event handler to the DisplaySettingsChanged event of the SystemEvents class.

For applications written in C++ and Visual Basic 6.0, you detect changes in screen orientation by assigning an event handler to the _win32_WM_DISPLAYCHANGE message. Whenever the display settings change, you then compare the height and width of the new screen. If the height is larger than the width, the application is in portrait orientation; if the width is larger than the height, the application is in landscape orientation.

Detecting Screen Rotation by Using Managed Code

A specific way of detecting screen rotation when using managed code is to set a handler for the DisplaySettingsChanged event of the SystemEvents class. You then attach an event handler to compare the Height and Width properties of the Screen object as shown in the previous managed code examples.

C#

The following C# example illustrates how to detect when the user changes any display settings. You then test to see if the user is viewing the application in portrait or landscape orientation and adjust your application accordingly.

using System.Windows.Forms;
//. . .
//Set event handler
private void Form1_Load(object sender, System.EventArgs e)
{
    Microsoft.Win32.SystemEvents.DisplaySettingsChanged +=
        new System.EventHandler( displaySettingsChanged );
}
// Event handler for the system's DisplaySettingsChanged event.
// Detect and then compare the height and width of the screen.
private void displaySettingsChanged(object sender, EventArgs e)
{
    Rectangle theScreenRect = Screen.GetBounds(this);

    if( theScreenRect.Height > theScreenRect.Width )
    {
        //Run the application in portrait, as in:
        MessageBox.Show("Run in portrait.");
    }
    else
    {
        //Run the application in landscape, as in:
        MessageBox.Show("Run in landscape.");
    }
}

Visual Basic .NET

The following Visual Basic .NET example illustrates how to detect when the user changes any display settings. You then test to see if the user is viewing the application in portrait or landscape orientation and adjust your application accordingly.

Public Class Form1
    Inherits System.Windows.Forms.Form
'. . .

'Create event handler to handle firing of DisplaySettingsChanged
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As 

  System.EventArgs) Handles MyBase.Load
    AddHandler Microsoft.Win32.SystemEvents.DisplaySettingsChanged,
      AddressOf DetectScreenRotation
    End Sub

'Compare height and width of screen and take appropriate action
    Public Sub DetectScreenRotation(ByVal sender As System.Object,
      ByVal e As System.EventArgs)
        Dim theScreenBounds As Rectangle
        theScreenBounds = Screen.GetBounds(Screen.PrimaryScreen.Bounds)

    If (theScreenBounds.Height > theScreenBounds.Width) Then
        'Run the application in portrait, as in:
        MsgBox("Run in portrait", MsgBoxStyle.OKOnly, "Portrait")
    Else
        'Run the application in landscape, as in:
        MsgBox("Run in landscape", MsgBoxStyle.OKOnly, "Landscape")
    End If
    End Sub

Detecting Screen Rotation by Using C++ and Visual Basic 6.0

C++

To detect screen rotation in applications written in C++, you listen for the WM_DISPLAYCHANGE message by setting a case for the WM_DISPLAYCHANGE message in the WindowProc function's switch statement. You then use the lParam parameter of the WindowProc function to retrieve the height and width of the full screen. The following C++ example illustrates this process.

#include <windows.h>
//. . . 
//
//    FUNCTION: WndProc(HWND, unsigned, WORD, LONG)
//
//    PURPOSE:  Processes messages for the main window.
//
//    WM_COMMAND        - process the application menu
//    WM_PAINT          - Paint the main window
//    WM_DESTROY        - post a quit message and return
//    WM_DISPLAYCHANGE  - determine screen orientatin and act accordingly
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM 
  lParam)
{
    int wmId, wmEvent;
    PAINTSTRUCT ps;
    HDC hdc;
    switch (message) 
    {
//. . .
//. . .
    case WM_DISPLAYCHANGE:
        {
        //Separate the screen width and height and then
        //check to see if screen width is greater than screen height
        if ((int)lParam % 0x10000 > (int)lParam / 0x10000) 
            //Run the application in landscape, for example:
            MessageBox(NULL,"Run in landscape.","Landscape",MB_OK);
        else
            //Run the application in portrait, as in:
            MessageBox(NULL,"Run in portrait.","Portrait",MB_OK);
        }
        break;

Visual Basic 6.0

To detect screen rotation in applications written in Visual Basic 6.0, you hook the WindowProc function to listen for a WM_DISPLAYCHANGE message. You then use the lParam parameter of the WindowProc function to retrieve the height and width of the full screen, as in the previous Visual Basic 6.0 example. Finally, you unhook, restoring the default WindowProc function. The following Visual Basic 6.0 example illustrates this process.

The form's code:

Private Sub Form_Load()
    gHW = Me.hwnd
    Hook
End Sub

Private Sub Form_Terminate()
    Unhook
End Sub

The module's code:

Declare Function CallWindowProc Lib "user32" Alias _
"CallWindowProcA" (ByVal lpPrevWndFunc As Long, _
ByVal hwnd As Long, ByVal Msg As Long, _
ByVal wParam As Long, ByVal lParam As Long) As Long

Declare Function SetWindowLong Lib "user32" Alias _
"SetWindowLongA" (ByVal hwnd As Long, _
ByVal nIndex As Long, ByVal dwNewLong As Long) As Long

Public Const GWL_WNDPROC = -4
Public Const WM_DISPLAYCHANGE = 126
Global lpPrevWndProc As Long
Global gHW As Long

Public Sub Hook()
    lpPrevWndProc = SetWindowLong(gHW, GWL_WNDPROC, _
    AddressOf WindowProc)
End Sub

Public Sub Unhook()
    Dim temp As Long
    temp = SetWindowLong(gHW, GWL_WNDPROC, _
    lpPrevWndProc)
End Sub

Function WindowProc(ByVal hw As Long, ByVal uMsg As _
Long, ByVal wParam As Long, ByVal lParam As Long) As _
Long
    'Activated when display changes
    If uMsg = WM_DISPLAYCHANGE Then 
        'Separate the width and height and then
        'check to see if screen width is greater than screen height
        If lParam Mod &H10000 > lParam \ &H10000 Then
            'Run the application in landscape, for example:
            MsgBox "Run in landscape."
        Else
            'Run the application in portrait, for example:
            MsgBox "Run in portrait."
        End If
    End If
    'Pass windows messages on to the default WindowProc
    WindowProc = CallWindowProc(lpPrevWndProc, hw, _
    uMsg, wParam, lParam)
End Function