Učíme se Win API - uživatelsky kreslený ListBox II - Builder.cz - Informacni server o programovani

Odběr fotomagazínu

Fotografický magazín "iZIN IDIF" každý týden ve Vašem e-mailu.
Co nového ve světě fotografie!

 

Zadejte Vaši e-mailovou adresu:

Kamarád fotí rád?

Přihlas ho k odběru fotomagazínu!

 

Zadejte e-mailovou adresu kamaráda:

Soutěž

Sponzorem soutěže je:

IDIF

 

Kde se koná výstava fotografií Luďka Vojtěchovského?

V dnešní soutěži hrajeme o:



C/C++

Učíme se Win API - uživatelsky kreslený ListBox II

winapi_logo

9. prosince 2002, 00.00 | V dnešním pokračování si vylepšíme "souborový" ListBox z minulého článku o následující: volbu zobrazení malé/velké ikony, umožníme přidávat soubory přetažením (Drag and Drop), otevírat soubory poklepáním na položku a trochu změníme vykreslování.

Uživatelsky kreslený ListBox II

V dnešním pokračování si vylepšíme "souborový" ListBox z minulého článku. Budeme implementovat následující:

  • volbu zobrazení malé/velké ikony
  • umožníme přidávat položky (soubory) přetažením (Drag and Drop)
  • otevírat soubory poklepáním (dvojklikem) na položku
  • zefektivníme (na námět čtenáře) vykreslování.
  • zobrazování pouze názvů souborů bez cesty

Volba zobrazení malé/velké ikony

Nastavení malé nebo velké ikony si budeme udržovat pro další použití v globální proměnné a dále si vytvoříme funkci pro změnu této volby, která zajistí okamžité překreslení ListBoxu.

BOOL g_VelkeIkony = TRUE;

void NastavVelkeIkony(HWND hListBox, BOOL Velke)
{
  g_VelkeIkony = Velke;
  int vyska;
  if ( g_VelkeIkony )
    vyska = GetSystemMetrics(SM_CYICON)+2;
  else
    vyska = GetSystemMetrics(SM_CYSMICON)+2;
  SendMessage(hListBox, LB_SETITEMHEIGHT, 0, (LPARAM)vyska);
  RedrawWindow(hListBox, NULL, NULL,
    RDW_ERASE | RDW_INVALIDATE | RDW_ERASENOW );
}

Vytvoříme si novou funkci na přidání již vybraného souboru, kterou budeme volat ze dvou míst. Dále si změníme způsob získávání handle asociované ikony při vykreslování. Tento handle si uložíme do dat položky při jejím přidání a při vykreslování pak při jeho získání ušetříme nějaký ten takt procesoru.

void PridatPolozku(HWND hListBox, LPCTSTR lpSoubor)
{
  LRESULT Polozka =
    SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)lpSoubor);
  WORD index = 0;
  HICON hIcon = ExtractAssociatedIcon(g_hInstance, (LPTSTR)lpSoubor, &index);
  SendMessage(hListBox, LB_SETITEMDATA, Polozka, (LPARAM)hIcon);
}

Vykreslování položek

Upravíme si také obsluhu zprávy WM_DRAWITEM. Nyní budeme zobrazovat pouze názvy souborů bez cesty, musíme vzít v úvahu aktuální nastavení malé-velké ikony a handle ikony nyní získáváme z dat položky, které dostaneme jako prvek itemData struktury DRAWITEMSTRUCT. Celá obsluha bude nyní vypadat takto:

void DrawItem(LPDRAWITEMSTRUCT lpDIS)
{
  TCHAR szSoubor[MAX_PATH];
  HBRUSH hbPozadi;
  SendMessage(lpDIS->hwndItem, LB_GETTEXT, lpDIS->itemID, (LPARAM)szSoubor);
    WORD index = 0;
  int xIkona, yIkona; // rozměry malé nebo velké ikony
  if ( g_VelkeIkony )
  {
    xIkona = GetSystemMetrics(SM_CXICON);
    yIkona = GetSystemMetrics(SM_CYICON);
  }
  else
  {
    xIkona = GetSystemMetrics(SM_CXSMICON);
    yIkona = GetSystemMetrics(SM_CYSMICON);
  }
  HICON hIcon = (HICON)lpDIS->itemData;
  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);
  
  if ( g_VelkeIkony )
    DrawIcon(lpDIS->hDC,
      lpDIS->rcItem.left+1,
      lpDIS->rcItem.top+1, hIcon);
  else
    DrawIconEx(lpDIS->hDC,
      lpDIS->rcItem.left,
      lpDIS->rcItem.top, hIcon,
      xIkona, yIkona,
      0, NULL, DI_NORMAL);
  RECT rect;
  CopyMemory(&rect, &lpDIS->rcItem, sizeof(RECT));
  rect.left += xIkona + 2;
  LPTSTR  lpSoubor = strrchr(szSoubor, '\\');
  if ( !lpSoubor ) // pro jistotu :))
    lpSoubor = szSoubor;
  else
    lpSoubor++;
  DrawText(lpDIS->hDC, lpSoubor, -1, &rect,
    DT_SINGLELINE | DT_VCENTER);
}

