Rounded Buttons : Does any one see any problems with this method?
-
Tuesday, August 11, 2009 11:56 PM
I decided to look into another way to render round buttons on Windows Mobile; it seems that most of the techniques out there rely on the button rendering code having knowledge on how the parent renders itself. I got the results that you would expect on first attempt.
I made a slight result to the code and ended up with the desired result. I got this result by hiding the button for a moment, forcing the parent to render itself, unhiding the button, and then rendering the button. The results were as follows.
It works, but a satisficing solution is not necessarily a good solution. In theory I could extend this technique to make transparent buttons. I wanted to share this method and see if any one had any comments on criticisms on using this method. The actual rendering code is posted below. If you'd like to see the entire Visual Studio 2008 project you can download it from this page.
BOOL DrawButton(HWND hWnd) { HDC hdc = GetDC(hWnd); RECT clientRect; RECT textRect; RECT drawTextRect; TCHAR szWindowText[100]; int width, height; int marginX, marginY; HWND hParent = GetParent(hWnd); if(hParent!=NULL) { RECT buttonRect; POINT upperLEft; POINT lowerRight; GetWindowRect(hWnd,&buttonRect); ShowWindow(hWnd,SW_HIDE); upperLEft.x = buttonRect.left; upperLEft.y = buttonRect.top; lowerRight.x = buttonRect.right; lowerRight.y = buttonRect.bottom; ScreenToClient(hParent, &upperLEft); ScreenToClient(hParent, &lowerRight); POINT p; buttonRect.top = upperLEft.y; buttonRect.left = upperLEft.x; buttonRect.bottom = lowerRight.y; buttonRect.right = lowerRight.x; InvalidateRect(hParent, &buttonRect, FALSE); SendMessage(hParent,WM_PAINT, 0, 0); ShowWindow(hWnd,SW_SHOW); InvalidateRect(hWnd,NULL,FALSE); } GetClientRect(hWnd,&clientRect); CopyMemory(&textRect, &clientRect, sizeof(RECT)); width = clientRect.right - clientRect.left; height = clientRect.bottom - clientRect.top; marginX = width / 20; marginY = height / 20; GetWindowText(hWnd, szWindowText, 100); RoundRect(hdc, clientRect.left, clientRect.top, clientRect.right, clientRect.bottom, 40, 40); //RoundRect(hdc, clientRect.left+marginX, clientRect.top+marginY, clientRect.right-marginX, clientRect.top+(height/2), 40, 40); DrawText(hdc, szWindowText, -1, &textRect, DT_END_ELLIPSIS|DT_CENTER|DT_CALCRECT); drawTextRect.top = (height - textRect.bottom) / 2; drawTextRect.left = (width - textRect.right) / 2; drawTextRect.right = drawTextRect.left + textRect.right; drawTextRect.bottom = drawTextRect.top + textRect.bottom; DrawText(hdc, szWindowText, -1, &drawTextRect, DT_END_ELLIPSIS|DT_CENTER); ValidateRect(hWnd, NULL); ReleaseDC(hWnd, hdc); return TRUE; }
Joel Ivory Johnson- Edited by Joel Ivory JohnsonMVP Friday, December 25, 2009 12:19 PM
Answers
-
Wednesday, August 12, 2009 1:43 AM
Hi Joel,
There are a number of potential issues with this technique.
Some controls won't respond "properly" to being sent explicit WM_PAINT messages. Some of the webbrowser controls for example fall into this category. João Paulo Figueira mentions this in his blog post http://nativemobile.blogspot.com/2008/05/animating-child-view-transitions.html where he discusses producing sliding style transistions between two forms.
The technique also has difficulties if more than one window or control is located in the "transparent" regions. As an example if your parent window has a child static control which overlaps this region, the text won't get drawn if you request the parent to repaint.
I would be interested in your research and any eventual conclusions you reach, as I have a couple of ideas for custom controls where it is impractical (or atleast undesierable) for the custom control to have too much interation with its parent, but currently have only implemented work arounds similiar to the techniques you note.
Hope it helps,
Christopher Fairbairn
Visit my blog at http://www.christec.co.nz/blog/- Marked As Answer by Joel Ivory JohnsonMVP Tuesday, August 18, 2009 2:28 AM
-
Wednesday, August 12, 2009 6:49 AM
Hi Joel,
it is really nice, that with this solution no specific code at the parent window is needed.
A Problem I see with this approach is that you may get some flickering depending on how complex the button (or other control) is to get drawn, because while it is drawn the complete background of the parent is shown on the screen.
I'm not pretty sure about this, but shouldn't you use "PostMessage" instead of "SendMessage(hParent,WM_PAINT, 0, 0);" to be really sure that the background got repainted before you start drawing your button?.
Just my two cents...
Maik- Marked As Answer by Joel Ivory JohnsonMVP Tuesday, August 18, 2009 2:29 AM
-
Wednesday, August 12, 2009 9:11 AM
Here's a suggestion for an alternative approach: a few years ago I wrote this article where I used non-window rounded buttons to implement a numeric pad control for Windows Mobile. All buttons are drawn on the same window so you don't get any of the child window features such as clipping and coordinate transformations. On the other hand, you are free to draw however and whatever you like.
João Paulo Figueira (Device Application Development MVP)- Marked As Answer by Joel Ivory JohnsonMVP Tuesday, August 18, 2009 2:29 AM
-
Wednesday, August 12, 2009 8:23 PM
Wouldn't it be much faster to prepare proper png resources (for VGA), resize those (if needed) and use IImaging API to draw transparent? Seems faster and doesn't require mendling with 100 lines of code.
If You'll find my answer satisfactory or helpful - mark it as answered! Thank You. PS. Votes also doesn't hurt :).- Marked As Answer by Joel Ivory JohnsonMVP Tuesday, August 18, 2009 2:32 AM
-
Thursday, August 13, 2009 3:35 AM
Hi Joel,
My eventual solution to any kind of graphical manipulation on WM turns out that I pretty much have to draw the entire window and anything in it using my own graphic routines and calculations. I tried long ago OWNER_DRAW versions of buttons, but they too were limited and caused flicker under certain instances.
In the end, this becomes the realm of graphics game programming techniques. Double buffering the whole screen and drawing everything yourself. Yes, it's a pain. But with that much control, you can also produce the best results.
We can only hope that Microsoft with WM7 might provide a unified graphics library to handle a shiny new GUI for us. Otherwise, the grunt work is left up to us. :/
p.s. I like your blog and too am expanding my focus to Android. Of course, now I need to learn Java. (oi!). but I digress.
ttyl
- Marked As Answer by Joel Ivory JohnsonMVP Tuesday, August 18, 2009 2:29 AM
-
Friday, August 28, 2009 4:06 PM
Nice technique! It was just wait I needed - almost.
I did discover a problem with it. When you hide and show the buttons, it messes up keyboard focus handling. This is a big problem if you need keyboard support (as I do).
I modified the technique to resize the window using MoveWindow(x, y, 1, 1) instead of hiding, and restoring the size instead of showing. It works great, and doesn't change the current focus!
Once other thing I noticed: I was drawing the button in response to the DrawItem message. When I changed the window size, the HDC in the DRAWITEMSTRUCT apparently became invalid. I had to create a new WindowDC to get painting to work. It worked on on the PC, but not on a device.
Thanks for the great idea!- Marked As Answer by Joel Ivory JohnsonMVP Friday, August 28, 2009 4:13 PM
All Replies
-
Wednesday, August 12, 2009 1:43 AM
Hi Joel,
There are a number of potential issues with this technique.
Some controls won't respond "properly" to being sent explicit WM_PAINT messages. Some of the webbrowser controls for example fall into this category. João Paulo Figueira mentions this in his blog post http://nativemobile.blogspot.com/2008/05/animating-child-view-transitions.html where he discusses producing sliding style transistions between two forms.
The technique also has difficulties if more than one window or control is located in the "transparent" regions. As an example if your parent window has a child static control which overlaps this region, the text won't get drawn if you request the parent to repaint.
I would be interested in your research and any eventual conclusions you reach, as I have a couple of ideas for custom controls where it is impractical (or atleast undesierable) for the custom control to have too much interation with its parent, but currently have only implemented work arounds similiar to the techniques you note.
Hope it helps,
Christopher Fairbairn
Visit my blog at http://www.christec.co.nz/blog/- Marked As Answer by Joel Ivory JohnsonMVP Tuesday, August 18, 2009 2:28 AM
-
Wednesday, August 12, 2009 2:04 AMYou make some good points. I hadn't considered the scenario of a sibling overlapping a transparent portion of the control. So I'll need to identify the requirements for this technique to be applicable.
Joel Ivory Johnson -
Wednesday, August 12, 2009 6:49 AM
Hi Joel,
it is really nice, that with this solution no specific code at the parent window is needed.
A Problem I see with this approach is that you may get some flickering depending on how complex the button (or other control) is to get drawn, because while it is drawn the complete background of the parent is shown on the screen.
I'm not pretty sure about this, but shouldn't you use "PostMessage" instead of "SendMessage(hParent,WM_PAINT, 0, 0);" to be really sure that the background got repainted before you start drawing your button?.
Just my two cents...
Maik- Marked As Answer by Joel Ivory JohnsonMVP Tuesday, August 18, 2009 2:29 AM
-
Wednesday, August 12, 2009 9:11 AM
Here's a suggestion for an alternative approach: a few years ago I wrote this article where I used non-window rounded buttons to implement a numeric pad control for Windows Mobile. All buttons are drawn on the same window so you don't get any of the child window features such as clipping and coordinate transformations. On the other hand, you are free to draw however and whatever you like.
João Paulo Figueira (Device Application Development MVP)- Marked As Answer by Joel Ivory JohnsonMVP Tuesday, August 18, 2009 2:29 AM
-
Wednesday, August 12, 2009 8:15 PMThanks, that points out a performance aspect that needs to be evaluated.
PostMessage puts a message on the queue for later processing. But messages sent through SendMessage are processed immediatly.
Joel Ivory Johnson -
Wednesday, August 12, 2009 8:23 PM
Wouldn't it be much faster to prepare proper png resources (for VGA), resize those (if needed) and use IImaging API to draw transparent? Seems faster and doesn't require mendling with 100 lines of code.
If You'll find my answer satisfactory or helpful - mark it as answered! Thank You. PS. Votes also doesn't hurt :).- Marked As Answer by Joel Ivory JohnsonMVP Tuesday, August 18, 2009 2:32 AM
-
Wednesday, August 12, 2009 8:38 PM
I don't know if it would be faster or not. I've not done any formal performance testing at all.
The bigger concern I have with images is that changing the color of the button at run time gets a little more complex. I've simplified the code presented here but I've got code that renders the button with gradients and results in the button looking shinny. If I want to render the button in a differet color then then there is only one value to change and the new gradient colors are calculated.
I'm not too concerned with the number of lines of code in this solution. Once it works I'd be able to reuse it. So most of the effort would be up front for the initial development. For subsequent projects I would just include this code in the project.
Joel Ivory Johnson -
Wednesday, August 12, 2009 10:13 PMI just bookmarked that article. That solution has a lot of potential.
Joel Ivory Johnson -
Thursday, August 13, 2009 3:35 AM
Hi Joel,
My eventual solution to any kind of graphical manipulation on WM turns out that I pretty much have to draw the entire window and anything in it using my own graphic routines and calculations. I tried long ago OWNER_DRAW versions of buttons, but they too were limited and caused flicker under certain instances.
In the end, this becomes the realm of graphics game programming techniques. Double buffering the whole screen and drawing everything yourself. Yes, it's a pain. But with that much control, you can also produce the best results.
We can only hope that Microsoft with WM7 might provide a unified graphics library to handle a shiny new GUI for us. Otherwise, the grunt work is left up to us. :/
p.s. I like your blog and too am expanding my focus to Android. Of course, now I need to learn Java. (oi!). but I digress.
ttyl
- Marked As Answer by Joel Ivory JohnsonMVP Tuesday, August 18, 2009 2:29 AM
-
Thursday, August 13, 2009 1:03 PMThanks! Was looking at tyour GPS program a few days ago and wondering how you went about rendering your buttons on it.
Joel Ivory Johnson -
Thursday, August 13, 2009 1:21 PMI double-buffer the entire window and compose it off screen. No flicker, and perfect transition between portrait and landscape modes.
The buttons are bitmaps. I load two sets, depending on the DPI (hence resolution) of the device..
one for QVGA, one for VGA (also works on W/X VGA too then).
I am currently working on drawing my own buttons within a scrolling dialog and playing with the right gradients and all. It's more flexible and doesn't require me to store fixed bitmaps. But it's more tricky to code. Of course once you got it working, then it's also more flexible.
It brings me back to my good'ol game programming days on the 286 machines, in assembly. ;)
Of course, you would think that 15 years later, these kind of challenges would have long been resolved and made easier. :/
We should chat some time. You can contact me through my web site.
Cheers. -
Thursday, August 13, 2009 3:46 PMJust a short note I have found with GDI: sometimes drawing the whole screen from the offscreen bitmap hurts performance, especially when you are making small changes to the screen (not scrolling it, of course). I solved this issue by duplicating the GDI "invalid region" feature with a list of invalid RECT structures (yes, reinventing the wheel...). The net result is faster than painting the whole screen bitmap with GDI (DD should be way faster).
João Paulo Figueira (Device Application Development MVP) -
Tuesday, August 18, 2009 2:32 AMYou've actually got my e-mail address, though you may not remember. Back inFebruary I mentioned something to you about Exchange accounts not beind detected, only pop 3 were. I'll send you another message in a bit.
Joel Ivory Johnson -
Tuesday, August 18, 2009 2:33 AM
-
Friday, August 28, 2009 4:06 PM
Nice technique! It was just wait I needed - almost.
I did discover a problem with it. When you hide and show the buttons, it messes up keyboard focus handling. This is a big problem if you need keyboard support (as I do).
I modified the technique to resize the window using MoveWindow(x, y, 1, 1) instead of hiding, and restoring the size instead of showing. It works great, and doesn't change the current focus!
Once other thing I noticed: I was drawing the button in response to the DrawItem message. When I changed the window size, the HDC in the DRAWITEMSTRUCT apparently became invalid. I had to create a new WindowDC to get painting to work. It worked on on the PC, but not on a device.
Thanks for the great idea!- Marked As Answer by Joel Ivory JohnsonMVP Friday, August 28, 2009 4:13 PM
-
Friday, August 28, 2009 4:13 PM
-
Thursday, September 03, 2009 10:29 AMHi,
I would like to try your solution, can you please explain what do you mean by resize the window (x, y, 1, 1)?
When do you use it?
Thanks. -
Friday, September 18, 2009 3:19 PMHi!
Where I can find the C# version of the code?
Thank you!

