 |
|
 |
This applies to the file: ImageButtonWithStyle.cpp
The funtcion draw_icon(...) uses the WINAPI-Function GetIconInfo(hIcon, &ii) which creates two Bitmaps. These Bitmaps won't be deleted using DeleteObject()
Here is my bugfixed version of the funcion:
void
CImageButtonWithStyle::draw_icon (HDC hDC, const CRect& Rect, DWORD style)
{
HICON hIcon = GetIcon ();
if (hIcon == NULL)
return;
ICONINFO ii;
GetIconInfo (hIcon, &ii); BITMAPINFO bmi;
memset (&bmi, 0, sizeof (BITMAPINFO));
bmi.bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
int cx = 0;
int cy = 0;
if (ii.hbmColor != NULL)
{
GetDIBits(hDC, ii.hbmColor, 0, 0, NULL, &bmi, DIB_RGB_COLORS);
cx = bmi.bmiHeader.biWidth;
cy = bmi.bmiHeader.biHeight;
}
else
{
GetDIBits(hDC, ii.hbmMask, 0, 0, NULL, &bmi, DIB_RGB_COLORS);
cx = bmi.bmiHeader.biWidth;
cy = bmi.bmiHeader.biHeight/2;
}
if (ii.hbmColor != NULL) {
DeleteObject(ii.hbmColor);
}
if (ii.hbmMask != NULL) {
DeleteObject(ii.hbmMask);
}
int x = image_left (cx, Rect, style);
int y = image_top (cy, Rect, style);
DrawState(hDC, NULL, NULL, (LPARAM) hIcon, 0, x, y, cx, cy,
(style & WS_DISABLED) != 0 ? (DST_ICON | DSS_DISABLED) : (DST_ICON | DSS_NORMAL));
}
|
|
|
|
 |
|
 |
This is a helpful approach, but I have run into an odd case where NM_CUSTOMDRAW messages are skipped. Since nobody has commented on it for the last five years, it might be new with VS 2008; at any rate it shows up when I try the demo project.
When the dialog with the buttons first comes up, tap the tab key very quickly and then use the arrow keys to switch between dialog items. The CImageButtonWithStyle controls promptly go back to the old 3D effect; it oddly enough doesn't happen with a slower key press, after which all subsequent tabs are fine.
As far as I can tell the difference is that a slower keypress sends out an extra WM_CHANGEUISTATE message. I haven't been able to figure out how to fix the problem, though, either by sending messages or changing handlers.
|
|
|
|
 |
|
 |
It worked great for me -- just dropped the four files in, followed directions, and it worked. Thank you for the class -- just what I needed.
Except for one thing -- it failed to center my 32x32 icon in a much smaller button (all I expected to display was the center of the icon). This worked fine with CButton, so since the new class is supposed be a drop-in replacement ....
Here is the fixed code, tested only for my case -- a 32x32 icon that I wanted centered.
static int
image_left (int cx, const CRect& Rect, DWORD style)
{
#ifdef ORIGINAL_CODE
int x = Rect.left;
if (cx > Rect.Width ())
cx = Rect.Width();
else if ((style & BS_CENTER) == BS_LEFT)
x = Rect.left;
else if ((style & BS_CENTER) == BS_RIGHT)
x = Rect.right - cx;
else
x = Rect.left + (Rect.Width () - cx)/2;
#else int x;
if ((style & BS_CENTER) == BS_LEFT)
x = Rect.left;
else if ((style & BS_CENTER) == BS_RIGHT)
x = Rect.right - cx;
else
x = Rect.left + (Rect.Width () - cx)/2;
#endif return (x);
}
static int
image_top (int cy, const CRect& Rect, DWORD style)
{
#ifdef ORIGINAL_CODE
int y = Rect.top;
if (cy > Rect.Height ())
cy = Rect.Height ();
#else int y;
#endif if ((style & BS_VCENTER) == BS_TOP)
y = Rect.top;
else if ((style & BS_VCENTER) == BS_BOTTOM)
y = Rect.bottom - cy;
else
y = Rect.top + (Rect.Height () - cy)/2;
return (y);
}
|
|
|
|
 |
|
 |
I guess I would need to check that themes is enbled for the current application, or the comctrl version, or... ?
The reason is, I have a few owner/custom draw buttons in all the apps I am responsible for, and want to put them under one code base. Some of the apps need to support W2K, though.
|
|
|
|
 |
|
 |
