Radek ChalupaČlánkyUkázky kóduTipy a trikyAktualityŠkolení a konzultaceVývoj softwareFreewareKontakt

Výběr složky a naplnění ListBoxu soubory.  31.1.2002

V tomto pokračování se dotkneme 2 témat. Využijeme ListBoxu z minulého dílu, abychom si ukázali, jak jej lze naplnit soubory ze zvolené složky. V té souvislosti se naučíme použít systémový dialog umožňující uživateli vybrat složku.

Dialog výběru složky

Nejprve si ukažme, jak vytvořit dialog pro výběr složky, aniž bychom museli použít běžný dialog pro výběr souboru (což by bylo poněkud křečovité, i když řešitelné), jehož ukázku vidíte na obrázku:

win-api-dir-listbox

Jádrem je použití funkce SHBrowseForFolder, která je deklarovaná v hlavičkovém souboru shlobj.h, který si tedy musíme vložit do zdrojového kódu. Pro použití této funkce musíme vytvořit vlastní callback funkci, která má parametry jako procedura okna a do které jsou posílány určité zprávy v době kdy je zobrazen dialog výběru složky. Zpráva BFFM_SELCHANGED přijde, pokud uživatel změní aktuálně vybranou složku. My pak máme možnost vypsat tuto složku do stavového textu (nad oknem se stromem složek). Při zachycení zprávy BFFM_INITIALIZED, která přijde jednou bezprostředně po inicializaci dialogu, zase můžeme nastavit požadovanou počáteční složku procházení. Pokud bychom chtěli, můžeme také pomocí zprávy BFFM_SETOKTEXT nastavit vlastní text tlačítku OK. Kód této funkce může tedy vypadat následovně:
// callback procedura pro funkci SHBrowseForFolder

TCHAR chStartBrowseDir[MAX_PATH];

int CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
{
  TCHAR szDir[MAX_PATH];
  switch ( uMsg )
  {
    case BFFM_INITIALIZED:
      if ( chStartBrowseDir != NULL )
        SendMessage(hwnd, BFFM_SETSELECTION, TRUE, (LPARAM)chStartBrowseDir);
      break;
    case BFFM_SELCHANGED:
      if ( SHGetPathFromIDList((LPITEMIDLIST)lParam, szDir))
        SendMessage(hwnd, BFFM_SETSTATUSTEXT, 0, (LPARAM)szDir);
      break;
  }
  return 0;
}
Nyní jak vypadá vlastní vyvolání dialogu. Vytvoříme si funkci, v jejích parametrech si budeme předávat požadovanou výchozí složku, okno vlastníka dialogu, vlastní titulek dialogu (který mám možnost změnit) a dále textový buffer, do kterého nám funkce naplní uživatelem vybranou složku. Návratová hodnota funkce nám bude říkat, zda uživatel složku skutečně vybral nebo zda ukončil dialog tlačítkem Storno. Celá funkce pak vypadá takto:
// Vyvolá dialog vybrání složky
BOOL VyberSlozku(LPTSTR lpVybranaSlozka, LPCTSTR lpStart, LPCTSTR lpNadpis, HWND hVlastnik)
{
  if ( lpStart != NULL )
    lstrcpy(chStartBrowseDir, lpStart);
  BOOL bResult = TRUE;
  LPITEMIDLIST pidl  = NULL;
  BROWSEINFO bi;
  ZeroMemory(&bi, sizeof(bi));
  LPMALLOC pMalloc = NULL;
  SHGetMalloc(&pMalloc);
  if ( IsWindow(hVlastnik) )
    bi.hwndOwner = hVlastnik;
  else
    bi.hwndOwner = NULL;
  bi.pszDisplayName = 0;
  bi.pidlRoot = 0;
  bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT;
  bi.lpfn = BrowseCallbackProc;
  if ( lpNadpis == NULL )
    bi.lpszTitle = _T("Vyberte požadovanou složku");
  else
    bi.lpszTitle = lpNadpis;
  pidl= SHBrowseForFolder(&bi);
  if ( pidl )
  {
    if ( !SHGetPathFromIDList(pidl, lpVybranaSlozka) )
      bResult = FALSE;
    pMalloc->Free(pidl);
  }
  else
    bResult = FALSE;
  pMalloc->Release();
  return bResult;
}

Naplnění ListBoxu obsahem složky

Nyní máme vybranou složku a chceme naplnit náš ListBox všemi soubory obsaženými ve vybrané složce. Vzhledem k tomu, že náš ListBox může být uživatelsky kreslený, musíme postupovat různým způsobem. Abychom měli funkci v run-timu univerzální, otestujeme si za běhu, zda je ListBox nastaven jako uživatelsky kreslený, a podle výsledku použijeme jednu nebo druhou variantu. Rozhodovací funkce bude testovat přítomnost jednoho ze stylu ListBoxu označujícího uživatelsky kreslený ListBox - LBS_OWNERDRAWFIXED a LBS_OWNERDRAWVARIABLE. Funkce bude vypadat takto:
void PridatSlozku(HWND hWnd)
{
  TCHAR szSlozka[MAX_PATH] = {0};
  if ( !VyberSlozku(szSlozka, szSlozka,  _T("Přidat složku do ListBoxu"), hWnd) )
    return;
  // Otestujeme, zda je uživatelsky kreslený
  if ( GetWindowLongPtr(GetDlgItem(hWnd, IDC_LISTBOX), GWL_STYLE)
    & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE) )
    PridejZeSlozky(hWnd, szSlozka);
  else
  {
    lstrcat(szSlozka, _T("\\*.*"));
    SendDlgItemMessage(hWnd, IDC_LISTBOX, LB_DIR, 0,
      (LPARAM)szSlozka);
  }
}
Jak je vidět z výpisu, pro naplnění ListBoxu soubory z dané složky slouží zpráva LB_DIR. Do ListBoxu jsou jako položky přidána pouze jména souborů bez cesty. V parametru lParam je šablona, ve které s využitím běžně známých takzvaných "divokých znaků" (* a ?) specifikujeme, které soubory mají být přidány. V parametr wParam pak můžeme uvést kombinaci jedné nebo více hodnot, specifikujících, jaké další objekty chceme přidat. Úplný seznam cca 8 hodnot naleznete v popisu této zprávy v dokumentaci, jako příklad uveďme:
  • DDL_DIRECTORY - zahrne také podsložky (jejich názvy jsou uvedeny v hranatých závorkách [] )
  • DDL_HIDDEN - zahrne do seznamu také skryté soubory
V případě našeho uživatelsky kresleného ListBoxu nemůžeme tuto zprávu použít, neboť potřebujeme při každém jednotlivém přidání souboru nastavit data položky. V příštím pokračování si ukážeme, jak prohledávat obsah složky a získat výčet souborů. Doprovodný projekt je ke stažení zde: win_api_dir_listbox.zip

Copyright © 2019 - Radek Chalupa