问题描述
在常规组合框中,您可以跳转到以键入的字母开头的项目。例如,如果您有 "baa","arch","foo","art"
之类的项目并键入“a”,则项目“arch”被选中,您再次键入“a”然后它会跳转到“art”。我怎样才能在自绘组合框上实现这个?我以为我可以在 comoboBox 的子过程中像下面那样处理 WM_CHAR
,但是我什至无法测试它,因为像这样为 comoboBox 设置过程失败了:
HWND hwndEdit = Getwindow(hwndCombo,GW_CHILD);
assert(hwndEdit != NULL); // fails here
lpfnEditWndProc = (WNDPROC) SetwindowLongPtr(hwndEdit,GWLP_WNDPROC,(LONG_PTR) SubClassproc);
程序是这样的:
LRESULT CALLBACK SubClassproc(HWND hwnd,UINT msg,WParaM wParam,LParaM lParam)
{
switch (msg)
{
case WM_CHAR:
{
static int prevIndex = 0;
static wchar_t buffer[2] = {0};
memset(&buffer,sizeof(buffer));
buffer[0] = wParam;
prevIndex = SendMessage(hwndCombo,CB_FINDSTRING,(WParaM) prevIndex,(LParaM) buffer);
SendMessage(hwndCombo,CB_SETCURSEL,0);
return 0;
}
break;
}
return CallWindowProc(lpfnEditWndProc,hwnd,msg,wParam,lParam);
}
以下是自绘组合框的完整代码:
#pragma comment(lib,"user32.lib")
#pragma comment(lib,"Comctl32.lib")
#pragma comment(lib,"Gdi32.lib")
#pragma comment(lib,"UxTheme.lib")
#pragma comment(lib,"Comdlg32.lib")
#define WIN32_LEAN_AND_MEAN
#define UNICODE
#define _UNICODE
#include <windows.h>
#include <Commctrl.h>
#include <assert.h>
#include <uxtheme.h>
#include <Commdlg.h>
LRESULT CALLBACK WndProc(HWND,UINT,WParaM,LParaM);
void SetDefaultFont(HWND hwnd);
HFONT LoadSystemDefaultFont();
void DrawBorder(HDC hdc,RECT *rect);
void DrawLine(HDC hdc,LONG x1,LONG y1,LONG x2,LONG y2);
void freeBrushes();
HBrush getBrushAt(int index);
void drawColorRect(HDC dc,RECT *editRect,int colorIndex);
void EnableVisualStyles2(void);
LRESULT CALLBACK SubClassproc(HWND hwnd,LParaM lParam);
HINSTANCE g_hinst;
HFONT hDefaultSystemFont;
HWND hwndCombo;
#define COUNTOF(a) (sizeof(a)/sizeof(a[0]))
#define LIST \
X(L"Black",RGB(0,0)) \
X(L"Red",RGB(255,0)) \
X(L"Blue",255)) \
X(L"Green",128,0)) \
X(L"Yellow",255,0))
#define X(a,b) a,const wchar_t *items[] = { LIST };
#undef X
#define X(a,b) b,const int colorCodes[] = { LIST };
#undef X
HBrush brushes[COUNTOF(items)];
WNDPROC lpfnEditWndProc;
enum
{
BTN_A = 20,BTN_COMBO,};
int WINAPI wWinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,PWSTR lpCmdLine,int nCmdshow) {
HWND hwnd;
MSG msg;
WNDCLASSW wc = {0};
wc.lpszClassName = L"Application";
wc.hInstance = hInstance ;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc;
wc.hCursor = LoadCursor(0,IDC_ARROW);
g_hinst = hInstance;
RegisterClassW(&wc);
hwnd = CreateWindowW(wc.lpszClassName,L"Combo Box",WS_OVERLAPPEDWINDOW | WS_VISIBLE,100,270,170,hInstance,0);
while (GetMessage(&msg,NULL,0)) {
dispatchMessage(&msg);
}
DeleteObject(hDefaultSystemFont);
freeBrushes();
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd,LParaM lParam)
{
switch(msg)
{
case WM_CREATE:
{
hwndCombo = CreateWindow(L"ComboBox",WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST |
CBS_OWNERDRAWFIXED | CBS_AUTOHSCROLL | WS_VSCROLL | WS_HSCROLL,10,110,(HMENU) BTN_COMBO,g_hinst,NULL);
SendMessage(hwndCombo,CB_SETMINVISIBLE,5,0);
SetDefaultFont(hwndCombo);
COMBOBoxINFO ci = {0};
ci.cbSize = sizeof(COMBOBoxINFO);
GetComboBoxInfo(hwndCombo,&ci);
lpfnEditWndProc = (WNDPROC)SetwindowLongPtr(ci.hwndList,(LONG_PTR)SubClassproc);
for (int i = 0; i < COUNTOF(items); ++i)
{
SendMessage(hwndCombo,CB_ADDSTRING,(LParaM) items[i]);
}
}
break;
case WM_DRAWITEM:
{
LPDRAWITEMSTRUCT b = (LPDRAWITEMSTRUCT) lParam;
SetBkMode(b->hDC,TRANSPARENT);
if(b->itemState & ODS_SELECTED)
{
FillRect(b->hDC,&b->rcItem,(HBrush) (COLOR_HIGHLIGHT+1));
SetTextColor(b->hDC,GetSysColor(COLOR_HIGHLIGHTTEXT+1));
}
else
{
FillRect(b->hDC,(HBrush) (COLOR_WINDOW+1));
SetTextColor(b->hDC,GetSysColor(COLOR_WINDOWTEXT+1));
}
if(b->itemID != -1)
{
drawColorRect(b->hDC,b->itemID);
DrawText(b->hDC,items[b->itemID],-1,DT_CENTER | DT_VCENTER | DT_SINGLELINE);
}
if(b->itemAction & ODA_FOCUS)
{
DrawFocusRect(b->hDC,&b->rcItem);
}
return (INT_PTR) TRUE;
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd,lParam);
}
HFONT LoadSystemDefaultFont()
{
if(hDefaultSystemFont == NULL) {
NONCLIENTMETRICS ncm;
ncm.cbSize = sizeof(NONCLIENTMETRICS);
SystemParametersInfo(SPI_GETNONCLIENTMETRICS,sizeof(NONCLIENTMETRICS),&ncm,0);
hDefaultSystemFont = CreateFontIndirect(&ncm.lfMessageFont);
}
return hDefaultSystemFont;
}
void SetDefaultFont(HWND hwnd)
{
SendMessage(hwnd,WM_SETFONT,(LParaM) LoadSystemDefaultFont(),TRUE);
}
void drawColorRect(HDC dc,int colorIndex)
{
assert(colorIndex >= 0 && colorIndex < COUNTOF(brushes));
assert(editRect != NULL);
RECT rt = {0};
rt.left = editRect->left + 2;
rt.top = editRect->top - 2;
rt.right = editRect->right / 5;
rt.bottom = editRect->bottom - 2;
InflateRect(&rt,-1);
DrawBorder(dc,&rt);
//FrameRect(dc,&rt,getBrushAt(0));
FillRect(dc,getBrushAt(colorIndex));
}
void DrawLine(HDC hdc,LONG y2)
{
MovetoEx(hdc,x1,y1,NULL);
Lineto(hdc,x2,y2);
}
void DrawBorder(HDC hdc,RECT *rect)
{
DrawLine(hdc,rect->left,rect->top,rect->bottom);
DrawLine(hdc,rect->right,rect->top);
DrawLine(hdc,rect->bottom,rect->bottom);
}
HBrush getBrushAt(int index)
{
assert(index >= 0 && index < COUNTOF(brushes));
HBrush b = brushes[index];
if(b == NULL) {
int code = colorCodes[index];
brushes[index] = CreateSolidBrush(code);
b = brushes[index];
}
return b;
}
void freeBrushes()
{
for(int i = 0; i < COUNTOF(brushes); ++i){
DeleteObject(brushes[i]);
brushes[i] = NULL;
}
}
void EnableVisualStyles2(void)
{
TCHAR dir[MAX_PATH] = {0};
GetSystemDirectory(dir,sizeof(dir) / sizeof(*dir));
ACTCTX actCtx = {0};
actCtx.cbSize = sizeof(ACTCTX);
actCtx.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID |
ACTCTX_FLAG_SET_PROCESS_DEFAULT |
ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID;
actCtx.lpSource = L"shell32.dll";
actCtx.lpAssemblyDirectory = dir;
actCtx.lpResourceName = (LPCTSTR) 124;
ULONG_PTR cookie = FALSE;
HANDLE h = CreateActCtx(&actCtx);
assert(h != INVALID_HANDLE_VALUE);
ActivateActCtx(h,&cookie);
}
LRESULT CALLBACK SubClassproc(HWND hwnd,LParaM lParam)
{
switch (msg)
{
case WM_CHAR:
{
static int prevIndex = 0;
static wchar_t buffer[2] = {0};
buffer[0] = wParam;
MessageBox(NULL,buffer,L"",MB_OK);
prevIndex = SendMessage(hwndCombo,lParam);
}
更新 抱歉,我从所有者绘制组合框以外的其他文件中删除了代码示例。正如 @Song Zhu - MSFT 所指出的,将程序设置为正确但仍不处理 WM_CHAR。
解决方法
下拉 COMBOBOX 没有 EDIT 控件。相反,它有一个下拉列表。
推荐使用GetComboBoxInfo
来获取COMBOBOX控件的子窗口。
尝试将代码修改为:
COMBOBOXINFO ci{};
ci.cbSize = sizeof(COMBOBOXINFO);
GetComboBoxInfo(hwndCombo,&ci);
lpfnEditWndProc = (WNDPROC)SetWindowLongPtr(ci.hwndList,GWLP_WNDPROC,(LONG_PTR)SubClassProc);
当然,您可以使用此代码查看 hwndItem
的 COMBOBOXINFO
返回 NULL。您可以根据自己的组合框类型调整需要控制的句柄。
编辑:
您的消息循环代码和设置样式有误(需要CBS_HASSTRINGS
),请参考:
while (GetMessage(&msg,NULL,0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
......
hwndCombo = CreateWindow(L"Combobox",WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST |
CBS_OWNERDRAWFIXED | CBS_AUTOHSCROLL | WS_VSCROLL | WS_HSCROLL | CBS_HASSTRINGS,10,100,110,hwnd,(HMENU) BTN_COMBO,g_hinst,NULL);