Window Layout and Mirroring (Windows Embedded CE 6.0)

1/6/2010

The window layout defines how text and Microsoft Windows Graphics Device Interface (GDI) objects are laid out in a window or device context (DC). Most languages, such as English, French, and German, require a left-to-right (LTR) layout. Other languages, such as Arabic and Hebrew, require right-to-left (RTL) layout.

The window layout applies to text but also affects the other GDI elements of the window. These other GDI elements include bitmaps, icons, the location of the origin, buttons, cascading tree controls, and whether the horizontal coordinate increases as you go left or right. For example, after an application has set RTL layout, the origin is positioned at the right edge of the window or device, and the number representing the horizontal coordinate increases as you move left.

However, not all GDI elements are affected by the layout of a window. For example, the layout for dialog boxes, message boxes, and device contexts that are not associated with a window, such as metafile and printer DCs, must be handled separately. Specifics for these are mentioned later in this topic.

Note

The Windows Embedded CE Standard Shell, formerly known as the HPC Shell, has been mirrored. You can use this shell as an example for a mirroring implementation. To view the mirrored shell at run time, you need to build a run-time image with Arabic or Hebrew as your default language.

By default, the window layout is LTR. To set the RTL window layout, call CreateWindowEx with the style WS_EX_LAYOUTRTL. Also by default, a child window (one created with the WS_CHILD style and with a valid parent hWnd parameter in the call to CreateWindow or CreateWindowEx) has the same layout as its parent.

To disable inheritance of mirroring for an individual window, as in the case of a child window that should not inherit the layout of its parent, process the WM_CREATE message with GetWindowLong and SetWindowLong to turn off the WS_EX_LAYOUTRTL flag. This processing is in addition to whatever other processing is needed. The following code fragment shows how this is done.

SetWindowLong (hWnd, 
               GWL_EXSTYLE, 
               GetWindowLong(hWnd,GWL_EXSTYLE) & ~WS_EX_LAYOUTRTL))

To mirror any DC, call SetLayout (hdc, LAYOUT_RTL). To query the layout settings of the device context, call GetLayout. Upon a successful return, GetLayout returns a DWORD that indicates the layout settings by the settings of the LAYOUT_RTL bit.

After a window has been created, change the layout using the SetWindowLong function. For example, this is necessary when the user changes the user interface language of an existing window from Arabic or Hebrew to German. However, when changing the layout of an existing window, you must invalidate and update the window to ensure that the contents of the window are all drawn on the same layout. The following code sample changes the window layout as needed:

lExStyles = GetWindowLong(hWnd, GWL_EXSTYLE);

// Check whether new layout is opposite the current layout
if (!(pLState -> IsRTLLayout) != !(lExStyles & WS_EX_LAYOUTRTL))
{
    // the following lines will update the window layout

    lExStyles ^= WS_EX_LAYOUTRTL;        // toggle layout
    SetWindowLong(hWnd, GWL_EXSTYLE, lExStyles);
    InvalidateRect(hWnd, NULL, TRUE);    // to update layout in the client area
}

In mirroring, it is helpful to think in terms of "near" and "far" instead of "left" and "right". Failure to do so can cause problems. One common coding practice that causes problems in a mirrored window occurs when mapping between screen coordinates and client coordinates. For example, applications often use code similar to the following to position a control in a window:

// DO NOT USE THIS IF APPLICATION MIRRORS THE WINDOW

// get coordinates of the window in screen coordinates
GetWindowRect(hControl, (LPRECT) &rControlRect);  

// map screen coordinates to client coordinates in dialog
ScreenToClient(hDialog, (LPPOINT) &rControlRect.left); 
ScreenToClient(hDialog, (LPPOINT) &rControlRect.right);

This causes problems in mirroring because the left edge of the rectangle becomes the right edge in a mirrored window, and vice versa. To avoid this problem, replace the ScreenToClient calls with a call to MapWindowPoints as follows:

// USE THIS FOR MIRRORING

GetWindowRect(hControl, (LPRECT) &rControlRect);
MapWindowPoints(NULL, hDialog, (LPPOINT) &rControlRect, 2)

This code works because, on platforms that support mirroring, MapWindowPoints is modified to swap the left and right point coordinates when the client window is mirrored. For more information, see the Remarks section of MapWindowPoints.

Another common practice that can cause problems in mirrored windows is positioning objects in a client window using offsets in screen coordinates instead of client coordinates. For example, the following code uses the difference in screen coordinates as the x position in client coordinates to position a control in a dialog box.

// OK if LTR layout but WRONG for a mirrored dialog 

RECT rdDialog;
RECT rcControl;

HWND hControl = GetDlgItem(hDlg, IDD_CONTROL);
GetWindowRect(hDlg, &rcDialog);             // gets rect in screen coordinates
GetWindowRect(hControl, &rcControl);
MoveWindow(hControl,
           rcControl.left - rcDialog.left,  // uses x position in client coords
           rcControl.top - rcDialog.top,
           nWidth,
           nHeight,
           FALSE);

This code is applicable when the dialog window has LTR layout and the mapping mode of the client is MM_TEXT, because the new x position in the client coordinates correspond to the difference in left edges of the control and the dialog box in screen coordinates. However, in a mirrored dialog box, left and right are reversed, so instead you should use MapWindowPoints as follows:

RECT rcDialog;
RECT rcControl;

HWND hControl - GetDlgItem(hDlg, IDD_CONTROL);
GetWindowRect(hControl, &rcControl);

// MapWindowPoints works correctly in both mirrored and non-mirrored windows.
MapWindowPoints(NULL, hDlg, (LPPOINT) &rcControl, 2);

// Now rcControl is in client coordinates.
MoveWindow(hControl, rcControl.left, rcControl.top, nWidth, nHeight, FALSE)

Mirroring Dialog Boxes and Message Boxes

Dialog boxes and message boxes do not inherit layout, so you must set the layout explicitly. To mirror a message box, call MessageBox with the MB_RTLREADING option. To lay out a dialog box right-to-left, use the extended style WS_EX_LAYOUTRTL in the dialog template structure DLGTEMPLATEEX. Property sheets are a special case of dialog boxes. Each tab is treated as a separate dialog box, so you need to include the WS_EX_LAYOUTRTL style in every tab that you want mirrored.

Mirroring Device Contexts Not Associated with a Window

DCs that are not associated with a window, such as metafile or printer DCs, do not inherit layout, so you must set the layout explicitly. To change the device context layout, use the SetLayout function.

The SetLayout function is rarely used with windows. Typically, windows receive an associated DC only in processing a WM_PAINT message. Occasionally, a program creates a DC for a window by calling GetDC. Either way, the initial layout for the DC is set by BeginPaint or GetDC according to the window's WS_EX_LAYOUTRTL flag.

See Also

Other Resources

International Application Development
Uniscribe Application Development
Window and Control Styles
Working with Windows and Messages
Getting a Handle to a Device Context
Graphics Device Interface (GDI)