Call Stack:
->ntdll.dll!7c90120e()
[Frames below may be incorrect and/or missing, no symbols loaded for ntdll.dll]
Lava.exe!_CrtDbgBreak() Line 89 C
Lava.xee!_VCrtDbgReportA(int nRptType=2, const char * szFile=0x00861eb8, int nLine=84, const char * szModule=0x00000000, const char * szFormat=0x00000000, char * arglist=0x00127f44) Line 290 C
what seems the problem here?
~God Bless The Internet~
|
|
|
|
 |
|
 |
Our customer want to use keyboard navigate, highlight, and execute each buttons (my code draws a green frame to highlight the button currently has focus). But when the dialog first launches there is no green frame showed up. I employed Owner draw, the green box shows but I also get the classic 3D button.... inconsistent with other buttons.
I just want to draw 1st button when the dialog (formview) popup. My old code call CButton function SetButtonStyle(OWNERDRAW) to trigger DrawItem(). Now in your case How to trigger Customer Draw from parent window? Only parent window can decide which button has focus and need to show the green box.
Thanks
Bida
|
|
|
|
 |
|
 |
Using the BS_OWNERDRAW style and using NM_CUSTOMDRAW notifications are mutually exclusive approaches. If you use the BS_OWNERDRAW message, you'll never receive NM_CUSTOMDRAW messages.
However, you may be able to use some of the same CVisualStylesXP members in your DrawItem() member to produce the effect you're after. However, you'll also need to have code in your DrawItem() to deal with the case where XP themes are not enabled, or with Windows versions that don't include support for themes (i.e. they don't include uxtheme.dll).
Stephen C. Steel
Kerr Vayne Systems Ltd.
|
|
|
|
 |
|
 |
I'm going to do what you said:
"Forget BS_OWNERDRAW, Just Use NM_CUSTOMDRAW Instead".
can I trigger customer draw from parent window?
NM_CUSTOMDRAW sent by button to notify parent window about drawing operations. I'm trying to have parent window control which button need
customer draw now.
Thanks
bida
|
|
|
|
 |
|
 |
Excellent article. Just what the doctor ordered.
I have one problem however and quite possibly it is my lack of knowledge of MFC, but when I use your classes on an ActiveX control based on an dialog template, the imagebuttons on the ActiveX control remain highlighted after pressing them during runtime. I.e. the blue border around the button 'sticks'.
This does not happen with imagebuttons on the dialog on which this ActiveX control has been deployed, only the ActiveX control.
Has anyone seen this and knows what the cure is?
|
|
|
|
 |
|
 |
ImageButtonWithStyle.cpp has a bad assert:
ASSERT (pCustomDraw->hdr.code = NM_CUSTOMDRAW);
|
|
|
|
 |
|
 |
Well spotted. Definately a typo on my part. It should, of course, be a test for equality not an assignment:
ASSERT (pCustomDraw->hdr.code == NM_CUSTOMDRAW);.
It was intended to verify that the OnNotifyCustomDraw() handler was only called for NM_CUSTOMDRAW notifications.
Luckily, the intended ASSERT () was always true, so this typo shouldn't have caused any problems (since it was only assigning hdr.code the value it already had).
Stephen C. Steel
Kerr Vayne Systems Ltd.
|
|
|
|
 |
|
 |
When I use an Icon, I can set the transparent color. But using your bitmap, it doesn't go transparent when pressed. A round bitmap looks no good when the background should go transparent.
Any ideas how to make it work?
Nowlex
|
|
|
|
 |
|
 |
I have the same problem - did you find a way to work around this?
Thanks!
|
|
|
|
 |
|
 |
Bitmaps do not have a "transparent color". Therefore bitmaps are no good idea to use. It is better use icons instead
|
|
|
|
 |
|
 |
Hi, this is a very good class....
the only issue I noted is about the tooltips....I need to visualize a text description when the button has the focus (one description also into the status bar)...I tried using the string table..this is my code:
STRINGTABLE
BEGIN
IDC_CHECK_6X2 "6x2 visualization\n 6x2"
IDC_CHECK_ANNOTATIONS "visualize annotations \n annotations"
END
...but it does not work....
Have you any ideas?
tnx a lot.
Nicomaco
|
|
|
|
 |
|
 |
instead of icon resources ?
now, if I use a bitmap as the source of the image for the button, the whole "bitmap rectangle" is etched in compared to only what is not a background when using an icon.
Great article BTW !
Thanks.
Maximilien Lincourt
Your Head A Splode - Strong Bad
|
|
|
|
 |
