Skip to main content

Globalization Step-by-Step

Mirroring Awareness

String Handling UI Considerations

Overview and Description

For Right-To-Left (RTL) languages, not only does the text alignment and text reading order go from right to left, but also the UI elements layout should follow this natural direction of going from right to left. Of course, this layout change would only apply to localized RTL languages (Before Windows 2000 this was only Arabic and Hebrew). During the Windows 95 project, a flipping method was introduced in "Espresso" which allowed you to replace control elements of a dialog box. This partial solution did not address the layout problem for title bars, tree-views combo-boxes, etc. It only gave them a new position within the flipped dialog.

To resolve the remaining issues with flipping, the mirroring technology was introduced in Arabic and Hebrew Win98 and was used in Win2000. It gives a perfect right-to-left (RTL) look and feel to the UI. For Win98, this technology was only available on localized Arabic and Hebrew OS', however, on Windows 2000, all language versions and flavors of the OS are already mirroring aware and a mirrored application can be created and executed on all Win2000 platforms. Figure 1 below displays an Arabic Win98 mirrored desktop where every UI element from the "Start" button to tree-view controls are mirrored.

Figure 1: Arabic Windows XP desktop

Figure 1: Arabic Windows XP desktop


Mirroring is in fact nothing else than a coordinate transformation:

  • Origin (0,0) is in the upper RIGHT corner of a window

  • X scale factor = -1 (i.e. x values increase from right to left

Figure 2: Coordinate transformation

Figure 2: Coordinate transformation


To avoid confusion around coordinates, try to replace the concepts of left and right with the concepts of near and far.

To minimize the amount of re-write needed for applications to support mirroring, system components such as "GDI" and "User" have been modified to turn mirroring on and off with almost no additional code changes except for a few considerations regarding owner-drawn controls and bitmaps (see Examples).

There are two different approaches to turn mirroring on and off:

  • Compiled application approach: This is the approach you use when you have full access to the applications source code.
  • Non-compiled application approach: This is the approach you use when you have legacy applications for which you have on access to any of the code. This is usually done with some resource localization tool.

Details about these two approaches can be found in the Code Samples section.

Top of pageTop of page

Examples

As mentioned in the overview section, except few considerations related to owner-drawn controls there are not too many code changes required to be fully mirroring aware.

Most of the mirroring related bugs are in fact related to owner drawn controls. Here are a few examples:

  1. Reversed bitmap:

    This happens when the bitmap is drawn in a mirrored DC. For compiled applications, you can turn the mirroring of the DC off by calling the SetLayout API. For non-compiled applications, the workaround would be to flip the Windows logo in the localized resources. A bitmap flipped twice horizontally would be displayed okay! For more info, see Solution and Code Samples section.

  2. Off the screen bitmap:

    The owner-drawn command or bitmap is being drawn outside the desired area because its coordinates are expecting the origin to be in the top-left of the window instead of the top-right. For compiled applications, use MapWindowPoints to get the coordinates of the destination rectangle. For non-compiled applications, try to turn the inheritance flag of the dialog box off - this solution would only work if the rectangle in not a child of the parent dialog!

Top of pageTop of page

Mirroring in Win32

To minimize the amount of rewriting needed for applications to support mirroring, system components such as the GDI and User modules have been adapted to enable control over the mirroring state of the UI objects. Almost no additional code changes are necessary, with the exception of a few things you will need to consider when working with owner-drawn controls and bitmaps. As you will see, new APIs and extended styles for windows let you turn mirroring on and off at the process level, the window level, the dialog-resources level, or even the device-context level. However, before examining the capabilities of system components and APIs that are available when you enable mirroring in code, this chapter will take a quick look at how and when to enable mirroring in resources.

Enabling Mirroring in Resources

With changes that the Windows team has made to the system's resource loader, you can mirror your applications by simply editing your resources. This is a good approach to use when you want to mirror old components for which the source is not available or is no longer used; it is also beneficial when you want to conduct primary testing of your applications to see if they support mirroring.

You can mirror applications by adding two left-to-right marks (LRMs)-represented by Unicode code point U+200E-in front of the FileDescription value of the version stamping. Figure below shows an English Notepad.exe file that has been mirrored with this approach.

Figure 5: A Notepad.exe file that has been mirrored by adding two LRMs

Figure 5: A Notepad.exe file that has been mirrored by adding two LRMs


Although easy, enabling mirroring in resources offers little flexibility when it comes to just mirroring a specific window or control. In fact, all windows and controls within those windows that belong to the affected process will be automatically mirrored. For that reason, enabling mirroring in code is more flexible than mirroring in resources, since it allows you the freedom to decide what you want mirrored. Keep in mind that a child process created by the mirrored process does not inherit the mirroring style.

Enabling Mirroring in Code

Although you can activate mirroring through the localization process (with non- compile mirroring), doing so will often reveal some issues that require a code fix. Also, as stated, you might want full control over when to enable mirroring-such as per process, per window, for dialog resources, or per device context, as shown in the following sections-and over which elements you want mirrored. This is only possible through coding and through new mirroring APIs.

Activating Mirroring per Process

By activating the mirroring layout on a per-process basis, all windows that are created after activation will be mirrored; but this activation will not affect the existing windows. This approach is very similar to noncompile mirroring, with the exception that the mirroring style within an application can be turned on and off at run time rather than at a binary level, as in the case of noncompile mirroring.

You set the default direction to RTL for a process by calling SetProcessDefaultLayout (LAYOUT_RTL). You can also turn off default mirroring by calling SetProcessDefaultLayout(0). In addition, you can query the current default layout direction as follows:

BOOL WINAPI

GetProcessDefaultLayout
(DWORD *pdwDefaultLayout);  

After returning successfully, pdwDefaultLayout contains either LAYOUT_RTL or 0.

Although you can activate mirroring at the process level, there are times you might wish to activate it at a lower level-such as per window. The following section explains when this method is a good option and shows you what extended styles to use.

Activating Mirroring per Window

Once the mirroring style is set at a process level, all newly created windows are automatically mirrored. An alternative approach is to activate mirroring on a per-window basis (if not all windows in your application are meant to be mirrored). With this approach, a window can be mirrored upon its creation by calling CreateWindowEx, and set by using the extended style WS_EX_LAY- OUTRTL.

By default, all child windows of a parent window inherit the mirroring attribute. However, once again, not all windows are necessarily meant to be mirrored; thus you might want to prevent child windows from being mirrored, for example. To remove the mirroring inheritance for child windows, you need to specify the new extended style WS_EX_NOINHERITLAYOUT in your call to CreateWindowEx.

A good example of a case where you should prevent the mirroring of child windows would be when you have windows that host or import external controls, such as Microsoft ActiveX, Java applets, Component Object Model (COM) objects, and so on. The problem with controls such as these is that you have little ability to manipulate their behavior, and you cannot readily impose mirroring requirements on them.

The following code shows how to toggle the layout of a window between RTL and LTR:

LONG lExStyles;

// Retrieve the extended style of the window.
lExStyles = GetWindowLong(hWnd, GWL_EXSTYLE);
// Toggle layout.
lExStyles ^= WS_EX_LAYOUTRTL;
// Set extended styles to new value.
SetWindowLong(hWnd, GWL_EXSTYLE, lExStyles);
// Update client area.
InvalidateRect(hWnd, NULL, TRUE);
 

Important: Changing the mirroring style on the fly is not always the most efficient way of implementing mirroring of windows. Many controls might not behave as expected and could be improperly mirrored. Therefore, enabling mirroring of windows as they are initially created remains the better approach.

Activating Mirroring for Dialog Resources

It's important to distinguish between a window and a dialog box (which does not receive WM_CREATE, but rather WM_INITDIALOG). To activate mirroring for dialog boxes, you need to set the mirroring flags in your resource editor and add the extended styles WS_EX_LAYOUTRTL and WS_EX_NOINHERITLAYOUT. (The second style is only if you want to prevent the inheritance from taking place.)

The only way to switch between mirrored and nonmirrored dialog resources at run time is to have two sets of dialog resources: one mirrored and one nonmirrored. The Dialog Properties property sheet within the resource editor of Microsoft Visual Studio 6 allows you to set the mirroring style of the dialog resources.

Figure 6: Setting the mirroring style of the dialog resources within the resource editor of Visual Studio 6

Figure 6: Setting the mirroring style of the dialog resources within the resource editor of Visual Studio 6


Activating Mirroring per Device Context

The mirroring technology described up to now applies to all objects in a standard window. These objects, as well as bitmaps and icons in a mirrored window, are all mirrored by default. Obviously, there are some graphics objects, particularly those that include text, that shouldn't be mirrored. Therefore, Microsoft provides several GDI functions in platforms that support mirroring (which, again, includes all versions of Windows 2000 and Windows XP). These functions are shown in the following code:

BOOL WINAPI 
SetLayout(HDC hDc, DWORD dwLayout);
DWORD WINAPI
GetLayout(HDC hDc);

The device context of a mirrored window is mirrored by default. Nevertheless, you can disable mirroring in a device context by calling SetLayout (hdc, 0) , and activate it by calling SetLayout(hdc, LAYOUT_RTL). You might need control over the mirroring properties of a device context in cases when some drawing operation performed by a legacy code is broken by mirroring.

Drawing images that may be sensitive to directionality of the output presents a separate problem. To prevent mirroring of bitmaps, call SetLayout with the LAYOUT_BITMAPORIENTATIONPRESERVED bit set in dwLayout :

SetLayout(hdc, LAYOUT_RTL | LAYOUT_BITMAPORIENTATIONPRESERVED);  

Upon successful completion of GetLayout, GetLayout returns the following:

  • 0 if the device context is not mirrored

  • LAYOUT_RTL if the device context is mirrored

  • LAYOUT_RTL and LAYOUT_BITMAP if the device context is mirrored and if the programmer does not want the device context to have mirrored bitmaps

Yet another way to prevent a bitmap from being mirrored is to define NOMIR-RORBITMAP in calls to BitBlt and StretchBlt.

Mirroring and Property Sheets

A tricky situation when enabling mirroring is when you are dealing with property sheets (and wizards). Imagine the case where a property-sheet container has several pages, and only a few of them are localized into an RTL language. Individual pages are made out of dialog templates that can be mirrored separately in the resources. But what about the container? Should the container be mirrored? To avoid confusion and inconsistent behavior with property sheets, the following approaches are among many steps that the system takes in order to decide whether to create a mirrored container:

  • If the first page does not contain the mirroring window style and the client process is not mirrored, the client will not be considered a mirrored client. This can happen when the application loads the first page from a different binary.

  • If the primary resource language of the first page is not Arabic or Hebrew, the client will not be considered a mirrored client. This can happen when the application loads the first page from a different binary (a binary which might not be localized for the same language).

Mapping Coordinates

Perhaps one of the biggest issues when it comes to dealing with mirroring is mapping between screen coordinates and client coordinates, or failing to do so. For example, developers often use code such as the following to position a control in a window:

GetWindowRect(hControl, (LPRECT) &rControlRect);
// Gets coordinates of window in screen coordinates.
ScreenToClient(hDialog, (LPPOINT) &rControlRect.left);
// Maps screen coordinates to client coordinates in dialog box.
ScreenToClient(hDialog, (LPPOINT) &rControlRect.right);

In this code, the left edge of a rectangle becomes the right edge in a mirrored window, and vice versa, causing unintended results. The solution is to replace the ScreenToClient calls as shown in the following code:

GetWindowRect(hControl, (LPRECT) &rControlRect);
// Gets coordinates of window in screen coordinates.
MapWindowPoints (NULL, hDialog, (LPPOINT) &rControlRect, 2);
 

This code works because the MapWindowPoints API has been modified on platforms that support mirroring. As a result of this modification, Map-WindowPoints is able to swap the left- and right-point coordinates of the client window that is being mirrored, whenever two points at a time are passed to this API.

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

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

This works fine when the dialog window has an LTR layout, and when the mapping mode of the client is MM_TEXT. The new x position in client coordinates corresponds to the difference in the left edges of the control and the dialog box in screen coordinates. In a mirrored dialog box, however, the roles of left and right are reversed. You can remove the assumption from the previous code that near is left and far is right by using MapWindowPoints to go into client coordinates, as shown in the following code sample:

RECT rcDialog;
RECT rcControl;
HWND hControl = GetDlgItem(hDlg, IDD_CONTROL);
GetWindowRect(hControl, &rcControl);
// MapWindowPoints will work correctly in mirrored
// windows and in non-mirrored windows.
MapWindowPoints(NULL, hDlg, (LPPOINT)&rcControl, 2);
//Now rcControl is in client coordinates.
MoveWindow(hControl, rcControl.left, rcControl.top, nWidth, nHeight, FALSE);
 

There is a common theme in these examples. Instead of thinking in terms of the concrete notions of left and right, you should substitute the more abstract concepts of near and far. The following are some specific guidelines on developing applications that can be mirrored by using the automatic mirroring interfaces:

  • Use client coordinates rather than screen coordinates wherever possible.

  • If necessary, map from screen coordinates to client coordinates.

  • To map points, use MapWindowPoints instead of ClientToScreen and ScreenToClient.

Handling Direction-Sensitive Graphics

Direction-sensitive graphics (such as bitmaps and icons) present another challenge with regard to mirroring. Some direction-sensitive graphics can have a different meaning when mirrored. For example, within an LTR layout in a browser, an arrow that points to the left represents the concept of going back to the previous page; an arrow that points to the right would signify going forward to the next page. When these arrows are mirrored for an RTL layout, the meaning will be just the opposite.

 

Figure 7: Example of a direction-sensitive arrow that changes meaning when mirrored

Figure 7: Example of a direction-sensitive arrow that changes meaning when mirrored


Other graphics should not be mirrored at all, such as legal trademarks or logos because they will make no sense when laid out from right to left.

Figure 8: Example of a direction-sensitive graphic that should not be mirrored

Figure 8: Example of a direction-sensitive graphic that should not be mirrored


There are several solutions available for dealing with cases such as these. Deciding which of the following four approaches to use depends on the particular situation at hand.

One solution is to temporarily turn off RTL layout of the device context when rendering direction-sensitive bitmaps or icons; you can then turn it back on once mirroring is completed. The problem with this approach is that sometimes you don't have access to the device context where the graphics will be rendered. (An example of this is when passing your icon or bitmap handle to a third-party DLL that will display the image for you.)

Another solution is to have two different sets of graphics in your resources-one set to be used when the drawing destination is LTR-oriented, and the other set to be used when the drawing destination is RTL-oriented. The first set will contain the original, LTR graphics. The second set will contain a copy of the first set of graphics with modifications made only to those graphics needing to reflect the RTL orientation. You can then choose which set to load and render based on whether the set will be rendered in a mirrored or nonmirrored situation. In other words, for nonmirrored device contexts you would load the original set of nonmirrored graphics, and for mirrored device contexts you would load the set that contained the assortment of mirrored and nonmirrored graphics. (In the latter case, when mirrored graphics are rendered on a mirrored device context, they will be mirrored again and thus revert back to the way you want them to be displayed.) Depending on the number of direction-sensitive graphics that you have in your project, this approach might not be your best choice if you have a lot of graphics that should not be mirrored, since duplicating those will increase the size of your resources.

As a third solution, if you ship a separate set of binaries based on the target localized languages, you can let your localization team mirror those graphics elements using a resource editor. (Most resource editors let you flip images in both x and y directions nowadays.) A final solution-especially when you don't have direct access to the device context where the graphics will be rendered-is to create a mirrored copy of the resource. You then pass this mirrored copy instead of the original, nonmirrored one. (Again, when the mirrored graphics are rendered on a mirrored device context, they will revert back to their original, intended look.) The following sample code illustrates how to mirror both a bitmap and an icon when each's handle is passed:

HBITMAP CreateMirroredBitmap( HBITMAP hbmOrig)
{
HDC hdc, hdcMem1, hdcMem2;
HBITMAP hbm = NULL, hOld_bm1, hOld_bm2;
BITMAP bm;
if (!hbmOrig)
return NULL;
if (!GetObject(hbmOrig, sizeof(BITMAP), &bm))
return NULL;

// Grab the screen DC.
hdc = GetDC(NULL);
if (hdc)
{
hdcMem1 = CreateCompatibleDC(hdc);
if (!hdcMem1)
{
ReleaseDC(NULL, hdc);
return NULL;
}
hdcMem2 = CreateCompatibleDC(hdc);
if (!hdcMem2)
{
DeleteDC(hdcMem1);
ReleaseDC(NULL, hdc);
return NULL;
}
hbm = CreateCompatibleBitmap(hdc, bm.bmWidth, bm.bmHeight);
if (!hbm)
{
ReleaseDC(NULL, hdc);
DeleteDC(hdcMem1);
DeleteDC(hdcMem2);
return NULL;
}
// Flip the bitmap.
hOld_bm1 = (HBITMAP)SelectObject(hdcMem1, hbmOrig);
hOld_bm2 = (HBITMAP)SelectObject(hdcMem2 , hbm );
SetLayout(hdcMem2, LAYOUT_RTL);
BitBlt(hdcMem2, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem1, 0, 0, SRCCOPY);
SelectObject(hdcMem1, hOld_bm1 );
SelectObject(hdcMem1, hOld_bm2 );
DeleteDC(hdcMem1);
DeleteDC(hdcMem2);
ReleaseDC(NULL, hdc);
}
return hbm;
}

HICON CreateMirroredIcon(HICON hiconOrg)
{
HDC hdcScreen, hdcBitmap, hdcMask = NULL;
HBITMAP hbm, hbmMask, hbmOld,hbmOldMask;
BITMAP bm;
ICONINFO ii;
HICON hicon = NULL;
hdcBitmap = CreateCompatibleDC(NULL);
if (hdcBitmap)
{
hdcMask = CreateCompatibleDC(NULL);
if( hdcMask )
{
SetLayout(hdcBitmap, LAYOUT_RTL);
SetLayout(hdcMask, LAYOUT_RTL);
}
else
{
DeleteDC( hdcBitmap );
hdcBitmap = NULL;
}
}
hdcScreen = GetDC(NULL);
if (hdcScreen)
{
if (hdcBitmap && hdcMask)
{
if (hiconOrg)
{
if (GetIconInfo(hiconOrg, &ii) && GetObject(ii.hbmColor, _
sizeof(BITMAP), &bm)
{
// Do the cleanup for the bitmaps.
DeleteObject( ii.hbmMask );
DeleteObject( ii.hbmColor );
ii.hbmMask = ii.hbmColor = NULL;
hbm = CreateCompatibleBitmap(hdcScreen,
bm.bmWidth, bm.bmHeight);
hbmMask = CreateBitmap(bm.bmWidth, bm.bmHeight,1, 1, NULL);
hbmOld = (HBITMAP)SelectObject(hdcBitmap, hbm);
hbmOldMask = (HBITMAP)SelectObject(hdcMask,hbmMask);
DrawIconEx(hdcBitmap, 0, 0, hiconOrg,bm.bmWidth, _
bm.bmHeight, 0, NULL, DI_IMAGE);
DrawIconEx(hdcMask, 0, 0, hiconOrg, _
bm.bmWidth,bm.bmHeight, 0, NULL, DI_MASK);
SelectObject(hdcBitmap, hbmOld);
SelectObject(hdcMask, hbmOldMask);

// Create the new mirrored icon and delete bitmaps

ii.hbmMask = hbmMask;
ii.hbmColor = hbm;
hicon = CreateIconIndirect(&ii);
DeleteObject(hbm);
DeleteObject(hbmMask);
}
}
}
ReleaseDC(NULL, hdcScreen);
}

if (hdcBitmap)
DeleteDC(hdcBitmap);
if (hdcMask)
DeleteDC(hdcMask);
return hicon;
}

Mirroring and Image-List Controls

A good way of dealing with graphics is to use image-list controls because of the wide flexibility these controls offer. Image-list controls are also used to manage graphics for other controls like tree-view and list-view controls. However, there is one potential issue that can arise. In the case of direction-sensitive bitmaps, if you are using an image list whose contents will be rendered on a mirrored device context, the image list will not have direct access to the image-list device context. On Windows XP, you can use the image-list flags ILC_MIRROR and ILC_PERITEMMIRROR to enable mirroring. If your code needs to run on Windows 2000, Windows 98, or Windows Me with mirroring support, you can use the previous code samples to mirror the direction-sensitive bitmap or icon before adding it to the list. Note, however, that the previous code will allow you to mirror such bitmaps and icons if you are adding these images to the list one by one. If you are adding images to the list more than one at a time, you can tweak the previous code to mirror each element inside the image strip that you are adding to the list. For example, you can modify CreateMirroredBitmap(), so that instead of this line:

BitBlt(hdcMem2, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem1, 0, 0, SRCCOPY); 

you can have the following code, where Width is the width of each individual image in the image strip that will be added to the image list:

int n = bm.bmWidth / Width, i;
for(i =0; i< n; i++)
{
BitBlt(hdcMem2, i * Width + 0, 0, Width, bm.bmHeight, hdcMem1, _
(n - i - 1) * Width, 0, SRCCOPY);
}

Specifying the width of each individual image will achieve the same effect on Windows 2000, Windows 98, or Windows Me as if you were to specify ILC_PERITEMMIRROR on Windows XP.

Top of pageTop of page

Mirroring .NET Framework

As was true for Web content, the DIR attribute can be used for mirroring text in Web Forms. The RightToLeft property-from the Control class-is valuable for providing RTL support for Windows Forms, whereas the MessageBox class supports RTL and mirroring for message boxes. In the sections that follow, you will learn how to take advantage of this support.

Mirrored Web Forms

In Microsoft Visual Studio .NET, you can create ASP.NET applications by using either Microsoft Visual Basic .NET or Microsoft Visual C# .NET. The guidelines that you should follow are basically the same as those you have just seen for mirroring Web content. When you design Arabic Web Forms pages, the best way to make text flow from right to left is to use the DIR attribute. As with Web content, this attribute is usually placed in the <HTML> tag or the <BODY> tag. Controls and HTML elements on the page then inherit the specified direction.

You can set the DIR attribute at a DOCUMENT object level. All the controls on the form will inherit the same settings. However, the DIR attribute can be used individually with other tags such as <TABLE> and in Web Forms controls, as in the following example, which allows items to be displayed from right to left:

<TABLE dir="rtl" ...><asp:TextBox dir="rtl" ...>  

Mirrored Windows Forms

Windows applications created in the .NET Framework allow you to access operating-system services and to take advantage of other benefits that your user's computing environment provides. Also, you can access data using ADO.NET. Additionally, GDI+ allows you to do advanced drawing and painting within your forms. Finally, your Windows applications can make calls to methods exposed through XML Web services.

Windows Forms can be standard windows, multiple-document interface (MDI) windows, dialog boxes, or display surfaces for graphical routines. The easiest way to define the UI for a form is to place controls on its surface. Forms are objects that have several functions. The objects expose properties that define the forms' appearance, methods which define their behavior, and events which define their interaction with the user. Windows Forms are controls because they inherit from the Control class. This class has a key property for RTL support-the RightToLeft property-which specifies whether the text is displayed from right to left. The form itself supports the RightToLeft property, as do all your controls.

Table 1: Values for the RightToLeft property
ValueDescription
InheritThe direction in which the text is rendered is inherited from the parent control.
NoThe text is rendered from left to right. This is the default value.
YesThe text is rendered from right to left.

What about design time? The Windows Forms Designer takes into account the effects of the RightToLeft property on controls. For example, when you set the RightToLeft property of the form to Yes, the property automatically updates the display of the form in the designer, so that the form's caption appears in the title bar right-aligned, as at run time. When the RightToLeft property value is set to RightToLeft.Yes, the horizontal alignment of the control's elements is reversed, yet the elements' alignment values are unchanged. For example, in a TextBox control with the TextAlign property value of HorizontalAlignment.Left, text is displayed right-aligned, but the property value remains HorizontalAlignment.Left. However, if the RightToLeft property value is set to RightToLeft.Yes and if the TextAlign property is set to HorizontalAlignment.Right, the text is displayed left-aligned.

Windows Forms don't support mirroring directly, as they do the RightToLeft property. Instead, you create your own controls to be displayed as you need. You can develop your own RTLTreeview control, for example, which inherits from the Treeview control and changes its style to be mirrored.

On some occasions, you might want to create a basic form with settings and properties such as a watermark or a certain control layout that you will then use again within a project. Each new form can contain modifications to the original form template. The solution is to use Windows Forms inheritance, which is a feature offered with the .NET Framework class library. For example, using inheritance you can build your own mirrored form by inheriting from the System.Windows.Forms.Form class and changing the window style of System.-Windows.Forms.Form.

Message Boxes

As its name suggests, the MessageBox class represents the message box; the Show method of this class displays a message box that can contain text, buttons, and symbols that inform and instruct the user. The MessageBox class fully supports RTL reading order and mirroring. The Show method of the MessageBox class takes constants defined in the MessageBoxOptions enumeration as parameters. These constants include RtlReading and RightAlign, both of which enable the message box to display bidirectional text. The following code illustrates how to display an Arabic message box:

[Visual Basic]
MessageBox.Show([put here your Arabic text],
MessageBoxButtons.OK,
MessageBoxIcon.Question,
MessageBoxDefaultButton.Button1,
MessageBoxOptions.RtlReading Or MessageBoxOptions.RightAlign)
 

RtlReading is responsible for ensuring that the text in the message box is displayed with RTL reading order, with mirroring applied. RightAlign allows the text to be right-aligned only, but does not enforce RTL reading order or mirroring in the message box.

Top of pageTop of page

Mirroring in Web Pages

Applying right-to-left layout to Web content is much easier than it is for Win32 applications. Microsoft Internet Explorer supports RTL rendering of HTML content natively. Toggling between LTR and RTL display in the browser is as simple as adding one attribute-direction (DIR)-to the HTML element. <HTML DIR=RTL> will cause the entire page to display from right to left. This means that the page is rendered as if the upper-right corner were the origin, with the x- axis increasing from right to left, as described earlier. A well constructed page will flow correctly with the DIR attribute set to either RTL or LTR. (If you do not use the DIR attribute, the layout will default to LTR.) There are important differences between placing the DIR attribute in the <HTML> tag and placing it in the <BODY> tag. DIR in the <BODY> tag will affect all visible elements in the document, but will not affect ambient properties (stationary elements). Placing DIR in the HTML tag will set the ambient property of the document to AMBIENT_RIGHTTOLEFT for ActiveX controls. Also, if RTL reading order is set in the HTML tag, the reading order of the document can be changed at run time by setting the DIR property of the document object by scripting.

To experiment with RTL rendering, try running the following page in Internet Explorer:

<HTML DIR=RTL>
<HEAD>
<TITLE>Playing with document.dir</TITLE>
<SCRIPT LANGUAGE=javascript>
<!--
function myfunction()
{
if (document.dir = "rtl")
document.dir = "ltr";
else document.dir = "rtl";
}
//-->
</SCRIPT></HEAD>
<BODY>
<P>This is some text...</P>
<P>
<INPUT type="button" value="Switch" onclick=myfunction()>
</P>
<Table border=1>
<TBODY>
<TR>
<TD width=100>Cell 1</TD>
<TD width=100>Cell 2</TD>
</TR>
<TR>
<TD width=100>Cell 1</TD>
<TD width=100>Cell 2</TD>
</TR>
</TBODY>
</Table>
</BODY>
</HTML>

The DIR attribute of an HTML file can also be modified first by right-clicking the content of the page and then choosing? the resulting context-sensitive menu's Encoding subitem (such as Right-To-Left Document).

The DIR attribute can be used in the following ways:

  • When direction is specified as RTL in the <HTML> tag, the page will be set with the correct extended styles so it displays the page as RTL on a bidirectional-enabled system, and so a vertical scroll bar appears on the left side.
  • When direction is specified as RTL in the <BODY> tag, frames and captions will not inherit the RTL direction.

The following HTML code sets the DIR attribute to RTL:

<HTML dir="rtl">
<!-- Or -->
<body dir="rtl" ...>

Despite the relative simplicity of mirroring Web content (when compared with mirroring Win32 applications), the complex positioning that is applied to HTML elements brings up several issues that you will need to be aware of whenever you are designing Web pages for RTL languages. One of the considerations that you should take into account includes how to properly handle directional images. You should also be aware of the problems associated with forced left alignment of text and with CSS absolute positioning. Finally, you should take advantage of the capabilities that tables offer for rendering RTL text.

Directional Images

Internet Explorer's rendering engine-Mshtml.dll-is enabled to handle RTL layout of Web content. Applying the DIR=RTL attribute does several things. It sets the reading order of the text to RTL, aligns the text to the right, and mirrors Web content such as dynamic elements, tables, and cells. However, applying the DIR=RTL attribute leaves the orientation of stationary elements, such as images, unchanged. This is mainly to prevent flipping of all the images that should not be flipped. Directional images, in contrast, need to be flipped and should follow the same layout as the text and content.

For example, Figure 8-11 shows sample text preceded by an arrow in the LTR document. Once the text is set to RTL, the arrow's original orientation remains the same, whereas it should be reversed.

Figure 8: The arrow to the left of the text in the LTR document needs to be reversed in the RTL document

Figure 8: The arrow to the left of the text in the LTR document needs to be reversed in the RTL document


In order to supply a single code base that renders correctly in either direction, it is not enough to simply provide alternate images for the bidirectional versions of the site. You must also include script in the Web page that dynamically changes the image depending on the rendering mode being used. To effect this change, you can use a CSS filter for the images on the site. The filter that is installed with Microsoft Internet Explorer 4 and later-known as "flipH"-flips any element horizontally, so that the element displays as a mirrored image. The following is the inline script that determines the direction of the images:

<Script Language=VBScript>
Option Explicit
Dim strFilter
If document.dir = "RTL" Then
strFilter = "filter:flipH;"
Else
strFilter = ""
End Ifdocument.write "<IMG SRC='images/arrow.gif' Style='" &
_strFilter & "'>"
</Script> 

Alternatively, in an HTML file, the code would be as follows:

<IMG Style=filter:flipH? SRC=C'images/arrow.gif'>

Forced Left Alignment of the Text

By default all tables, cells, and text in Latin-based script are aligned to the left. Explicitly adding the tag marked ALIGN=LEFT would overwrite the DIR attribute and would prevent the RTL text from being properly set to the right. Thus the ALIGN parameter in this case is superfluous. Because the default alignment for LTR display is left in any case, and because the default alignment for RTL display is right (since the DIR=RTL attribute has been applied), removing the ALIGN attribute completely will cause the page to display as intended. That is, it will be left-aligned for LTR languages, and right-aligned for RTL languages. As a general rule, less is more. If you put into your code only those elements and attributes necessary to produce the effect you need, you will avoid unnecessary headaches later on.

Absolute CSS Positioning

Absolute CSS positioning presents special problems for bidirectional Web sites. Figure 8-12 shows a dialog box after translation to Arabic and application of the RTL attribute. The buttons on the dialog box were placed using absolute CSS positioning. Not only do the buttons overlap the text, but the order of the buttons is fixed; the Yes button is fixed on the left side, and the No button is fixed to its right.

Figure 9: A dialog box in which the buttons, which were placed using absolute CSS positioning, both overlap the text and have a fixed order.

Figure 9: A dialog box in which the buttons, which were placed using absolute CSS positioning, both overlap the text and have a fixed order.


The following is the code that used absolute positioning:

<BUTTON ACCESSKEY="Y"
style="font-family: Arabic Transparent, arial; font-size: 10pt; position:absolute;
width: 58pt; height: 19pt; left: 146pt; top: 230pt" onclick="doOK()" >
<u>Y</u>es</BUTTON> 

In this case, absolute positioning was superfluous. Had the position attributes been left off the buttons, the buttons would have aligned themselves naturally. Removing the position, left, and top attributes (shown in bold in the previous code sample) from the style on the button causes the button to align more naturally below the text. In addition, allowing Internet Explorer to control the flow allows the button positions to be reversed, which is how they should be in this instance.

Reversibility Offered by Tables

Tables are the best and most reliable way to ensure that your content is correctly reversed when moving from LTR to RTL reading order, provided you do not specify alignment on the table cells. For example, take a look at the following HTML code:

<Table width=100% border=0>
<TR>
<TD bgcolor="#C0F000"><NOBR>Item 1 </NOBR></TD>
<TD width=30 bgcolor="#C0F000"></TD>
<TD width=30 bgcolor="#C0F000"></TD>
<TD bgcolor="#C0F000"><NOBR>Item 3 </NOBR></TD>
<TD width=700 bgcolor="#C0F000"></TD>
<TD bgcolor="#C0F000"><NOBR>Item 4 </NOBR></TD>
</TR>
</Table> 

To summarize, here is a list of general rules for site design abstracted from the information presented in the previous sections that can help ease the process of translation and text reversal:

  • Avoid specifying ALIGN=LEFT unnecessarily.
  • Avoid CSS absolute positioning.
  • Use tables for robust reversibility.
String Handling  UI Considerations

 

Top of pageTop of page

Previous 4 of 5 Next