Radek Chalupao programování a počítačích, vývoj software, školení ...
Domů | Články | Ukázky kódu | Tipy a triky | Aktuality | Školení a konzultace | Vývoj software | Freeware | Kontakt

Začínáme oživovat vzhled aplikace.  27.1.2002

win-api-27

V tomto článku se podíváme na problematiku oživení vzhledu aplikace. Z předchozích pokračování tohoto seriálu již víme, že pomocí skriptu zdrojů, a tím tedy samozřejmě také pomocí vizuálního editoru zdrojů (který máme k dispozici například ve Visual C++), nemůžeme u jednotlivých prvků (edit-boxy, list-boxy, tlačítka...) příliš měnit jejich vzhled. Přesně řečeno, ve skriptu zdrojů lze nastavit pouze to, co lze u toho kterého prvku nastavit pomocí stylu okna. Znamená to tedy, že například můžeme nastavit zarovnání textu vpravo u edit-boxu, nebo třeba u list-boxu nebo edit-boxu odstranit vnořené 3D okraje (tj. odebereme vlastnost "client-edge" apod. Ale již nemůžeme například nastavit písmo (font) každému jednotlivému prvku ani barvu textu či pozadí textu.

Existují opravdu vizuální nástroje, které samozřejmě umožňují "programátorovi" naklikat nejrůznější vlastnosti již při návrhu formuláře (tak se většinou nazývá okno aplikace). Toto však je za cenu poměrně výrazného snížení výkonu aplikace a výrazného zvýšení nároků na paměť a další systémové zdroje. Stačí se podívat pomocí Správce úloh (ve Windows NT/2000/XP) na hodnoty jako objekty GDI, popisovače apod. u aplikace vytvořené třeba s pomocí C++ Builderu nebo WinForms. Samozřejmě že tyto nástroje výrazně zrychlují vývoj aplikace, což je v mnoha případech dnes prioritou. Z tohoto hlediska osobně doporučuji používat knihovnu MFC, která je podle mého názoru optimálním "kompromisem" mezi "pohodlím a rychlostí vývoje" a výkonem výsledné aplikace. Zůstávat u čistého API v případě rozsáhlejších programů typu účetnictví apod. je dnes opravdu již nesmysl. Může to být samozřejmě zajímavé "intelektuální cvičení", ale programátor se musí také nějak živit a přizpůsobit volbu vývojových nástrojů "komerčním hlediskům".

Proč tento poměrně rozsáhlý teoretický úvod. Je totiž potřeba vědět, že vše, co si v tomto článku a v dalším pokračování ukážeme, znamená zvýšený nárok na systémové zdroje, který samozřejmě nebude zdaleka tak výrazný, jako když použijeme například celý balík knihovny VCL, ale "bez následků" to zákonitě zůstat nemůže.

Jaké máme tedy možnosti "customizace" běžných prvků Windows. Pokud bychom chtěli mít naprostou kontrolu nad jejich vzhledem, pak bychom u části z těchto prvků mohli použít tzv. "uživatelské kreslení". O něm si však řekneme až příště, nyní se podíváme na jednodušší způsob, kterým je použití zpráv typu WM_CTLCOLORxxx, kde xxx je typ jednoho z prvků, které toto podporují. Tímto "prvkem" může být i samo dialogové okno.

Ukázkový projekt je aplikace založená na hlavním dialogovém okně. Podívejme se, jak nastavit pozadí celého dialogu tak, jak to vidíte na obrázku (aniž bychom cokoli vykreslovali v handleru zprávy WM_PAINT). Zpráva WM_CTLCOLORDLG je posílána do procedury okna dialogu před tím, než jej systém vykreslí. V reakci na tuto zprávu můžeme specifikovat barvu nebo štětec, který bude použit pro vykreslení textu a pozadí dialogu. V parametru wParam této zprávy máme k dispozici kontext zařízení (HDC) dialogu. Pokud chceme specifikovat vlastní štětec, musíme v handleru této zprávy vrátit handle štětce (HBRUSH), který bude použit pro výplň pozadí dialogu. Vytvoříme si tedy při spuštění aplikace štětec (na základě bitmapy načtené ze zdrojů), jehož handle budeme vracet v handleru zprávy WM_CTLCOLORDLG. Podívejme se nyní na úplný výpis zdrojového kódu aplikace:

#define WIN32_LEAN_AND_MEAN
#include 
#include 
#include 
#include "resource.h"

HBRUSH g_hbPozadi;
HINSTANCE g_hInst;
LOGFONT logFont;
HFONT hfNadpis;

INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,
                            LPARAM lParam)
{
  switch ( uMsg )
  {
    case WM_COMMAND:
      switch ( LOWORD(wParam) )
      {
        case IDOK:
        case IDCANCEL:
          DeleteObject(g_hbPozadi);
          EndDialog(hwndDlg, LOWORD(wParam));
          break;
      }
      break;
    case WM_CTLCOLORDLG:
      SetBkMode((HDC)wParam, TRANSPARENT);
      return (INT_PTR)g_hbPozadi;
      break;
    case WM_CTLCOLORLISTBOX:
      SetBkMode((HDC)wParam, TRANSPARENT);
      SetTextColor((HDC)wParam, 0x00FFA0A0);
      return (INT_PTR)g_hbPozadi;
    case WM_CTLCOLOREDIT:
      SetTextColor((HDC)wParam, 0x00FFFFFF);
      SetBkMode((HDC)wParam, TRANSPARENT);
      if ( (HWND)lParam == GetDlgItem(hwndDlg, IDC_EDIT2))
        SetTextColor((HDC)wParam, 0x00D0A0A0FF);
      return (INT_PTR)g_hbPozadi;
      break;
    case WM_CTLCOLORSTATIC:
      if ( (HWND)lParam == GetDlgItem(hwndDlg, IDC_NADPIS))
        SetTextColor((HDC)wParam, 0x00A0FFFF);
      else
        SetTextColor((HDC)wParam, 0x00FFFFA0);
      SetBkMode((HDC)wParam, TRANSPARENT);
      return (INT_PTR)g_hbPozadi;
      break;
    case WM_INITDIALOG:
      g_hbPozadi = CreatePatternBrush(LoadBitmap(g_hInst, 
                           MAKEINTRESOURCE(IDB_POZADI)));
      GetObject(GetStockObject(DEFAULT_GUI_FONT), sizeof(logFont), &logFont);
      logFont.lfHeight = -22;
      logFont.lfItalic = TRUE;
      lstrcpy(logFont.lfFaceName, TEXT("Times New Roman"));
      hfNadpis = CreateFontIndirect(&logFont);
      SendDlgItemMessage(hwndDlg, IDC_NADPIS, WM_SETFONT, (WPARAM)hfNadpis, 
                                     (LPARAM)TRUE);
      SendMessage(GetDlgItem(hwndDlg, IDC_LIST1), LB_ADDSTRING, 0, 
                          (LPARAM)TEXT("1. řádek list-boxu"));
      SendMessage(GetDlgItem(hwndDlg, IDC_LIST1), LB_ADDSTRING, 0, 
                          (LPARAM)TEXT("2. řádek list-boxu"));
      SendMessage(GetDlgItem(hwndDlg, IDC_LIST1), LB_ADDSTRING, 0, 
                          (LPARAM)TEXT("3. řádek list-boxu"));
      SendMessage(GetDlgItem(hwndDlg, IDC_LIST1), LB_ADDSTRING, 0, 
                          (LPARAM)TEXT("4. řádek list-boxu"));
      SendMessage(GetDlgItem(hwndDlg, IDC_LIST1), LB_ADDSTRING, 0, 
                          (LPARAM)TEXT("5. řádek list-boxu"));
      break;
  }  
  return FALSE;
}

