Dropdown listbox (win32)

Description

Petit exemple d'une solution trouvée suite à cette discussion sur le forum:
http://www.cppfrance.com/infomsg/WS_POPUP-FOCUS-0_730993.aspx
Le but est de créer une listbox dans une autre fenêtre, sans que le fenêtre mère ne perde le focus, en vue d'avoir le même comportement que les bouton annuler/refaire de Office ou Visual Studio
Il y a une fenêtre edit qui permet d'entrer le nombre d'items qui seront présent dans la listbox

Source / Exemple :


#define _WIN32_WINNT 0x0501
#define _WIN32_IE 0x0501
#include <windows.h>

HINSTANCE g_hInst;
HWND g_hWnd, g_hListbox;
char g_szAppName[] = "DropDown";
BOOL g_bCapture = FALSE;

WNDPROC defListboxProc;

#define MAX_ITEMS  10

int __fastcall bnatoi(const char* psz)
{
  __asm
  {
    xor   eax, eax
    push  ebx
    xor   edx, edx
    xor   ebx, ebx
    cmp   byte ptr[ecx], '-'
    jne   short L1
    inc   ecx
    mov   edx, 0xFFFFFFFF
L1:
    mov   bl, byte ptr[ecx]
    cmp   bl, '0'
    jb    short L2
    cmp   bl, '9'
    ja    short L2
    lea   eax, dword ptr[eax + 4 * eax]
    sub   bl, '0'
    add   eax, eax
    inc   ecx
    add   eax, ebx
    jmp   short L1
L2:
    add   eax, edx
    pop   ebx
    xor   eax, edx
  }
}

LRESULT ListBox_OnLButtonDown(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
  POINT pt = {LOWORD(lParam), HIWORD(lParam)};
  RECT clientRect, screenRect;
  POINT screenMousePos;
  GetCursorPos(&screenMousePos);
  GetWindowRect(hWnd, &screenRect);
  GetClientRect(hWnd, &clientRect);
  LRESULT l = 0;
  if(PtInRect(&screenRect, screenMousePos))
  {
    if(pt.x > clientRect.right)
    {
      // Clic dans la fenêtre, mais en dehors de la zone client
      // Donc sur la scollbar
      g_bCapture = TRUE; // Car la ListBox va recevoir WM_CAPTURECHANGED, mais il ne faut pas la fermer
      // On enlève temporairement la capture
      ReleaseCapture();
      l = CallWindowProc((WNDPROC)GetWindowLongPtr(hWnd, GWL_WNDPROC),
        hWnd,
        WM_NCLBUTTONDOWN,
        HTVSCROLL,
        MAKELPARAM(screenMousePos.x, screenMousePos.y));
      SetCapture(hWnd);
      g_bCapture = FALSE;
    }
    return l;
  }
  else DestroyWindow(hWnd); // Clic à coté de la fenêtre
  return 0;
}

LRESULT ListBox_OnMouseMove(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
  int sel, count;
  POINT pt;
  RECT rect;
  pt.x = (short)LOWORD(lParam);
  pt.y = (short)HIWORD(lParam);
  GetClientRect(hWnd, &rect);
  if(PtInRect(&rect, pt) || (wParam & MK_LBUTTON))
  {
    // On prend les mouvements de la souris quand on est au dessus de la listBox,
    // ou quand le bouton gauche de la souris est enfoncé
    if(pt.y < 0)
      PostMessage(hWnd, WM_VSCROLL, SB_LINEUP, 0);
    if(pt.y > rect.bottom)
      PostMessage(hWnd, WM_VSCROLL, SB_LINEDOWN, 0);
    sel = LOWORD(SendMessage(hWnd, LB_ITEMFROMPOINT, 0, lParam));
    SendMessage(hWnd, LB_SELITEMRANGE, TRUE, MAKELPARAM(0, sel));
    count = (int)SendMessage(hWnd, LB_GETCOUNT, 0, 0);
    if(sel != count - 1)
      SendMessage(hWnd, LB_SELITEMRANGE, FALSE, MAKELPARAM(sel + 1, count - 1));
  }
  return 0;
}