|
 |
Very nice code! I wish I found it earlier it would've saved me so much time. Shame on MS for not providing this by default. Looks like someone was so eager to send off this XP into production. But anyway...
Here're three thing one need to add/change:
(1) Add support for BS_PUSHLIKE buttons in the OnNotifyCustomDraw() like this:
...
// determine state for DrawThemeBackground()
// note: order of these tests is significant
int state_id = PBS_NORMAL;
if (style & WS_DISABLED)
state_id = PBS_DISABLED;
else if (pCustomDraw->uItemState & CDIS_SELECTED)
state_id = PBS_PRESSED;
else if (pCustomDraw->uItemState & CDIS_HOT)
state_id = PBS_HOT;
else if(style & BS_PUSHLIKE && this->GetCheck() == BST_CHECKED) //<<<<<<<<THIS LINE
state_id = PBS_PRESSED; //<<<<<<<<THIS LINE
else if (style & BS_DEFPUSHBUTTON)
state_id = PBS_DEFAULTED;
...
(2) Make image depressed if button is in the PBS_PRESSED state. For this add 'state_id' variable into the following calls:
CImageButtonWithStyle::draw_bitmap (HDC hDC, const CRect& Rect, DWORD style, int state_id)
CImageButtonWithStyle::draw_icon (HDC hDC, const CRect& Rect, DWORD style, int state_id)
and add this into these functions right before a call to DrawState():
...
//Displace image if pressed
if(state_id == PBS_PRESSED ||
(style & BS_PUSHLIKE && this->GetCheck() == BST_CHECKED))
{
x++;
y++;
}
...
(3) It is not necessary to call bulky GetDIBits() API to get a size of bitmap. Use this instead:
BITMAP bm;
if(GetObject(hBitmap, sizeof(bm), &bm))
{
cx = bm.bmWidth;
cy = bm.bmHeight;
}
And of course delete both bitmaps returned by GetIconInfo(). But someone has already pointed it out. Thanks for a good sample again!
Cheers.
|
|
|
|
 |
|
 |
Thanks for the suggestions, they are all great. I'm afraid I simply overlooked the BS_PUSHLIKE style: I don't use it in the apps I wrote CImageButtonWithStyle for.
Displacing the image when the button is pressed is a nice refinement. and the simpler method of getting the bitmap size makes the code cleaner.
I'll add these changes to a new version shortly (along with the fix for the resource leak).
Stephen C. Steel
Kerr Vayne Systems Ltd.
|
|
|
|
 |
|
 |
I tried to upgraded the source to Visual Studio 2005 for VC++ project, but it went back to the old style. Why?
|
|
|
|
 |
|
 |
I don't have VS2005 installed yet, so I can't try this myself. By "it went back to the old style", do you mean ALL the buttons went back to the old style, or just the buttons with bitmaps/icons based on CImageButtonWithStyle?
Stephen C. Steel
Kerr Vayne Systems Ltd.
|
|
|
|
 |
|
 |
try to call ModifyStyle(...) before SetIcon(...)
example:
m_wnd_button1.ModifyStyle(0, BS_ICON);
m_wnd_button1.SetIcon (m_h_icon);
----
Yunce
|
|
|
|
 |
|
 |
Tried calling ModifyStyle but it still doesn't work when built in vs2005, the buttons are permanently in the old style
|
|
|
|
 |
|
 |
To make this work with Visual Studio 2005:
1) Remove the manifest file from the project resource files.
2) Open the properties for the project and on the
"Configuration Properties / Linker / Manifest File" page, set "Additional Manifest Dependencies" to the string below (include the beginning and end quotes).
"type='Win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='X86' publicKeyToken='6595b64144ccf1df' language='*'"
3) Do a full rebuild of the project.
|
|
|
|
 |
|
 |
Very useful class. Thanks for posting this. Just noticed that the button's disabled appearance does not display correctly if you use a 32-bit alpha channel icon/bitmap for the button image. Dropping the image down to 24-bit solves the problem. Any ideas how to use the full 32-bit images?
|
|
|
|
 |
|
 |
It appears you have to process alpha channel byte for disabled (i.e, gray) image in 32-bit mode. The transparency in that case is combined of two: mask bit (in case of icon) and alpha channel byte value (0=transparent, 255=opaque, and other combinations in between).
|
|
|
|
 |