int WINAPI _tWinMain(HINSTANCE hInstance,  HINSTANCE hPrevInstance,
  LPTSTR lpCmdLine, int nCmdShow)
{
  g_hInst = hInstance;
  InitCommonControls();
  return (int)DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG_HLAVNI), NULL,
    (DLGPROC)DialogProc);
}

Z uvedeného je vidět, že štetec g_hbPozadi použijeme jako návratovou hodnotu u všech zpráv typu WM_CTLCOLORxxx. Pro všechny tyto zprávy také platí, že jejich parametr wParam je handle kontextu zařízení (HDC) a lParam je handle (HWND) okna prvku, kterého se tato zpráva týká. V případě prvků jako je edit, static nebo list-box pak můžeme jednoduše použít handle kontextu zařízení (parametr wParam) pro nastavení barvy textu a pozadí textu těchto prvků pomocí funkcí SetTextColor, SetBkColor a SetBkMode - jak vidíte ve výpisu.

Téma vlastního vzhledu tlačítek si necháme na příští pokračování.

V reakci na žádosti některých čtenářů jsem umístil do balíku ke stažení kromě zdrojů také spustitelný exe soubor aplikace (v release konfiguraci).

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


Máte připomínku, dotaz nebo komentář k článku a souvisejícími tématy?

Napište a odešlete ji emailem

Copyright © 2019  Radek Chalupa