如何根据自绘组合框中输入的字母选择项目?

时间:2021-03-12 05:09:14

标签: winapi combobox windows-10 ownerdrawn

在常规组合框中,您可以跳转到以键入的字母开头的项目。例如,如果您有 "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, 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);
}

以下是自绘组合框的完整代码:

   #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);
}

更新 抱歉,我从所有者绘制组合框以外的其他文件中删除了代码示例。正如 @Song Zhu - MSFT 所指出的,将程序设置为正确但仍不处理 WM_CHAR。

1 个答案:

答案 0 :(得分:2)

下拉 COMBOBOX 没有 EDIT 控件。相反,它有一个下拉列表。

推荐使用GetComboBoxInfo来获取COMBOBOX控件的子窗口。

尝试将代码修改为:

COMBOBOXINFO ci{};
ci.cbSize = sizeof(COMBOBOXINFO);
GetComboBoxInfo(hwndCombo, &ci);
lpfnEditWndProc = (WNDPROC)SetWindowLongPtr(ci.hwndList, GWLP_WNDPROC, (LONG_PTR)SubClassProc);

当然,您可以使用此代码查看 hwndItemCOMBOBOXINFO 返回 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);
相关问题