Vývoj software na zakázku

Školení a konzultace pro programátory

Softwarové produkty

Webové prezentace na zakázku

Kliknutím na vybranou nabídku získáte další informace


Jak v oblasti zakázkového vývoje software, tak i školení a konzultací kladu důraz na kvalitu a individuální přístup. Jako programátor pracuji od roku 1985 a od roku 2000 podnikám samostatně jako OSVČ. Jsem také autorem 3 knih o programování.

 

Nejnovější příspěvky
  • Rozšířený prvek ComboBoxEx
  • Pracujeme s ComboBoxem II.
  • Pracujeme s ComboBoxem I.
  • Jak na záznam zvuku?
  • Vyhledávání souborů – zjištění obsahu složky
  • Výběr složky a naplnění ListBoxu soubory
  • Uživatelsky kreslený ListBox II.
  • Uživatelsky kreslený ListBox
  • Uživatelsky kreslená tlačítka
  • Začínáme oživovat vzhled aplikace
  • Rozšířený prvek ComboBoxEx

    ! Toto je nové uvedení článku staršího data v původní podobě, takže některá uvedená fakta jsou poplatná době vzniku článku.

    V tomto článku si řekneme o rozšířeném prvku ComboBoxEx, který patří stejně jako ComboBox mezi standardní ovládací prvky Windows.

    win-api-combobox-ex

    Ovládací prvek ComboBoxEx má některé další rozšiřující vlastnosti a schopnosti oproti běžnému prvku ComboBox. Tato výhoda však současně vyžaduje od programátora „trochu více práce“ při jeho vytvoření a použití. Dejme se tedy do práce. Prvním krokem, který musíme učinit před vlastním vytvořením tohoto prvku je správná inicializace knihovny běžných ovládacích prvků, comctl32. Provedeme ji pomocí funkce InitCommonControlsEx s použitím hodnoty ICC_USEREX_CLASSES, jak vidíte v následujícím kódu, umístěném před vlastním vytvořením okna nebo dialogového okna obsahujícího ComboBoxEx.

      INITCOMMONCONTROLSEX icc;
      icc.dwSize = sizeof(INITCOMMONCONTROLSEX);
      icc.dwICC = ICC_WIN95_CLASSES  | ICC_USEREX_CLASSES;
      if ( !InitCommonControlsEx(&icc) )
        return -1;
    

    Dalším rozdílem je, že prvek ComboBoxEx nemůžeme definovat přímo ve skriptu prostředků, tedy nemůžeme jej vytvořit vizuálním editorem dialogu. Vytvoříme jej tedy v obsluze zprávy WM_INITDIALOG pomocí funkce CreateWindowEx. Tento prvek dále umožňuje jednoduchým způsobem (bez uživatelského kreslení, které samozřejmě není nijak složité, ale..) zobrazovat u položek obrázky. Tyto obrázky musí být obsaženy v prvku ImageList, což je sada jednotlivých obrázků stejné velikosti. Každé položce pak přiřadíme index obrázku z tohoto ImageListu, který má být zobrazován v levé části položky. ImageList nastavíme prvku ComboBoxEx pomocí zprávy CBEM_SETIMAGELIST. Vše uvidíte v dalším výpisu kódu, v kterém si také ukážeme, jak lze nastavit prvku ComboBoxEx vlastní kurzor a dále jiný kurzor editačnímu prvku, obsaženém v ComboBoxu. V té souvislosti si musíme říci, z čeho se vlastně rozšířený ComboBoxEx skládá. Vlastní okno prvku ComboBoxEx totiž v sobě obsahuje ještě (jako dětské okno) běžný prvek ComboBox, a ten jak jsme mohli poznat v minulých dílech obsahuje prvek Edit, představující editační pole v případě, že ComboBox má nastaven potřebný styl CBS_DROPDOWN). Kromě tohoto Editu je pak při rozbalení ComboBoxu dynamicky tvořen prvek ListBox. Položky do prvku ComboBoxEx přidáváme pomocí zprávy CBEM_SETIMAGELIST, jejímž parametrem je adresa struktury COMBOBOXEXITEM, obsahující vlastnosti položky. Nyní se tedy již podívejme na výpis obsluhy zprávy WM_INITDIALOG:

    BOOL NaInitDialog(HWND hWnd)
    {
      HWND hCombo = CreateWindowEx(0,
        WC_COMBOBOXEX,
        NULL,
        WS_BORDER | WS_VISIBLE | WS_CHILD | CBS_DROPDOWN,
        0, 0,  0, 100, // polohu a rozměry nastvíme později
        hWnd,
        (HMENU)IDC_COMBOEX, // nastavíme ID pro snadnější použití
        GetModuleHandle(NULL),
        NULL);
      g_hImageList = ImageList_LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(IDB_IMAGELIST),
        16, 3, 0x0000FF00);
      if ( !g_hImageList )
        return FALSE;
      SendMessage(hCombo, CBEM_SETIMAGELIST, 0, (LPARAM)g_hImageList);
      SetWindowPos(hCombo, NULL,10,32,250,120,SWP_NOACTIVATE | SWP_NOZORDER);
      COMBOBOXEXITEM cbei;
      // Naplníme ComboBox 4 položkami.
      ZeroMemory(&cbei, sizeof(cbei));
      cbei.mask = CBEIF_IMAGE | CBEIF_TEXT | CBEIF_SELECTEDIMAGE;
      cbei.pszText = _T("první položka");
      cbei.iImage = 0;
      cbei.iItem = 0;
      cbei.iSelectedImage = 0;
      SendMessage(hCombo, CBEM_INSERTITEM, 0, (LPARAM)&cbei);
      cbei.pszText = _T("druhá položka");
      cbei.iImage = 1;
      cbei.iItem = 1;
      cbei.iSelectedImage = 1;
      SendMessage(hCombo, CBEM_INSERTITEM, 0, (LPARAM)&cbei);
      cbei.mask = CBEIF_IMAGE | CBEIF_TEXT | CBEIF_SELECTEDIMAGE;
      cbei.pszText = _T("třetí položka");
      cbei.iImage = 2;
      cbei.iItem = 2;
      SendMessage(hCombo, CBEM_INSERTITEM, 0, (LPARAM)&cbei);
      cbei.mask = CBEIF_IMAGE | CBEIF_TEXT | CBEIF_SELECTEDIMAGE;
      cbei.pszText = _T("čtvrtá položka");
      cbei.iImage = 3;
      cbei.iItem = 3;
      cbei.iSelectedImage = 3;
      SendMessage(hCombo, CBEM_INSERTITEM, 0, (LPARAM)&cbei);
      // Nastavíme výchozí výběr
      SendMessage(hCombo, CB_SETCURSEL, 1, 0);
      
      // Zjistíme "dětský" ComboBox a prvek Edit
      HWND hChildCombo = (HWND)SendMessage(hCombo, CBEM_GETCOMBOCONTROL, 0,0);
      HWND hEdit = (HWND)SendMessage(hCombo, CBEM_GETEDITCONTROL, 0,0);
      // Nastavíme vlastní kurzory
      SetClassLongPtr(hChildCombo, GCLP_HCURSOR,
        (LONG_PTR)LoadCursor(GetModuleHandle(NULL), MAKEINTRESOURCE(IDC_CURSOR1)));
      SetClassLongPtr(hEdit, GCLP_HCURSOR,
        (LONG_PTR)LoadCursor(GetModuleHandle(NULL), MAKEINTRESOURCE(IDC_CURSOR2)));
      return TRUE;
    }
    

    Dále si ještě ukažme, jak v případě rozšířeného ComboBoxu reagovat na změnu výběru položky a následně získat a nějakým způsobem zobrazit některá data položky. Konkrétně budeme zobrazovat text vybrané položky do prvku Static a obrázek vybrané položky ve formě ikony v prvku Static typu STM_ICON – který v editoru dialogu vložíme jako Picture Control. V proceduře dialogu budeme reagovat na oznamovací zprávu CBN_SELCHANGE. Vypíšeme si pro úplnost kompletní kód procedury dialogu z doprovodného příkladu:

    // Procedura dialogu
    INT_PTR CALLBACK DialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
      switch ( uMsg )
      {
        case WM_COMMAND:
          switch ( LOWORD(wParam) )
          {
            case IDOK:
              EndDialog(hWnd, IDOK);
              break;
            case IDCANCEL:
              EndDialog(hWnd, IDCANCEL);
              break;
            case IDC_COMBOEX:
              if ( HIWORD(wParam) == CBN_SELCHANGE )
                NaSelChange(hWnd);
              break;
          }
          break;
          
        case WM_INITDIALOG:
          NaInitDialog(hWnd);
          NaSelChange(hWnd);
          break;
      }
      return FALSE;
    }
    

    Obsluha oznamovací zprávy CBN_SELCHANGE vypadá pak takto:

    void NaSelChange(HWND hWnd)
    {
      TCHAR szText[100];
      COMBOBOXEXITEM cbei;
      ZeroMemory(&cbei, sizeof(cbei));
      cbei.mask = CBEIF_IMAGE | CBEIF_TEXT;
      cbei.pszText = szText;
      cbei.cchTextMax = sizeof(szText);
      // index vybrané položky
      cbei.iItem = SendDlgItemMessage(hWnd, IDC_COMBOEX, CB_GETCURSEL, 0, 0);
      SendDlgItemMessage(hWnd, IDC_COMBOEX, CBEM_GETITEM,  0, (LPARAM)&cbei);
      SetDlgItemText(hWnd, IDC_COMBO_TEXT, szText);
      SendDlgItemMessage(hWnd, IDC_IMAGE, STM_SETIMAGE, IMAGE_ICON,
        (LPARAM)ImageList_GetIcon(g_hImageList, cbei.iImage, ILD_NORMAL));
    }
    

    Doprovodný projekt je ke stažení zde: win_api_comboboxex.zip