LRESULT CALLBACK ListboxProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  int sel;
  switch(uMsg)
  {
  case WM_MOUSEMOVE:
    return ListBox_OnMouseMove(hWnd, wParam, lParam);
  case WM_LBUTTONDOWN:
    return ListBox_OnLButtonDown(hWnd, wParam, lParam);
  case WM_KEYDOWN:
    switch(wParam)
    {
    case VK_DOWN:
      sel = (int)SendMessage(hWnd, LB_GETSELCOUNT, 0, 0);
      SendMessage(hWnd, LB_SETSEL, 1, sel);
      break;
    case VK_UP:
      sel = (int)SendMessage(hWnd, LB_GETSELCOUNT, 0, 0);
      SendMessage(hWnd, LB_SETSEL, 1, sel - 1);
      SendMessage(hWnd, LB_SETSEL, 0, sel - 1);
      break;
    case VK_RETURN:
      goto ok;
    case VK_ESCAPE:
      DestroyWindow(hWnd);
      break;
    }
    break;
  case WM_LBUTTONUP:
ok:
  case WM_CAPTURECHANGED:
    if(!g_bCapture)
    {
      DestroyWindow(hWnd);
      g_hListbox = 0;
    }
  }
  return CallWindowProc(defListboxProc, hWnd, uMsg, wParam, lParam);
}

LRESULT CALLBACK AppWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  static HWND hEdit;
  switch(uMsg)
  {
  case WM_KEYDOWN:
  case WM_MOUSEWHEEL:
    if(g_hListbox) PostMessage(g_hListbox, uMsg, wParam, lParam);
    break;
  case WM_CREATE:
    hEdit = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", 0, WS_VISIBLE | WS_CHILD, 0, 0, 200, 25,
      hWnd, (HMENU)1000, g_hInst, 0);
    CreateWindowEx(0, "Button", "Test", WS_VISIBLE | WS_CHILD, 200, 0, 100, 25,
      hWnd, (HMENU)1001, g_hInst, 0);
    break;
  case WM_COMMAND:
    if(LOWORD(wParam) == 1001)
    {
      char szitems[32];
      GetWindowText(hEdit, szitems, 32);
      int nItems = bnatoi(szitems);
      if(nItems < 1) return 0;

      POINT pt = {200, 25}; // Coordonnées dans la fenêtre mère
      // Comme on va mettre le bureau en fenêtre mère, il faut transformer les coordonnées
      MapWindowPoints(hWnd, HWND_DESKTOP, &pt, 1);
      g_hListbox = CreateWindowEx(WS_EX_TOOLWINDOW, "Listbox", 0, 
        WS_VSCROLL | WS_BORDER | LBS_HASSTRINGS | LBS_MULTIPLESEL |WS_VISIBLE | WS_CHILD,
        pt.x, pt.y, 100, 100, hWnd, 0, g_hInst, 0);
      int height = (int)SendMessage(g_hListbox, LB_GETITEMHEIGHT, 0, 0);
      if(nItems > MAX_ITEMS) height *= MAX_ITEMS;
      else height *= nItems;

      SetWindowPos(g_hListbox, 0, 0, 0, 100, height, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE);
      SetParent(g_hListbox, HWND_DESKTOP);
      for(int i = 0; i < nItems; i++)
        SendMessage(g_hListbox, LB_ADDSTRING, 0, (LPARAM)"Chaine d'exemple");

      // Donne le focus à la fenêtre principale, pour qu'elle puisse rediriger les
      // message vers la listbox (WM_KEYDOWN, WM_WOUSEWHEEL...)
      SetFocus(hWnd);
      SetCapture(g_hListbox);
      defListboxProc = (WNDPROC)SetWindowLongPtr(g_hListbox, GWL_WNDPROC, (LONG_PTR)ListboxProc);
    }
    break;
  case WM_DESTROY:
    PostQuitMessage(0);
    return 0;
  }
  return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

#ifdef _DEBUG
int main()
#else
#pragma comment(linker, "/entry:myWinMain")
int __stdcall myWinMain()
#endif
{
  MSG msg;
  g_hInst = GetModuleHandle(0);
  WNDCLASSEX wcex;

  memset(&wcex, 0, sizeof wcex);
  wcex.cbSize         = sizeof wcex;
  wcex.lpfnWndProc    = AppWndProc;
  wcex.style          = CS_HREDRAW | CS_VREDRAW;
  wcex.hInstance      = g_hInst;
  wcex.lpszClassName  = g_szAppName;
  wcex.hbrBackground  = GetSysColorBrush(COLOR_WINDOW);
  wcex.hCursor        = LoadCursor(0, IDC_ARROW);

  if(!RegisterClassEx(&wcex)) return 1;

  g_hWnd = CreateWindowEx(0, g_szAppName, g_szAppName, WS_OVERLAPPED | WS_VISIBLE | WS_SYSMENU,
    CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, g_hInst, 0);
  if(!g_hWnd) return 1;

  ShowWindow(g_hWnd, SW_NORMAL);
  while(GetMessage(&msg, NULL, 0, 0))
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
#ifdef _DEBUG
  return (int)msg.wParam;
#else
  ExitProcess(msg.wParam);
#endif
}

Codes Sources

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.