Mirror, mirror... before whom I fuss, does mirroring work well in GDI+?

by Michael S. Kaplan, published on 2006/05/28 20:01 -04:00, original URI: http://blogs.msdn.com/b/michkap/archive/2006/05/28/609369.aspx


Indeed, this is the question I came to ask the mirror, while the wicked queen was napping (or maybe she was off getting a facial?):

Mirror, mirror... before whom I fuss,
Does mirroring work well in GDI+?

Of course the mirror cannot lie, so it is forced to answer truthfully:

Thy international features are very powerful, 'tis true;
But GDI+'s mirroring capabilities will simply never do.
Sometimes it reverses one time too many for thee,
Other times it is off by a pixel or three.
Even when it does work, this won't add up to beans;
For Michael you, of all people, know what 'unsupported' means!

Annoyed, I decide to risk seven years of bad luck and break the freaking mirror. But anticipating that it might eventually come to this, I spoke with an attorney prior to the B&E, and he is convinced he can get the 7 years dropped down to 2⅓, possibly even with suspended sentence....

As I slip out of the wicked queen's chamber, I wonder whether it would have been safer to try to look in MSDN for the answer. :-)

(I don't know whether to be proud of the intro to this post or embarrassed about the fact that it took more time to write than the remaining text!)

I have talked a little about mirroring in previous posts.

But a question I have been asked before and which comes up from time to time on discussion lists is how to make mirroring work properly in GDI+ -- or in the managed world.

The short answer (covered in the GlobalDev article Mirroring Awareness) is that it doesn't:

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.

In other words, the suggestion is to create two controls, and hide one of them. :-(

The real truth is, as I said, that GDI+ does not support mirroring properly, and there are at present no plans to change that.

If you do the work to mirror a device context and the DC is clipped, then GDI+ will write to the wrong location (a pixel or two off). A few code workarounds have been suggested to fix the problem, such as using the GDI OffsetRect function to adjust the clipping rectangle:

RECT rcHDC, rcClip;
::GetClientRect(hwnd, &rcHDC );
::GetClipBox(hdc, &rcClip );
::OffsetRect(rcDestination,
             (rcClip.left - rcHDC.left) - (rcHDC.right - rcClip.right),
             0);

Other people actually just move it over one pixel, but doing the calculation feels a bit safer to do (and it doesn't always look like a single pixel to me!).

Now note that this only solves the 'slight pixel off' problem. The other problem that can occur is that the text can simply not flip at all (presumably due to the strange interactions of the RightToLeft property and the mirroring property for all new windows (set via the SetProcessDefaultLayout function).

The result? Everything can end up not being mirrored, since the mirror image of a mirror image looks like the original. :-(

Luckily, the fix for this is also reasonably straightforward -- you can simply remove the RTL layout flag if it is in there, so you can let the RightToLeft property do its thing without interference

        // Disable flipping if necessary, to avoid being 'double-flipped'
        DWORD dwLayout = ::GetLayout(hdc);
        if (dwLayout & LAYOUT_RTL) {
            ::SetLayout(hdc, dwLayout & ~LAYOUT_RTL);
        }

(I suppose you can also go the other way around -- keeping the layout LTR in the property and using the mirroring stuff to do the flipping -- the key is to make sure that you do not use both -- though I have not actually tried this method, myself)

Now, for both of these problems, my advice is not to do anything unless you are actually seeing the problems in question (e.g. you may actually be using GDI rather than GDI+ without realizing it!). The complaints come up often enough that I am pretty sure you will notice it pretty quickly if you try to mirror a managed application....

For direct text writing to a device context, It may be worth considering using the TextRenderer class, which neatly avoids the bulk of these issues.

There are other controls that have additional issues. For more information on them, the article How to: Display Right-to-Left Text in Windows Forms for Globalization can be somewhat useful, but the ultimate reference for the whole issue can be found in the document entitled Bi-Directional Support for Windows Forms Applications. It talks not only about the RightToLeft property, but also the (new in 2.0) RightToLeftLayout property that helps to overcome some of the 1.0/1.1 limitations. It also contains a handy reference chart for how each control supports things.

 

This post brought to you by "ק" (U+05e7, a.k.a. HEBREW LETTER QOF)


no comments

Please consider a donation to keep this archive running, maintained and free of advertising.
Donate €20 or more to receive an offline copy of the whole archive including all images.

referenced by

2010/11/06 Please stop using this turd. And if you are an MS employee: stop using this turd. Now.

go to newer or older post, or back to index or month or day