I'm trying to modify some old code (c++) that uses the CreateToolbarEx function, in an attempt to use a new bitmap and replace our 24x24 pixel toolbar buttons with flashier 36x36 ones. The function I'm using is as follows:
m_hToolbarWnd = CreateToolbarEx(m_hPagerWnd, ws, ID_TOOLBAR, NUMBERTOOLBARBITMAPS, hInst, IDB_TOOLBAR, tbInitButtons, m_ncButtons, 24, 24, 24, 24, sizeof(TBBUTTON));
I'm able to expand the size of the current toolbar buttons by changing the '24's to '36', but if I change IDB_TOOLBAR to the new toolbar bitmap and run the program I hit a memory access read violation pointing to the CreateToolbarEx function. Am I missing something on how the bitmap is getting its memory allocation or creating the individual buttons? The new toolbar is 1584x36 pixels (44 buttons).
This is old, but hopefully the solution I found will help someone. I had overlooked the fact that I was changing from a 16-bit color bitmap to 24-bit, which I was unable to get CreateToolbarEx to handle. Instead, I had to call CreateWindowEx and create and set the icon ImageList for it. Working code:
m_hToolbarWnd = CreateWindowEx(0L, TOOLBARCLASSNAME, "", ws, 36, 36, 36, 36, m_hPagerWnd, (HMENU) ID_TOOLBAR, hInst, NULL);
SendMessage(m_hToolbarWnd, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0);
HBITMAP hBmp = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_TOOLBAR));
HIMAGELIST hIcons = ImageList_Create(36, 36, ILC_COLOR24, 0, m_ncButtons);
ImageList_Add(hIcons, hBmp, NULL);
SendMessage(m_hToolbarWnd, TB_SETIMAGELIST, 0, (LPARAM) hIcons);
Related
I've been looking for a way to make a picture a button in c++ for a few hours now.. I've found stuff on using bitmaps, what what i am currently using to display the image is GDI+, because i want to use jpg/png files.
This is how i created my Image with gdiplus:
void Example_DrawImage(HDC hdc) {
Graphics graphics(hdc);
image = Image::FromFile(L"Path:/To/Image");
myBitmap = dynamic_cast<Bitmap*>(image);
Pen pen(Color(0, 0, 0, 0), 2);
graphics.DrawImage(image, 10, 10);
}
I converted that to a bitmap with:
myBitmap = dynamic_cast<Bitmap*>(image);
Then, in WM_CREATE I created a button which it's standard style is a Windows XP Button:
button = CreateWindow(TEXT("button"), TEXT("Hello"),
WS_VISIBLE | WS_CHILD | BS_BITMAP,
10, 10, /* x & y*/ 80, 25, /*width & height*/
hwnd, (HMENU) 1, hInstance, NULL
);
button is globally defined as HWND button;
All I want is to have a button that is a jpg picture. I tried doing it manually by seeing if a mouse click was inside a certain area, but I could not find a way to find the position of the Image.
This is hopefully simple: I have an application which uses CListCtr in report mode and I want to assign icons to the entries. The icons should be simple square uniformly coloured icons 16x16.
I know ( - I think I know - ) how to handle CListCtr and CImageList, but the challenge I have is the following:
The icon colour is created by the code and arbitrary COLORREF
The icons are not created as files in my resources but should be created by code also. If need be, there can be one 'template' icon in which a specific color is replaced by COLORREF from above.
Essentially, I want to 'abuse' the images as kind of status-light in my CListCtr but with arbitrary colour.
Can anybody give me a short example code snipped for:
Creating a HICON element which is a 16x16 uniformly filled bitmap of COLORREF specified colour?
Try something like this:
CDC dc;
dc.CreateCompatibleDC(NULL);
m_bitmap.CreateBitmap(16, 16, dc.GetDeviceCaps(PLANES), dc.GetDeviceCaps(BITSPIXEL), NULL);
m_imageList.Create(16, 16, ILC_COLORDDB, 0, 1);
HGDIOBJ hOld = dc.SelectObject(m_bitmap);
dc.FillSolidRect(0, 0, 16, 16, RGB(0xff, 0, 0));
dc.SelectObject(hOld);
m_imageList.Add(&m_bitmap, RGB(0, 0, 0));
m_listCtrl.SetImageList(&m_imageList, LVSIL_SMALL);
m_listCtrl.InsertItem(0, _T("hello"), 0);
Slight modification to the accepted answer from mockfrog above.
This also uses the trick I've learnt of here: Using CImageList to convert from HBITMAP to HICON.
Note that it's important to use ILC_COLOR32 to get the full range of colours and not some unwanted colour casting.
HICON CCMixTP_Dialog::CreateColorIcon2( COLORREF color )
{
CDC dc;
dc.CreateCompatibleDC(NULL);
CBitmap bitmap;
bitmap.CreateBitmap(16, 16, dc.GetDeviceCaps(PLANES), \
dc.GetDeviceCaps(BITSPIXEL), NULL);
CImageList convertBuffer;
convertBuffer.Create(16, 16, ILC_COLOR32, 1, 1);
HGDIOBJ hOld = dc.SelectObject(bitmap);
dc.FillSolidRect(0, 0, 16, 16, color);
dc.SelectObject(hOld);
convertBuffer.Add( &bitmap, RGB(0,0,0) ); // Mask black pixels as black.
return convertBuffer.ExtractIcon(0);
}
My advise is not to create icons in this way.
Just use NM_CUSTOMDRAW to draw the element by yourself. You find a great explanation here in this two articles. With this code it is simple to draw the specified area in the color you want!
Article1 Article2
Creating an icon is possible with CreateIconIndirect. You just need to create 2 bitmaps. The drawback here is you need to destroy all theicons you create.
h
I'm trying to understand the behaviour of a Windows toolbar - in particular how the following values interact:
the size of the bitmap image used
the effective size of a toolbar button
the padding between the image and the button edge
the height of the toolbar
Text displayed by a button is not relevant in my case.
What I actually want to do is provide an option for the user so he can choose from several toolbar button sizes (that will display bitmaps of say, 16x16, 32x32, or 48x48 pixels) and redisplay the toolbar accordingly after the option value changes. This is implemented by destroying the toolbar's image lists and rebuilding them with the appropriate bitmaps. The problem I currently have is that when switching from size 16 to 48 and back to size 16, the toolbar looks slightly different than before.
This is what the toolbar looks like when the application starts (correct):
Once I switch to size 48 and back again, it looks like this (wrong):
All buttons are higher than before, and each dropdown button has additional space around its bitmap and its dropdown arrow.
(For testing purposes, the toolbar has been made high enough to accomodate all button sizes without requiring an increase in height. This is to rule out the possibility that the change in button size stems from a possible toolbar resize, necessitated by temporarily switching to size 48.)
It looks as if additional padding were being rendered between a button bitmap and the button edge - as if rebuilding the toolbar with larger bitmaps/buttons caused Windows to internally increase the padding (which would make sense), but not decrease it when I subsequently rebuild the toolbar with the smaller bitmaps/buttons. However, sending TB_GETPADDING always returns 0x00060007, which indicates that the standard (correct) padding for 16x16 bitmaps is in place.
In an attempt to solve the problem by setting padding myself, I set the TBSTYLE_AUTOSIZE style on all non-separator buttons (this is required in order to apply padding). With this style, without even calling TB_SETPADDING, after switching to size 48 and back again, the toolbar looks like this:
In this case, the button height is also wrong.
The question is: What is causing the buttons to be displayed differently after rebuilding the image lists?
Some aside notes:
When building the toolbar, I call TB_SETBITMAPSIZE, but neither TB_SETBUTTONSIZE nor TB_SETPADDING, because the bitmap size is all I have, and I assumed the button size would be derived correctly from that.
I'm aware I could simply build the entire toolbar window from scratch (not just the image lists), but would like to avoid that, so I can keep working with the same toolbar window handle.
I'm aware of the CCS_NORESIZE toolbar style (it's currently set) and the TB_AUTOSIZE message, but experiments with them have not led to any insights.
I can't say what is the problem(there is no code in the question) but it is most probable
that the solution of destroying the list of images causes this. You dont need to destroy the lists
but to remove the buttons and then add new ones. The bellow code works fine:
Create ToolBar:
if((toolBarHwnd = CreateWindowEx(
0,
TOOLBARCLASSNAME,,
NULL,
WS_VISIBLE | WS_CHILD | TBSTYLE_WRAPABLE,
0,
0, //820,
0,
0,
winHwnd, //main window
(HMENU)IDC_TOOLBAR,
hThisInstance,
NULL
)) == NULL){/*Error*/}
Create ImageList's for your images:
HIMAGELIST g_hImageListSmall = NULL, g_hImageListMedium = NULL, g_hImageListLarge = NULL;
int numButtons = 3
g_hImageListSmall = ImageList_Create(16, 16, // Dimensions of individual bitmaps.
ILC_COLOR16 | ILC_MASK, // Ensures transparent background.
numButtons, 0);
g_hImageListMedium = ImageList_Create(32, 32,
ILC_COLOR16 | ILC_MASK,
numButtons, 0);
g_hImageListLarge = ImageList_Create(48, 48,
ILC_COLOR16 | ILC_MASK,
numButtons, 0);
Add images to the lists:
HBITMAP hBitmapImageSmall = NULL, hBitmapImageMedium = NULL, hBitmapImageLarge = NULL;
hBitmapImageSmall = LoadImage(NULL, L"....YourBitmap.bmp", IMAGE_BITMAP, 16, 16, 0x10);
ImageList_Add(g_hImageListSmall , hBitmapImageSmall, NULL);
ImageList_Add(g_hImageListSmall , hBitmapImageSmall, NULL);
ImageList_Add(g_hImageListSmall , hBitmapImageSmall, NULL); //I am using the same image
hBitmapImageMedium = LoadImage(NULL, L"....YourBitmap.bmp", IMAGE_BITMAP, 32, 32, 0x10);
ImageList_Add(g_hImageListSmall , hBitmapImageMedium , NULL);
ImageList_Add(g_hImageListSmall , hBitmapImageMedium , NULL);
ImageList_Add(g_hImageListSmall , hBitmapImageMedium , NULL);
The same with the large one(48x48)
Add g_hImageListSmall to the ToolBar for start:
//Set the image list.
SendMessage(toolBarHwnd, TB_SETIMAGELIST, (WPARAM)0, (LPARAM)g_hImageListSmall);
// Initialize button info.
// IDM_NEW, IDM_OPEN, and IDM_SAVE are application-defined command constants.
TBBUTTON tbButtons[numButtons] =
{
{ 0, IDM_NEW, TBSTATE_ENABLED, BTNS_AUTOSIZE, {0}, 0, (INT_PTR)NULL },
{ 1, IDM_OPEN, TBSTATE_ENABLED, BTNS_AUTOSIZE, {0}, 0, (INT_PTR)NULL},
{ 2, IDM_SAVE, TBSTATE_ENABLED, BTNS_AUTOSIZE, {0}, 0, (INT_PTR)NULL}
};
// Add buttons.
SendMessage(toolBarHwnd, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
SendMessage(toolBarHwnd, TB_ADDBUTTONS, (WPARAM)numButtons, (LPARAM)&tbButtons);
// Resize the toolbar
SendMessage(toolBarHwnd, TB_AUTOSIZE, 0, 0);
That is the first step.
Write two functions:
void RemoveButtons(void){
int nCount, i;
// Remove all of the existing buttons, starting with the last one.
nCount = SendMessage(toolBarHwnd, TB_BUTTONCOUNT, 0, 0);
for(i = nCount - 1; i >= 0; i--){ SendMessage(toolBarHwnd, TB_DELETEBUTTON, i, 0); }
return;
}
enum{SMALL, MEDIUM, LARGE};
void AddButtons(int sizeButtons){
if(sizeButtons == SMALL){
SendMessage(toolBarHwnd, TB_SETIMAGELIST, (WPARAM)0, (LPARAM)g_hImageListSmall);
}
else if(sizeButtons == MEDIUM){
SendMessage(toolBarHwnd, TB_SETIMAGELIST, (WPARAM)0, (LPARAM)g_hImageListMedium);
}
else{
SendMessage(toolBarHwnd, TB_SETIMAGELIST, (WPARAM)0, (LPARAM)g_hImageListLarge);
}
// Add buttons.
SendMessage(toolBarHwnd, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
SendMessage(toolBarHwnd, TB_ADDBUTTONS, (WPARAM)numButtons, (LPARAM)&tbButtons);
// Resize the toolbar
SendMessage(toolBarHwnd, TB_AUTOSIZE, 0, 0);
return;
}
When ever you want to change the size of the buttons in ToolBar:
RemoveButtons();
AddButtons(LARGE); //or SMALL, MEDIUM
References:
How to Create Toolbars
How to Customize Toolbars
The common controls have been a major bug factory in Windows. Microsoft has had a great deal of trouble keeping them compatible across 6 major Windows releases and 10 versions of comctl32.dll. Particularly the visual style renderers have been a problem spot.
Core issue is that the api for them was set in stone 18 years ago with no reasonable way to make it work differently from the way it worked in their first release. Their code acquired a great many deal of appcompat hacks to achieve this. Such an hack will for example doctor a value that was returned by the previous version so that the client program has no idea, and doesn't need to know, that it is working with a very different version from the one it was tested against.
This has side-effects, the kind you'll discover when you use the controls in an unusual way that's very different from the way they are normally used by meat-and-potatoes Windows programs. Exactly like your scenario. Very high odds that you are battling internal state of the toolbar that you cannot see and doesn't get properly restored when you switch sizes. Quite undebuggable, that internal state isn't visible at all. Other than from the undesirable side-effects.
The solution is the one you already know. Recreate the toolbar from scratch. It can't go wrong that way.
I'm writing a function that quickly draws an image of a menu for a game I'm making. I'm able to draw the background and text blocks just fine but I'm having trouble creating a bitmap image on the screen
bool menu::drawMenu(PAINTSTRUCT ps)
{
HWND hWnd = GetActiveWindow();
HDC hdc = GetDC(hWnd), hdcMem;
//Draw a new background
HPEN blackPen = CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
HBRUSH blackBrush = CreateSolidBrush(RGB(0, 0, 0));
SelectObject(hdc, blackPen);
SelectObject(hdc, blackBrush);
Rectangle(hdc, 0, 0, 1080, 720);
//insert selection text
TextOut(hdc, 30, 0, L"New Game", 8);
TextOut(hdc, 30, 30, L"Exit Game", 9);
//draw arrow sprite
HBITMAP arrow = (HBITMAP)LoadImage(NULL, L"C:\\Users\\Tim\documents\\visual studio 2013\\Projects\\BoulderBisque\\BoulderBisque\\arrow.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
DWORD lastError = GetLastError();
if (arrow == NULL)
{
TextOut(hdc, 0, 60, L"Image Load Failed", 17);
return false;
}
hdcMem = CreateCompatibleDC(hdc);
if (hdcMem == NULL)
{
TextOut(hdc, 0, 90, L"Memory Creation Failed", 22);
return false;
}
SelectObject(hdcMem, arrow);
BitBlt(hdc, 0, choice * 30, 16, 16, hdcMem, 0, 0, SRCCOPY);
//cleanup
ReleaseDC(hWnd, hdc);
DeleteDC(hdcMem);
return true;
}
As of now arrow is NULL and I get the Load Image Failed textbox.
I am using the relative path of arrow.bmp I also tried using a full path, that didn't work either.
You may have noticed that this function is outside the WndProc. Everything else draws fine.
I tried running it in there too everything but the arrow.bmp loads.
What am I doing wrong to cause arrow.bmp to be NULL? I have other methods I plan to run in similar ways so getting this function to run would really be a big help.
EDIT* Whenever I give the full path name it still fails to load.
Also is this not the appropriate code for SO? This is my first question...
EDIT** The aditional '/'s haven't fixed the issue.
EDIT*** Using GetLastError, I found the error code to be 2, ERROR_FILE_NOT_FOUND
Your early versions of the question checked the return value of LoadImage but then did nothing more. The documentation says this:
If the function fails, the return value is NULL. To get extended error information, call GetLastError.
So, if the function fails, call GetLastError to find out why. When you do so, you obtain the error code ERROR_FILE_NOT_FOUND. Which is pretty conclusive. The filename that you specified does not exist.
Do note that the code in the latest update to the question calls GetLastError unconditionally. That is a mistake, one that is seen all too frequently here on Stack Overflow. The documentation only tells you to call GetLastError when the call to LoadImage fails. If the call to LoadImage succeeds, then the value return by GetLastError is meaningless. Error handling in Win32 is largely handled in the same way as LoadImage does it, but not always. So you have to read the documentation very carefully.
Perhaps instead of
C:\\Users\\Tim\documents\\...
you meant
C:\\Users\\Tim\\documents\\...
OK, now you've got the path right. The call to LoadImage returns NULL, but GetLastError is no longer helpful and returns ERROR_SUCCESS. Which is weird in itself.
I believe that the problem is that your image uses a format that LoadImage does not understand. I took your .bmp file, loaded it in Paint.net and then re-saved it. Once I did that, the re-saved image was loaded successfully.
Rather than trying to load this from a file it would make far more sense to link the image as a resource and load it that way.
I found through experimenting with Gimp, that your .bmp file must have the following attributes in order for it to work:
depth:24bit
"Do not show write color space information" box checked: Gimp export options
If that still doesn't work then try to "unblock" it: File options.
Please help! I have tried loading image to image list via icon, hbitmap and cbitmap (i am using mfc dialog based application). But the images just wont show up. But I managed to view the image when i recreate it on an empty sdi mfc application.
m_TreeInspCtrl.DeleteAllItems();
CImageList imgl_Tree;
imgl_Tree.Create(16, 16, ILC_COLOR32, 1, 1);
/*
HBITMAP hBmp = (HBITMAP)::LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_TREE_PASS), IMAGE_BITMAP, 0, 0, LR_LOADMAP3DCOLORS);
*/
imgl_Tree.Add(AfxGetApp()->LoadIcon(IDI_ICON_PASS));
/*
CBitmap m_TreePass;
//m_TreePass.Attach(hBmp);
m_TreePass.LoadBitmap(IDB_TREE_PASS);
imgl_Tree.Add(&m_TreePass, RGB(255,0,255));
*/
m_TreeInspCtrl.SetImageList (&imgl_Tree, TVSIL_NORMAL);
CString s_Root = "Inspection Sequence";
HTREEITEM h_Root = m_TreeInspCtrl.InsertItem(s_Root, 0, 0, TVI_ROOT);
m_TreeInspCtrl.SetItemColor(h_Root, RGB(0, 150, 0));
You have to create a CImageList that is valid throughout the existence of the dialog. The one that you created in your code is only temporary on the stack, it will be destroyed when the initialization function returns. I suggest that you create it as a member variable of the dialog class.