Dropdown listbox (win32)

Soyez le premier à donner votre avis sur cette source.

Vue 8 265 fois - Téléchargée 350 fois

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

Ajouter un commentaire Commentaires
Messages postés
21042
Date d'inscription
jeudi 23 janvier 2003
Statut
Modérateur
Dernière intervention
21 août 2019
27
push ebx
met sa valeur sur la pile pour restituer en sortie de fonction.
Aller dur forum asmfr pour le reste, ça évitera de flooder cette source avec des questions sans rapport au sujet principal.
Messages postés
1787
Date d'inscription
lundi 22 novembre 2004
Statut
Membre
Dernière intervention
31 janvier 2009
2
Lol je veux bien mais bon je voulais juste savoir pourquoi ces registres et les réponses aux questions que j'ai posé dans les commentaires sur ta fonction lol :)

J'suis chiant je sais ;)
Messages postés
21042
Date d'inscription
jeudi 23 janvier 2003
Statut
Modérateur
Dernière intervention
21 août 2019
27
Allez, pour Joky (quelques cycles en moins):

__declspec(naked) int __fastcall bnatoi(char *szsrc)
{ // ECX = szsrc
__asm {
xor edx, edx
mov [esp-4], ebx
mov dl, [ecx]
xor eax, eax
xor ebx, ebx
cmp dl, '-'
jb short nbrEXIT
jne short noPOINT
inc ecx
dec ebx
nbrLOOP:
mov dl, [ecx]
noPOINT:
cmp dl, '0'
jb short nbrSTOP
cmp dl, '9'
ja short nbrSTOP
lea eax, [eax+eax*4]
sub dl, '0'
add eax, eax
inc ecx
add eax, edx
jmp short nbrLOOP
nbrSTOP:
add eax, ebx
xor eax, ebx
nbrEXIT:
mov ebx, [esp-4]
ret 0
}
}

lea peut faire addition, soustraction et multiplication en 1 passe.
donc: lea eax, [eax+eax*4]
fait: eax = eax * 5;
Messages postés
6535
Date d'inscription
lundi 16 décembre 2002
Statut
Modérateur
Dernière intervention
22 août 2010
7
WM_MOUSEMOVE et les autres messages de la souris sont effectivement recus automatiquement car la Listbox à la capture de la souris (je fais un SetCapture).
WM_KEYDOWN & WM_MOUSEWHEEL sont deux messages utiles qui ne sont pas envoyés à la ListBox car elle n'a pas le focus, et on ne peut pas le lui donner
Messages postés
6535
Date d'inscription
lundi 16 décembre 2002
Statut
Modérateur
Dernière intervention
22 août 2010
7
Mon dieu, voila ce qui arrive quand on commente de l'assembleur!
Je me doute que ecx contient le param, qu'est ce que ca pourrait être d'autre...
Je précise que c'est BruNews qui a écrit cette fonction, pas moi
Maintenant que tu sais ce que contient ecx, tu va peut être mieux comprendre
Afficher les 11 commentaires

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.