Tvorba komponent pro C++ Builder - úsporná komponenta – TMyWinControl

Prostředí Borland C++ Builder patří do té skupiny, ve které je prioritou rychlost a „snadnost“ vývoje nad maximální efektivitou. Podíváme se na to, co dělat, když tu efektivitu chcete.
Na mnoha diskusních fórech se vášnivě diskutuje o protikladu mezi rychlostí a pohodlností vizuálního návrhu aplikace a výkonností výsledného programu, a to především z hlediska nároků na paměť a systémové zdroje vůbec. Prostředí Borland C++ Builder, pokud používáme knihovnu VCL a z toho plynoucí výhody (i nevýhody) vizuálního návrhu, patří do té skupiny, ve které je prioritou rychlost a „snadnost“ vývoje nad maximální efektivitou. V tomto článku nebudeme řešit otázku, co je lepší nebo horší, ale budeme vycházet z toho, že mohou nastat situace, že programátor vyvíjející aplikaci v C++ Builderu chce (v rámci možností použití VCL knihovny) aplikaci co nejvíce zefektivnit i za cenu psaní vlastního kódu vyžadujícího trochu hlubší znalosti programování Windows, tedy Win API. V tom případě může být výhodné mít jednoduché komponenty zapouzdřující základní prvky Windows, jako je buton, list-box, edit a podobně. V tomto článku však půjdeme ještě „níže“.

Proč vytvářet pro každý takovýto typ prvku Windows podobnou komponentu, když můžeme použít obecný TWinControl a vše si napsat v konkrétní aplikaci. Zde ale narazíme na problém s přístupností k metodě CreateParams, ve které pomocí funkce CreateSubClass vytváříme konkrétní prvek. Navíc nemáme přímo přístupnou proceduru okna, tj. virtuální metodu WindowProc, a musíme se na proceduru okna „napíchnout“ jiným způsobem (jak, to jsme si v tomto seriálu již ukázali a ještě ukážeme).

Nabízí se proto myšlenka vytvořit si tu nejjednodušší komponentu odvozenou od TWinControl, ve které vypublikujeme formou událostí (events) proceduru okna, metodu CreateParams a případně CreateWindowHandle, pokud chceme mít jednoduchým způsobem přístupné místo bezprostředně po vytvoření handle okna.

Jako příklad jsem vytvořil komponentu TMyWinControl, která je odvozena od TWinControl. Máme zde tři vlastní typy událostí:

typedef void __fastcall(__closure *TMWCWindowProc)
(TObject* Sender, UINT uMsg, WPARAM wParam, LPARAM lParam,
LPBOOL bHandled);
typedef void __fastcall(__closure *TMWCCreateParams)
(TObject* Sender, TCreateParams &Params);
typedef void __fastcall(__closure *TMWCCreateWindowHandle)
(TObject* Sender);

jejichž význam je jistě zřejmý již z jejich názvů a předchozích odstavců. V komponentě jsou přidány odpovídající události (events):

__property TMWCWindowProc OnWindowProc  =
{ read=FOnWindowProc, write=FOnWindowProc };
__property TMWCCreateParams OnCreateParams  =
{ read=FOnCreateParams, write=SetOnCreateParams };
__property TMWCCreateWindowHandle OnCreateWindowHandle =
{ read=FOnCreateWindowHandle, write=SetOnCreateWindowHandle };

V kódu jsou přepsány virtuální metody

virtual void __fastcall CreateParams(TCreateParams &Params);
virtual void __fastcall CreateWindowHandle(const TCreateParams &Params);
virtual void __fastcall WndProc(Messages::TMessage &Message);

Realizaci vyvolání událostí vidíte ve výpisu zdrojového kódu komponenty, který je velice stručný a jednoduchý:

/////////////////////////////////////////////////////////////////////////////
//
// MyWinControl.cpp
//
// (C) Radek Chalupa, 2001
// www.rplusj.cz
//
/////////////////////////////////////////////////////////////////////////////

