C++ Winapi 列表框使用 LB_GETSEL/LB_GETCURSEL 获取选定的项目

C++ Winapi Listbox getting selected Item using LB_GETSEL/LB_GETCURSEL

所以我开始了一个标准的 windows 项目并添加了一个包含一堆项目的列表框。如果我单击列表框的一个项目,我想接收列表框的文本或我之前设置的项目数据。因此,如果用户单击列表框,我会尝试通过多种方式获取此信息(从 Internet 我尝试适应我的问题),如第二个代码 window 所示。问题是,LB_GETSEL 和 LB_GETCURSEL return 都不是我可以正确使用的值。该值始终为 -1 或 LB_ERR,这显然不是一个有用的值。我正在使用 MessageBox 可视化我的 SendMessage 的 return 值。我的错误是什么?

标准window代码

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{


    LBTest = CreateWindowExW(WS_EX_CLIENTEDGE
        , L"LISTBOX", NULL
        , WS_CHILD | WS_VISIBLE | WS_VSCROLL 
        , 7, 7, 300, 600
        , hWnd, (HMENU)19, hInst, NULL);
    std::wstring str;
    for (int l = 0; l < 210; l++)
    {
        str = std::to_wstring(l);
        int pos = SendMessage(LBTest, LB_ADDSTRING, 0, (LPARAM)str.c_str());
        SendMessage(LBTest, LB_SETITEMDATA, pos, (LPARAM)l);

    }

    break;

标准window代码

case WM_COMMAND:
    {
        int wmId = LOWORD(wParam);
        // Menüauswahl bearbeiten:
        switch (wmId)
        {
        case IDM_ABOUT:
            DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
            break;
        case IDM_EXIT:
            DestroyWindow(hWnd);
            break;
        case 19:
        {


            int count = SendMessage(LBTest, LB_GETCOUNT, 0, 0);

            int iSelected = -1;

            // go through the items and find the first selected one
            for (int i = 0; i < count; i++)
            {
                // check if this item is selected or not..
                if (SendMessage(LBTest, LB_GETSEL, i, 0) != LB_ERR)
                {
                    // yes, we only want the first selected so break.
                    iSelected = i;
                    break;
                }

            }
            std::wstring check = std::to_wstring(iSelected);
            MessageBox(hWnd, check.c_str(), L"wasd", MB_OK);

            //---------------------------------
            /*

            DWORD dwSel = SendMessage(hWnd, LB_GETCURSEL, 0, 0);

            if (dwSel != LB_ERR)
            {
                std::wstring str;
            SendMessage( LBTest, LB_GETTEXT, dwSel, (LPARAM)str.c_str());
            MessageBox(hWnd, L"Error", L"test", MB_OK);
            }

            //--------------------------------------------------------


            //int lbItem = (int)SendMessage(LBTest, LB_GETCURSEL, 0, 0);
            //int i = (int)SendMessage(LBTest, LB_GETITEMDATA, lbItem, 0);
            */
            break;
        }
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
    }
    break;

其余 window 代码

编辑: 由于评论区的建议,我尝试更改代码,但程序仍然无法运行。该建议包括 LBN_SELCHANGE 作为我的命令回调的条件,但我不确定是否可以在 MainWindow 回调中进行此回调或需要其自己的回调。 编辑完整程序代码:

#include "stdafx.h"
#include "Testproject.h"
#include <iostream>
#include <string>
#include <thread>

#define MAX_LOADSTRING 100
const UINT WM_WINDOW_UPDATE = WM_APP + 0;

HINSTANCE hInst;                               
WCHAR szTitle[MAX_LOADSTRING];                
WCHAR szWindowClass[MAX_LOADSTRING];            
HWND Button1,LBTest;




ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);




int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                 _In_opt_ HINSTANCE hPrevInstance,
                 _In_ LPWSTR    lpCmdLine,
                 _In_ int       nCmdShow)
{
 UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);




LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_TESTPROJECT, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);


if (!InitInstance (hInstance, nCmdShow))
{
    return FALSE;
}

HACCEL hAccelTable = LoadAccelerators(hInstance,    MAKEINTRESOURCE(IDC_TESTPROJECT));

MSG msg;


while (GetMessage(&msg, nullptr, 0, 0))
{
    if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}

return (int) msg.wParam;
}



//

//
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_TESTPROJECT));
wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_TESTPROJECT);
wcex.lpszClassName  = szWindowClass;
wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

return RegisterClassExW(&wcex);
}

//

//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; //

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

//

