How to create combobox with bitmaps in Win32?
I wanted to create a custom combo box like this (as in MS Word),
Are there any Win32 API calls (I can't use MFC) to get this job done, (like ChooseColor() or ChooseFont()? If there aren't any, can anyone please tell me how to do this? Tha开发者_如何学运维nks you.
Regards,
EDIT: Creating the Owner-Drawn Dialog Box!! Is this the only way ? http://msdn.microsoft.com/en-us/library/bb775794%28VS.85%29.aspx#creating_square_meal
You have some options to solve your problem:
- All common controls supports
WM_SETFONT
, so if you find a Font which has all line elements which you need, you can change font of the combobox control and fill items with corresponding textes. - There are
ComboBoxEx
control which combines images with textes (see http://msdn.microsoft.com/en-us/library/bb775738(VS.85).aspx). Be careful, what part of items will be selected (just try it). If you can a little change your requirements to the combobox control you will be able to use this. - You can use owner-draw combo-box. Then you are absolutely free, but your code can be a liitle longer and you should be more carful if you works with non-standard color shema of windows or a non-default theams. I'll recommend you use functions
GetSysColor
in this case.
You should deceide youself whay way is the best for your project requirements.
You can derive from WTL::CComboBox. You'd have to implement the message handlers for
WM_MEASUREITEM / OCM_MEASUREITEM
to do the size measurements of your combo box itemsWM_DRAWITEM / OCM_DRAWITEM
to do the drawing itself. You don't need bitmaps really, you could simply draw using GDI.
In Win32 this is called a owner-drawn combobox. A good startpoint in the online dicumentation is here:
http://msdn.microsoft.com/en-us/library/bb775794%28VS.85%29.aspx#creating_owner_drawn
This is working code of an owner drawn combo-box, instructions within.
// List image Icon Test.cpp : Defines the entry point for the application.
//
// READ ME:
// Create a new c++ desktop application
// Remove all the code from the cpp section and insert all this code.
// Create a dialog in VS editor, which had a list box called IDLIST and a combobox called IDCOMBO
// it will already have ok and cancel buttons these can stay.
// Create 4 bitmaps any size, only 24x24 will be shown and put them in the source project directory,
// eg C:\Users\Pts1\source\repos\List image Icon Test\List image Icon Test
// Rename the bitmaps Black.bmp, Blue.bmp, Red.bmp, Green.bmp and make sure they are of the format bmp not just called bmp.
// or ideally create the bitmaps so that they are 24x24 and all black,blue etc
// add IDB_Bread bitmap to the editor or remove them from the code below as instructed. We can load bitmaps directly from file instead.
// Make sure you change the combobox properties as instructed at the header for the dialog callback procedure.
//
#include "stdafx.h"
#include "List image Icon Test.h"
#include "strsafe.h"
#pragma comment(linker, "\"/manifestdependency:type='Win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#define MAX_LOADSTRING 100
#define ID_BREAD 0
#define ID_DAIRY 1
#define ID_FRUIT 2
#define ID_MEAT 3
#define CX_BITMAP 24
#define CY_BITMAP 24
HBITMAP hbmBread, hbmDairy, hbmMeat, hbmFruit, hbmMask, hbmIcon;
HINSTANCE hInst; // current instance
WCHAR szTitle[MAX_LOADSTRING]; // The title bar text
WCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
void InitGroupList(HWND hDlg);
void DeleteIconBitmaps(void);
BOOL LoadIconBitmaps(void);
void InitFoodList(HWND hDlg);
INT_PTR CALLBACK FoodDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: Place code here.
// Initialize global strings
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_LISTIMAGEICONTEST, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// Perform application initialization:
if (!InitInstance (hInstance, nCmdShow)) return FALSE;
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_LISTIMAGEICONTEST));
MSG msg;
// Main message loop:
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
//
// FUNCTION: MyRegisterClass()
//
// PURPOSE: Registers the window class.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_LISTIMAGEICONTEST));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_LISTIMAGEICONTEST);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
//
// FUNCTION: InitInstance(HINSTANCE, int)
//
// PURPOSE: Saves instance handle and creates main window
//
// COMMENTS:
//
// In this function, we save the instance handle in a global variable and
// create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // Store instance handle in our global variable
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
//
// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// PURPOSE: Processes messages for the main window.
//
// WM_COMMAND - process the application menu
// WM_PAINT - Paint the main window
// WM_DESTROY - post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_DIALOG1), hWnd, FoodDlgProc);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code that uses hdc here...
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// Message handler for Square Meal dialog box. Click on Help -> ABout Box to activate this dialog.
// You must set the combo box properties to these for this to work.
//
// !!!!! The combo box you create for this example uses the CBS_DROPDOWNLIST, CBS_OWNERDRAWFIXED, (optional)CBS_SORT, CBS_HASSTRINGS, WS_VSCROLL, and (optional) WS_TABSTOP styles. !!!!!!
//
INT_PTR CALLBACK FoodDlgProc(HWND hDlg, UINT message, WPARAM wParam,LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
TCHAR achTemp[256];
HWND hwnd;
switch (message)
{
case WM_INITDIALOG:
// Call an application-defined function to load bitmap resources.
if (!LoadIconBitmaps())
{
Beep(2000, 250);
EndDialog(hDlg, -1);
break;
}
// Initialize the food groups combo box and select the first item.
InitGroupList(hDlg);
SendDlgItemMessage(hDlg, IDCOMBO, CB_SETCURSEL, 0, 0);
// Initialize the food list box and select the first item.
InitFoodList(hDlg);
SendDlgItemMessage(hDlg, IDLIST, LB_SETCURSEL, 0, 0);
return (INT_PTR)TRUE;
case WM_MEASUREITEM:
{
// Set the height of the items in the food groups combo box.
LPMEASUREITEMSTRUCT lpmis = (LPMEASUREITEMSTRUCT)lParam;
if (lpmis->itemHeight < CY_BITMAP + 2) lpmis->itemHeight = CY_BITMAP + 2;
}
break;
case WM_DRAWITEM:
{
COLORREF clrBackground;
COLORREF clrForeground;
TEXTMETRIC tm;
int x;
int y;
HRESULT hr;
size_t cch;
LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT)lParam;
if (lpdis->itemID == -1) // Empty item)
break;
// Get the food icon from the item data.
hbmIcon = (HBITMAP)lpdis->itemData;
// The colors depend on whether the item is selected.
clrForeground = SetTextColor(lpdis->hDC,
GetSysColor(lpdis->itemState & ODS_SELECTED ?
COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT));
clrBackground = SetBkColor(lpdis->hDC,
GetSysColor(lpdis->itemState & ODS_SELECTED ?
COLOR_HIGHLIGHT : COLOR_WINDOW));
// Calculate the vertical and horizontal position.
GetTextMetrics(lpdis->hDC, &tm);
y = (lpdis->rcItem.bottom + lpdis->rcItem.top - tm.tmHeight) / 2;
x = LOWORD(GetDialogBaseUnits()) / 4;
// Get and display the text for the list item.
SendMessage(lpdis->hwndItem, CB_GETLBTEXT,
lpdis->itemID, (LPARAM)achTemp);
hr = StringCchLength(achTemp, 256, &cch);
if (FAILED(hr))
{
// TODO: Write error handler.
}
ExtTextOut(lpdis->hDC, CX_BITMAP + 2 * x, y,
ETO_CLIPPED | ETO_OPAQUE, &lpdis->rcItem,
achTemp, (UINT)cch, NULL);
// Restore the previous colors.
SetTextColor(lpdis->hDC, clrForeground);
SetBkColor(lpdis->hDC, clrBackground);
// Draw the food icon for the item.
HDC hdc = CreateCompatibleDC(lpdis->hDC);
if (hdc == NULL)
break;
SelectObject(hdc, hbmMask);
BitBlt(lpdis->hDC, x, lpdis->rcItem.top + 1,
CX_BITMAP, CY_BITMAP, hdc, 0, 0, SRCAND);
SelectObject(hdc, hbmIcon);
BitBlt(lpdis->hDC, x, lpdis->rcItem.top + 1,
CX_BITMAP, CY_BITMAP, hdc, 0, 0, SRCPAINT);
DeleteDC(hdc);
// If the item has the focus, draw the focus rectangle.
if (lpdis->itemState & ODS_FOCUS) DrawFocusRect(lpdis->hDC, &lpdis->rcItem);
}
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDCOMBO:
if (HIWORD(wParam) == CBN_SELENDOK)
{
InitFoodList(hDlg);
SendDlgItemMessage(hDlg, IDLIST,
LB_SETCURSEL, 0, 0);
}
break;
case IDLIST:
if (HIWORD(wParam) != LBN_DBLCLK)
break;
// For a double-click, process the OK case.
case IDOK:
// Get the text for the selected list item.
hwnd = GetDlgItem(hDlg, IDLIST);
// Here it is assumed the text can fit into achTemp.
// If there is doubt, call LB_GETTEXTLENGTH first.
SendMessage(hwnd, LB_GETTEXT,
SendMessage(hwnd, LB_GETCURSEL, 0, 0),
(LPARAM)achTemp);
// TODO: Do something with the selected text.
EndDialog(hDlg, 0);
break;
case IDCANCEL:
hwnd = GetDlgItem(hDlg, IDCOMBO);
if (SendMessage(hwnd, CB_GETDROPPEDSTATE, 0, 0))
SendMessage(hwnd, CB_SHOWDROPDOWN, FALSE, 0);
else EndDialog(hDlg, 0);
}
break;
case WM_DESTROY:
// Call the application-defined function to free the bitmap resources.
DeleteIconBitmaps();
break;
}
return (INT_PTR)FALSE;
}
// Loads string resources and adds them as items to the drop-down list of
// the food groups combo box. The bitmap handle of each item's icon is
// stored as item data for easy access when the item needs to be drawn.
//
void InitGroupList(HWND hDlg)
{
TCHAR achTemp[256];
DWORD dwIndex;
// Get the handle of the food groups combo box.
HWND hwndGroupsBox = GetDlgItem(hDlg, IDCOMBO);
//LoadString(hInst, IDS_BREAD, achTemp, sizeof(achTemp) / sizeof(TCHAR)); //originals were strings in editor
////dwIndex = SendMessage(hwndGroupsBox, CB_ADDSTRING, 0, (LPARAM)achTemp);
dwIndex = SendMessage(hwndGroupsBox, CB_ADDSTRING, 0, (LPARAM)_T("Bread"));
SendMessage(hwndGroupsBox, CB_SETITEMDATA, dwIndex, (LPARAM)hbm1);
//LoadString(hInst, IDS_DAIRY, achTemp, sizeof(achTemp) / sizeof(TCHAR));
dwIndex = SendMessage(hwndGroupsBox, CB_ADDSTRING, 0, (LPARAM)_T("Dairy"));
SendMessage(hwndGroupsBox, CB_SETITEMDATA, dwIndex, (LPARAM)hbm2);
//LoadString(hInst, IDS_FRUIT, achTemp, sizeof(achTemp) / sizeof(TCHAR));
dwIndex = SendMessage(hwndGroupsBox, CB_ADDSTRING, 0, (LPARAM)_T("Meat"));
SendMessage(hwndGroupsBox, CB_SETITEMDATA, dwIndex, (LPARAM)hbm3);
//LoadString(hInst, IDS_MEAT, achTemp, sizeof(achTemp) / sizeof(TCHAR));
dwIndex = SendMessage(hwndGroupsBox, CB_ADDSTRING, 0, (LPARAM)_T("Fruit"));
SendMessage(hwndGroupsBox, CB_SETITEMDATA, dwIndex, (LPARAM)hbm4);
return;
}
// Fills the food list based on the selected item in the food groups combo box.
void InitFoodList(HWND hDlg)
{
TCHAR achTemp[256];
HWND hwndGroupsBox = GetDlgItem(hDlg, IDCOMBO);
HWND hwndFoodList = GetDlgItem(hDlg, IDLIST);
// Clear the list contents.
SendMessage(hwndFoodList, LB_RESETCONTENT, 0, 0);
// Find out which food group is selected.
int idFoodGroup = SendMessage(hwndGroupsBox, CB_GETCURSEL, 0, 0);
switch (idFoodGroup)
{
case ID_BREAD:
LoadString(hInst, IDS_OATS, achTemp, sizeof(achTemp) / sizeof(TCHAR));
SendMessage(hwndFoodList, LB_ADDSTRING, 0, (LPARAM)achTemp);
LoadString(hInst, IDS_WHEAT, achTemp, sizeof(achTemp) / sizeof(TCHAR));
SendMessage(hwndFoodList, LB_ADDSTRING, 0, (LPARAM)achTemp);
LoadString(hInst, IDS_RYE, achTemp, sizeof(achTemp) / sizeof(TCHAR));
SendMessage(hwndFoodList, LB_ADDSTRING, 0, (LPARAM)achTemp);
break;
case ID_DAIRY:
LoadString(hInst, IDS_BREAD, achTemp, sizeof(achTemp) / sizeof(TCHAR));
SendMessage(hwndFoodList, LB_ADDSTRING, 0, (LPARAM)achTemp);
LoadString(hInst, IDS_BREAD, achTemp, sizeof(achTemp) / sizeof(TCHAR));
SendMessage(hwndFoodList, LB_ADDSTRING, 0, (LPARAM)achTemp);
LoadString(hInst, IDS_BREAD, achTemp, sizeof(achTemp) / sizeof(TCHAR));
SendMessage(hwndFoodList, LB_ADDSTRING, 0, (LPARAM)achTemp);
LoadString(hInst, IDS_BREAD, achTemp, sizeof(achTemp) / sizeof(TCHAR));
SendMessage(hwndFoodList, LB_ADDSTRING, 0, (LPARAM)achTemp);
break;
case ID_FRUIT:
LoadString(hInst, IDS_BREAD, achTemp, sizeof(achTemp) / sizeof(TCHAR));
SendMessage(hwndFoodList, LB_ADDSTRING, 0, (LPARAM)achTemp);
LoadString(hInst, IDS_BREAD, achTemp, sizeof(achTemp) / sizeof(TCHAR));
SendMessage(hwndFoodList, LB_ADDSTRING, 0, (LPARAM)achTemp);
LoadString(hInst, IDS_BREAD, achTemp, sizeof(achTemp) / sizeof(TCHAR));
SendMessage(hwndFoodList, LB_ADDSTRING, 0, (LPARAM)achTemp);
break;
case ID_MEAT:
LoadString(hInst, IDS_BREAD, achTemp, sizeof(achTemp) / sizeof(TCHAR));
SendMessage(hwndFoodList, LB_ADDSTRING, 0, (LPARAM)achTemp);
LoadString(hInst, IDS_BREAD, achTemp, sizeof(achTemp) / sizeof(TCHAR));
SendMessage(hwndFoodList, LB_ADDSTRING, 0, (LPARAM)achTemp);
LoadString(hInst, IDS_BREAD, achTemp, sizeof(achTemp) / sizeof(TCHAR));
SendMessage(hwndFoodList, LB_ADDSTRING, 0, (LPARAM)achTemp);
break;
default:
break;
}
return;
}
// Loads the food icon bitmaps from the application resources.
//
BOOL LoadIconBitmaps(void)
{
hbmBread = (HBITMAP)LoadImageW(hInst, (wchar_t*) L"Blue.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); //File has to be of format .bmp
if (hbmBread != NULL) hbmDairy = (HBITMAP)LoadImageW(hInst, (wchar_t*) L"Green.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); //File has to be of format .bmp
if (hbmDairy != NULL) hbmMeat = (HBITMAP)LoadImageW(hInst, (wchar_t*)L"Red.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); //File has to be of format .bmp
if (hbmMeat != NULL) hbmFruit = (HBITMAP)LoadImageW(hInst, (wchar_t*)L"Black.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); //File has to be of format .bmp
if (hbmFruit != NULL) hbmMask = (HBITMAP)LoadImageW(hInst, (wchar_t*)L"Black.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); //Needs to be a black mask to show other images.
/*
// original code which still works, you need to put in bitmaps into the editor itself. eg IDB_Bread, IDB_DAIRY, are 48x48 bitmaps in my editor by default.
hbmBread = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BREAD));
if (hbmBread != NULL) hbmDairy = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_DAIRY));
if (hbmDairy != NULL) hbmMeat = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_Green));
*/
//if (hbmMeat != NULL) hbmFruit = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BREAD)); //Bitmap defined in the editor(resource.h) which calls a Black.bmp file. See VS Bitmap editor.
//if (hbmFruit != NULL) hbmMask = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BREAD));
if (hbmMask != NULL)
return TRUE;
return FALSE;
}
// Frees the icon bitmps.
//
void DeleteIconBitmaps(void)
{
FreeResource(reinterpret_cast<HGLOBAL>(hbmBread));
FreeResource(reinterpret_cast<HGLOBAL>(hbmDairy));
FreeResource(reinterpret_cast<HGLOBAL>(hbmMeat));
FreeResource(reinterpret_cast<HGLOBAL>(hbmFruit));
FreeResource(reinterpret_cast<HGLOBAL>(hbmMask));
}
精彩评论