#include <vcl.h>
#pragma hdrstop

#include "MyWinControl.h"
#pragma package(smart_init)

static inline void ValidCtrCheck(TMyWinControl >
{
new TMyWinControl(NULL);
}

__fastcall TMyWinControl::TMyWinControl(TComponent* Owner)
: TWinControl(Owner)
{
Width = 100;
Height = 40;
}

namespace Mywincontrol
{
void __fastcall PACKAGE Register()
{
TComponentClass classes[1] = {__classid(TMyWinControl)};
RegisterComponents("Vyuka", classes, 0);
}
}

void __fastcall TMyWinControl::CreateParams(TCreateParams &Params)
{
TWinControl::CreateParams(Params);
if ( FOnCreateParams != NULL )
FOnCreateParams(this, Params);
}

void __fastcall TMyWinControl::CreateWindowHandle(const TCreateParams &Params)
{
TWinControl::CreateWindowHandle(Params);
if ( FOnCreateWindowHandle != NULL )
FOnCreateWindowHandle(this);
}

void __fastcall TMyWinControl::WndProc(Messages::TMessage &Message)
{
if ( FOnWindowProc == NULL )
{
TWinControl::WndProc(Message);
return;
}
BOOL bHandled = FALSE;
FOnWindowProc(this, Message.Msg, Message.WParam,
Message.LParam, &bHandled);
if ( !bHandled )
TWinControl::WndProc(Message);
}

void __fastcall TMyWinControl::SetOnCreateParams
(TMWCCreateParams value)
{
if ( FOnCreateParams != value )
{
FOnCreateParams = value;
RecreateWnd();
}
}

void __fastcall TMyWinControl::SetOnCreateWindowHandle
(TMWCCreateWindowHandle value)
{
if ( FOnCreateWindowHandle != value )
{
FOnCreateWindowHandle = value;
RecreateWnd();
}
}

Příklad I. – button animující ikony

Jako příklad použití této ikony si můžeme ukázat třeba buton animující ikony uložené v ImageListu s tím, že po jeho stisknutí se animace zastaví a zůstane zobrazena právě „aktivní“ ikona. Jsou použity 2 „pomocné“ proměnné

HICON m_hIcon;
int m_IconIndex;

Vše je realizováno v následujících třech událostech. Nejprve si vytvoříme vlastní událost OnCreateParams tohoto butonu, ve které přímo v aplikaci vytvoříme prvek typu button pomocí funkce CreateSubClass, jak jsme to již dělali v předchozích článcích v kódu komponenty. Pro zobrazení ikony na tlačítku použijeme ten nejednodušší způsob, který se nám nabízí, tedy definovat vlastnost butonu BS_ICON. Nastavení ikony si ukážeme za okamžik.

void __fastcall TForm1::MyButtonCreateParams(TObject *Sender,
TCreateParams &Params)
{
CreateSubClass(Params, "BUTTON");
Params.Style = Params.Style | BS_PUSHBUTTON | BS_ICON | WS_TABSTOP;
Params.Width = 42;
Params.Height = 42;
}

Pro umožnění animace ikon spustíme timer. Jako vhodný okamžik jeho spuštění se nám nabízí událost OnCreateWindowHandle (kterou jsme si také sami vytvořili v komponentě).

void __fastcall TForm1::MyButtonCreateWindowHandle(TObject *Sender)
{
SetTimer(MyButton->Handle, 1, 200, NULL);
}

Nakonec si vytvoříme událost OnWindowProc (výše zmíněnou proceduru okna), ve které ošetříme zastavení animace při stisknutí tlačítka, resp. zcela přesně řečeno při stlačení myši na tlačítku (což ještě nemusí znamenat, že dojde ke kliknutí). Při zachycení časovače, tedy zprávy WM_TIMER, pak vybereme další ikonu z image-listu a pomocí zprávy BM_SETIMAGE nastavíme její handle jako handle ikony, která se má zobrazovat na tlačítku.

void __fastcall TForm1::MyButtonWindowProc(TObject *Sender, UINT uMsg,
WPARAM wParam, LPARAM lParam, LPBOOL bHandled)
{
switch ( uMsg )
{
case WM_LBUTTONDOWN:
KillTimer(MyButton->Handle, 1);
break;
case WM_TIMER:
if ( m_hIcon != NULL )
DestroyIcon(m_hIcon);
m_hIcon = ImageList_GetIcon((HIMAGELIST)ImageListIcons->Handle,
m_IconIndex,
ILD_NORMAL | ILD_TRANSPARENT);
SendMessage(MyButton->Handle, BM_SETIMAGE, IMAGE_ICON,
(LPARAM)m_hIcon);
if ( m_IconIndex < ImageListIcons->Count )
m_IconIndex++;
else
m_IconIndex = 0;
break;
}
}

Příklad II – vlastní static control

Dalším příkladem může být komponenta nazvaná MyStatic, kterou použijeme pro jednoduché a efektivní zobrazení bitmapy pomocí standardního prvku „static control“. Tomuto prvku můžeme (mimo jiné) nastavit příznak stylu SS_BITMAP a pak použít zprávu STM_SETIMAGE, kde jako parametr lParam je handle bitmapy (HBITMAP). Celý kód, který to realizuje, je velmi jednoduchý:

void __fastcall TForm1::MyStaticCreateParams(TObject *Sender,
TCreateParams &Params)
{
CreateSubClass(Params, "STATIC");
Params.Style = Params.Style | SS_BITMAP;
Params.ExStyle = WS_EX_CLIENTEDGE;
}

void __fastcall TForm1::MyStaticCreateWindowHandle(TObject *Sender)
{
SendMessage(MyStatic->Handle,
STM_SETIMAGE, IMAGE_BITMAP,
(LPARAM)LoadBitmap(HInstance, "HONZA"));
}

Zde si můžete stáhnout zdrojový kód my_win_control.zip

Váš názor Další článek: AMD uvádí Athlon XP 1900+

Témata článku: Software, Windows, Programování, Komp, TV +, Přístupné místo, Tvorba, Button, Zobrazený prvek, Tvor, Read, Hicon, Komponenta, Předchozí odstavec, Odpovídající událost, Timer, Message


Určitě si přečtěte

Velký den pro Apple: Uvedl tři nové Macy s vlastním procesorem M1
Lukáš Václavík
PočítačeApple
Co je to UWB? Nová technologie zastoupí Wi-Fi, Bluetooth i NFC a slibuje velké věci

Co je to UWB? Nová technologie zastoupí Wi-Fi, Bluetooth i NFC a slibuje velké věci

** V nových mobilech se začíná objevovat tajemná zkratka UWB ** Jde o další technologii, jak navzájem propojit různá zařízení ** Oproti Wi-Fi a Bluetooth má řadu výhod

Lukáš Václavík | 36

Lukáš Václavík
UWBIoTTechnologie
CZ.NIC bezplatně naděluje USB/NFC klíče. Jak jej získat?
Lukáš Václavík
CZ.NICeGovernment
Jak se šíří Covid v Česku: Čerstvá data, semafor PES, mapy okresů a obcí. Každý den aktualizované grafy

Jak se šíří Covid v Česku: Čerstvá data, semafor PES, mapy okresů a obcí. Každý den aktualizované grafy

** Vývoj COVID-19 v Česku: nakažení, úmrtí, testovaní, hospitalizovaní ** Mapa podle okresů, přehled podle věku, situace v Evropě i ve světě ** Každý den aktualizované grafy a mapy

Marek Lutonský | 172

Marek Lutonský
COVID-19Koronavirus

Aktuální číslo časopisu Computer

Jak prodloužit výdrž notebooku

Velké testy: gamepady a inkoustové tiskárny

Důkladný test Sony Playstation 5