//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{


    LBTest = CreateWindowExW(WS_EX_CLIENTEDGE
        , L"LISTBOX", NULL
        , WS_CHILD | WS_VISIBLE | WS_VSCROLL 
        , 7, 7, 300, 600
        , hWnd, (HMENU)19, hInst, NULL);
    std::wstring str;
    for (int l = 0; l < 210; l++)
    {
        str = std::to_wstring(l);
        int pos = SendMessage(LBTest, LB_ADDSTRING, 0, (LPARAM)str.c_str());
        SendMessage(LBTest, LB_SETITEMDATA, pos, (LPARAM)l);

    }

    break;
}
case WM_COMMAND:
    {
        int wmId = LOWORD(wParam);

        switch (wmId)
        {
        case IDM_ABOUT:
            DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
            break;
        case IDM_EXIT:
            DestroyWindow(hWnd);
            break;
            case LBN_SELCHANGE:
            {
                /*
                int count = SendMessage(LBTest, LB_GETCOUNT, 0, 0);
                int iSelected = -1;
                // go through the items and find the first selected one
                for (int i = 0; i < count; i++)
                {
                    // check if this item is selected or not..
                    if (SendMessage(LBTest, LB_GETSEL, i, 0) != LB_ERR)
                    {
                        // yes, we only want the first selected so break.
                        iSelected = i;
                        break;
                    }

                }
                std::wstring check = std::to_wstring(iSelected);
                MessageBox(hWnd, check.c_str(), L"wasd", MB_OK);
                */
                //---------------------------------


                DWORD dwSel = SendMessage(hWnd, LB_GETCURSEL, 0, 0);

                if (dwSel > 0)
                {
                    std::wstring str;
                    SendMessage(LBTest, LB_GETTEXT, dwSel,     (LPARAM)str.c_str());
                    MessageBox(hWnd, str.c_str(), L"test", MB_OK);
                }
                MessageBox(hWnd, L"Check", L"test", MB_OK);
                /*


                //int lbItem = (int)SendMessage(LBTest, LB_GETCURSEL, 0, 0);
                //int i = (int)SendMessage(LBTest, LB_GETITEMDATA, lbItem, 0);
                */
                break;
            }   
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
    }
    break;
case WM_WINDOW_UPDATE:
{
    g_i = static_cast<int>(wParam);
    InvalidateRect(hWnd, nullptr, TRUE);
    break;
}
case WM_PAINT:
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hWnd, &ps);
        //TODO: Zeichencode, der hdc verwendet, hier einfügen...



        EndPaint(hWnd, &ps);
    }
    break;
case WM_DESTROY:
    PostQuitMessage(0);
    break;
default:
    return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}

// Meldungshandler für Infofeld.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
    return (INT_PTR)TRUE;

case WM_COMMAND:
    if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
    {
        EndDialog(hDlg, LOWORD(wParam));
        return (INT_PTR)TRUE;
    }
    break;
}
return (INT_PTR)FALSE;
}

好的,我找到了这个问题的答案。首先,我必须了解从列表框输入的数据是如何工作的。如果我与列表框交互,则会发送 WM_Command 消息。此消息包含一个 wparam,它只是一个无符号整数,在 windows 应用程序中有 4 个字节(如果我错了请纠正我)。这 4 个字节可以拆分为 2 个消息,LOWORD 和 HIWORD,每个消息包含 2 个字节。因此,如果我与列表框交互,我会收到来自 Windows 的 WM_Command 消息,告诉我有人与我的 window 的对象交互,并且 wparam 中有 2 个信息。第一个是 LOWORD,其中包含我与之交互的实际对象。在此示例中,它是我在 CreateWindowEx 中分配给列表框的 (HMENU)19。但是 HIWORD 仍然包含有关列表框已完成的操作的信息。有几个选项,包括文本框是否聚焦以及 selected 项目是否发生变化。事实证明,无论我在该列表框中单击什么,都没有关于传入的 select 更改的信息。所以我发现,对于进入消息循环的消息,您需要在列表框的 CreateWindowEx 中指定该消息从 Windows 发送到您的程序。您可以在 dwStyle 属性中使用 LBS_NOTIFY 来做到这一点。在我的例子中,固定的 CreateWindowEx 看起来像这样:

    LBTest = CreateWindowEx(WS_EX_CLIENTEDGE
        , L"LISTBOX",NULL
        , WS_CHILD | WS_VISIBLE | WS_VSCROLL| LBS_NOTIFY
        , 7, 7, 300, 600
        , hWnd, (HMENU)19, hInst, NULL);

修复后,我只需修复消息处理程序,使其看起来像这样:

case WM_COMMAND:
    {
        int wmId = LOWORD(wParam);

        switch (wmId)
        {
        case IDM_ABOUT:
            DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
            break;
        case IDM_EXIT:
            DestroyWindow(hWnd);
            break;
            case 19:
            {
                switch (HIWORD(wParam))
                {
                case LBN_SELCHANGE:
                {
                    int count = SendMessage(LBTest, LB_GETCOUNT, 0, 0);
                    int iSelected = -1;
                    // go through the items and find the first selected one
                    for (int i = 0; i < count; i++)
                    {
                        // check if this item is selected or not..
                        if (SendMessage(LBTest, LB_GETSEL, i, 0) > 0)
                        {
                            // yes, we only want the first selected so break.
                            iSelected = i;
                            break;
                        }

                    }
                    break;
                }
                }
                break;
            }   
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
    }
    break;

iSelected 就是您的列表框的 selected 项目编号。您可以将其发送到 LB_GETTEXT 以获取 selected 项目的当前文本:

std::wstring check ;
SendMessage(LBTest, LB_GETTEXT, iSelected, (LPARAM)check.c_str());

你也可以通过这样做得到相同的结果:

                    std::wstring str;

                    DWORD dwSel = SendMessage(LBTest, LB_GETCURSEL, 0, 0);


                    if (dwSel > 0)
                    {
                    std::wstring str;
                    SendMessage(LBTest, LB_GETTEXT, dwSel, (LPARAM)str.c_str());
                    MessageBox(hWnd, str.c_str(), L"test", MB_OK);
                    }

这可能是比第一个解决方案更快的解决方案,因为您不必遍历所有列表框项目。