Realizace Drag and Drop

Jak nyní na realizaci Drag and Drop způsobu přijímání souborů. Aby okno vůbec mohlo být cílem "puštění", musíme toto povolit pomocí funkce DragAcceptFiles, jejíž 2. parametr povolí nebo zakáže příjem položek pomocí Drag and Drop. Toto v našem případě nastavíme nejlépe v obsluze WM_INITDIALOG.

DragAcceptFiles(GetDlgItem(hWnd, IDC_LISTBOX), TRUE);

Od tohoto okamžiku bude okno dostávat při puštění položky zprávu WM_DROPFILES, jejímž parametrem wParam je handle interní struktury HDROP, ze které získáme potřebná data. Musíme ale nejprve na ListBox aplikovat již dříve probíraný subclassing, tady přesměrování jeho procedury okna na vlastní funkci, což provedeme opět v obsluze WM_INITDIALOG takto:

wndprocLB = (WNDPROC)SetWindowLongPtr(GetDlgItem(hWnd, IDC_LISTBOX),
  GWLP_WNDPROC, (LONG_PTR)WindowProcListBox);

Vlastní procedura okna ListBoxu bude obsluhovat pouze zmíněnou zprávu WM_DROPFILES, při čemž si zavoláme vlastní funkci pro její zpracování. Obě funkce vypadají následovně:

void DropFiles(HWND hWnd, HDROP hDrop)
{
  TCHAR szSoubor[MAX_PATH];
  int Pocet = (int)DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0);
  if ( Pocet <= 0 )
    return;
  for ( int i = 0; i < Pocet; i++ )
  {
    DragQueryFile(hDrop, i, szSoubor, MAX_PATH);
    PridatPolozku(hWnd, szSoubor);
  }
  DragFinish(hDrop);
}

WNDPROC wndprocLB;

LRESULT CALLBACK WindowProcListBox(HWND hWnd, UINT uMsg, WPARAM wParam,
                              LPARAM lParam)
{
  switch ( uMsg )
  {
    case WM_DROPFILES:
      DropFiles(hWnd,  (HDROP)wParam);
      break;
  }
  return CallWindowProc(wndprocLB,  hWnd, uMsg, wParam, lParam);
}

Otevírání souborů v položkách

Nyní zbývá ještě realizovat schopnost na poklepání (dvojklik) položky příslušný soubor otevřít stejným způsobem jako Průzkumník Windows, tedy v přidruženém programu, pokud takový existuje, nebo přímo spustit pokud jde o spustitelný soubor. Budeme tedy zachytávat v proceduře dialogu oznamovací zprávu LBN_DBLCLK a z ní volat vlastní funkci které předáme handle ListBoxu a která vypadá takto..

void LBNDblClk(HWND hListBox)
{
  TCHAR szSoubor[MAX_PATH];
  LRESULT Vyber = SendMessage(hListBox, LB_GETCURSEL, 0, 0);
  SendMessage(hListBox, LB_GETTEXT, Vyber,
    (LRESULT)szSoubor);
  ShellExecute(NULL, _T("open"), szSoubor,
    NULL, NULL, SW_SHOWNORMAL);
}

Pro úplnost si v části kódu procedury dialogu ukážeme její volání. Z procedury dialogu je ve výpisu pouze obsluha oznamovacích zpráv ListBoxu a CheckBoxu přepínajícího zobrazení na malé/velké ikony.

INT_PTR CALLBACK DialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  switch ( uMsg )
  {
    case WM_COMMAND:
      switch ( LOWORD(wParam) )
      {
        case IDC_LISTBOX:
          if ( HIWORD(wParam) == LBN_DBLCLK )
            LBNDblClk((HWND)lParam);
          break;
        case IDC_VELKE_IKONY:
          if ( HIWORD(wParam) == BN_CLICKED )
          {
            NastavVelkeIkony(GetDlgItem(hWnd, IDC_LISTBOX),
              IsDlgButtonChecked(hWnd, IDC_VELKE_IKONY) == BST_CHECKED);
          }
          break;
      }
      break;
  }
  return FALSE;
}

Doprovodný projekt včetně spustitelné release verze je ke stažení zde: uk_listbox_2.zip (36 kB)

Obsah seriálu (více o seriálu):

Tématické zařazení:

 » Rubriky  » C/C++  

 

 

 

Nejčtenější články
Nejlépe hodnocené články

 

Přihlášení k mému účtu

Uživatelské jméno:

Heslo: