Radek Chalupa   konzultace a školení programování, vývoj software na zakázku

Uživatelsky kreslený ListBox.

29.1.2002

V tomto pokračování ještě zůstaneme u uživatelsky kreslených prvků. Vytvoříme si ListBox, do kterého budeme jako položky přidávat vybrané soubory. V ListBoxu bude pak zobrazena ikona asociovaná s příslušným souborem a jméno souboru vypsané tak, že v případě delšího názvu bude povolen víceřádkový výpis do zbylého prostoru položky ListBoxu, jak vidíte na obrázku:

win-api-uk-listbox

U uživatelsky kresleného ListBoxu (a podobně některých další ovládacích prvků) musíme kromě zprávy WM_DRAWITEM reagovat na zprávu WM_MEASUREITEM, ve které systému řekneme požadované rozměry (v případě ListBoxu výšku) položky. V našem případě budeme zobrazovat velké ikony, takže výšku položky nastavíme a výšku ikony plus řekněme 4 pixely. Tuto zprávu ošetříme přímo v proceduře dialogu, pro obsluhu zprávy WM_DRAWITEM si vytvoříme vlastní funkci, stejně jako pro zobrazení dialogu výběru souboru a přidání vybraného souboru jako položky ListBoxu. Procedura dialogu bude vypadat následovně:
// Procedura dialogu
INT_PTR CALLBACK DialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  LPMEASUREITEMSTRUCT lpMIS;
  switch ( uMsg )
  {
    case WM_COMMAND:
      switch ( LOWORD(wParam) )
      {
        case IDOK:
          EndDialog(hWnd, IDOK);
          break;
        case IDCANCEL:
          EndDialog(hWnd, IDCANCEL);
          break;
        case IDC_PRIDAT:
          PridatSoubor(hWnd);
          break;
      }
      break;
    case WM_INITDIALOG:
      break;
    case WM_DRAWITEM:
      DrawItem((LPDRAWITEMSTRUCT)lParam);
      break;
    case WM_MEASUREITEM:
      lpMIS = (LPMEASUREITEMSTRUCT)lParam;
      lpMIS->itemHeight = GetSystemMetrics(SM_CYICON) + 4;
      break;
  }
  return FALSE;
}
Funkce pro přidání vybraného souboru jako položky je již pro čtenáře seriálu jednoduchá, pro úplnost ji zde uvedu:
void PridatSoubor(HWND hWnd)
{
  TCHAR szSoubor[MAX_PATH];
  lstrcpy(szSoubor, _T(""));
  OPENFILENAME ofn;
  ZeroMemory(&ofn, sizeof(OPENFILENAME));
  ofn.lStructSize = sizeof(OPENFILENAME);
  ofn.hwndOwner = hWnd;
  ofn.lpstrFile = szSoubor;
  ofn.nMaxFile = sizeof(szSoubor);
  ofn.lpstrFilter = TEXT("Všechny soubory\0*.*\0");
  ofn.lpstrFileTitle = NULL;
  ofn.nMaxFileTitle = 0;
  ofn.lpstrInitialDir = NULL;
  ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
  if ( !GetOpenFileName(&ofn) ) 
    return;
  SendDlgItemMessage(hWnd, IDC_LISTBOX, LB_ADDSTRING, 0,
    (LPARAM)szSoubor);
}
Nyní zbývá to nejdůležitější, vykreslit položku ListBoxu při obdržení zprávy WM_DRAWITEM. Identifikátor dané položky dostaneme jako prvek itemID struktury DRAWITELSTRUCT. Z toho je již snadné získat text položky, což je plné jméno souboru. Handle asociované ikony získáme pomocí funkce ExtractAssociatedIcon. Dále zjistíme, zda se jedná o právě vybranou položku otestování přítomnosti hodnoty ODS_SELECTED v prvku itemState. Podle výsledku vyplníme plochu položky jedním z příslušných systémových štětců. Poté vykreslíme ikonu do levé části položky a do zbylého obdélníka vypíšeme název souboru funkcí DrawText, která umožňuje nastavit automatické zalamování řádek v rámci daného obdélníka.
void DrawItem(LPDRAWITEMSTRUCT lpDIS)
{
  TCHAR szSoubor[MAX_PATH];
  HBRUSH hbPozadi;
  SendMessage(lpDIS->hwndItem, LB_GETTEXT, lpDIS->itemID, (LPARAM)szSoubor);
    WORD index = 0;
  HICON hIcon = ExtractAssociatedIcon(g_hInstance, szSoubor, &index);
  if ( lpDIS->itemState & ODS_SELECTED )
  {
    hbPozadi = GetSysColorBrush(COLOR_HIGHLIGHT);
    SetTextColor(lpDIS->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
  }
  else
  {
    hbPozadi = GetSysColorBrush(COLOR_WINDOW);
    SetTextColor(lpDIS->hDC, GetSysColor(COLOR_WINDOWTEXT));
  }
  
  SetBkMode(lpDIS->hDC, TRANSPARENT);
  FillRect(lpDIS->hDC, &lpDIS->rcItem, hbPozadi);
  
  DrawIcon(lpDIS->hDC, lpDIS->rcItem.left + 1,
    lpDIS->rcItem.top + 2, hIcon);
  RECT rect;
  CopyMemory(&rect, &lpDIS->rcItem, sizeof(RECT));
  rect.left += 34;
  DrawText(lpDIS->hDC, szSoubor, -1, &rect,
    DT_WORDBREAK | DT_VCENTER);
}
Doprovodný projekt je ke stažení zde: win_api_uk_listbox.zip