如何在所有者-绘制组合框上根据输入的字母选择项目?



在常规组合框中,您可以跳转到以键入的字母开头的项目。例如,如果您有像"baa", "arch", "foo", "art"这样的项目,并键入";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, 0, sizeof(buffer));
buffer[0] = wParam;
prevIndex = SendMessage(hwndCombo, CB_FINDSTRING, (WPARAM) prevIndex, (LPARAM) buffer);
SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) prevIndex, 0);
return 0;
}
break;
}
return CallWindowProc(lpfnEditWndProc, hwnd, msg, wParam, lParam);
}
下面是owner-draw combobox的完整代码:
#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, UINT msg, WPARAM wParam, 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, 0)) 
X(L"Red", RGB(255, 0, 0)) 
X(L"Blue", RGB(0, 0, 255)) 
X(L"Green", RGB(0, 128, 0)) 
X(L"Yellow", RGB(255, 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, 100, 270, 170, 0, 0, hInstance, 0);  

while (GetMessage(&msg, NULL, 0, 0)) {
DispatchMessage(&msg);
}

DeleteObject(hDefaultSystemFont);
freeBrushes();
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, 
WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CREATE:
{
hwndCombo = CreateWindow(L"Combobox", NULL, 
WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST |
CBS_OWNERDRAWFIXED | CBS_AUTOHSCROLL | WS_VSCROLL | WS_HSCROLL,
10, 10, 100, 110, hwnd, (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, GWLP_WNDPROC, (LONG_PTR)SubClassProc);
for (int i = 0; i < COUNTOF(items); ++i)
{
SendMessage(hwndCombo, CB_ADDSTRING, 0, (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, &b->rcItem, (HBRUSH) (COLOR_WINDOW+1));
SetTextColor(b->hDC, GetSysColor(COLOR_WINDOWTEXT+1));
}
if(b->itemID != -1)
{
drawColorRect(b->hDC, &b->rcItem, b->itemID);
DrawText(b->hDC, items[b->itemID], -1, &b->rcItem, 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, msg, wParam, 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, RECT *editRect, 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, -1);
DrawBorder(dc, &rt);
//FrameRect(dc, &rt, getBrushAt(0));
FillRect(dc, &rt, getBrushAt(colorIndex));
}
void DrawLine(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2)
{
MoveToEx(hdc, x1, y1, NULL);
LineTo(hdc, x2, y2);
}

void DrawBorder(HDC hdc, RECT *rect)
{
DrawLine(hdc, rect->left, rect->top, rect->left, rect->bottom);
DrawLine(hdc, rect->left, rect->top, rect->right, rect->top);
DrawLine(hdc, rect->right, rect->top, rect->right, rect->bottom);
DrawLine(hdc, rect->left, rect->bottom, rect->right, 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, UINT msg, WPARAM wParam, 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, CB_FINDSTRING, (WPARAM) prevIndex, (LPARAM) buffer);
SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) prevIndex, 0);
return 0;
}
break;
}
return CallWindowProc(lpfnEditWndProc, hwnd, msg, wParam, lParam);
}

UPDDATE对不起,我过去的代码样本从其他文件比所有者绘制组合框。正如@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);

当然,您可以使用这段代码看到COMBOBOXINFOhwndItem返回NULL。您可以根据组合框类型调整需要控制的句柄。

编辑:

您的消息循环代码和设置样式错误(需要CBS_HASSTRINGS),请参考:

while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
......
hwndCombo = CreateWindow(L"Combobox", NULL, 
WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST |
CBS_OWNERDRAWFIXED | CBS_AUTOHSCROLL | WS_VSCROLL | WS_HSCROLL | CBS_HASSTRINGS,
10, 10, 100, 110, hwnd, (HMENU) BTN_COMBO, g_hinst, NULL);

最新更新