Officír - Co se má stát, stane se - Události v Excelu

Co se má stát, stane se

Události v Excelu

autor: Ing. Petr Pecháček

http://officir.ic.cz

Sešit Excelu s příklady (komprese ZIP)

Na úvod

Jak již bylo řečeno hned v úvodu článků o VBA, objekty modelu podporují nejen metody a vlastnosti, ale i události, tj. umí reagovat na změnu (klepnutí myši, přechod na jiný list, obsahu buňky, ukládání sešitu apod.). Pod objektem si přitom můžete představit aplikaci Excel, sešit, listy ale i ovládací prvky ActiveX. Využití některých metod si dnes ukážeme.

Události na úrovni aplikace

Narazili jsem hned na to nejtěžší, alespoň z pohledu teorie. Jak poznáte dále, události sešitu i jeho listů jsou programátorovi dostupné v příslušných modulech. Aplikace ale nic takového nemá. Ono běžně ani není třeba události na úrovni aplikace zpracovávat a pokud ano, pak je nutné zajistit nastartování sledovacích procedur při každém spuštění Excelu. To se dá zařídit prakticky jen dvěma způsoby - šablonou nebo doplňkem. VBA znalí programátoři mi snad odpustí poněkud neúplný další výklad, resp. komentář k ukázce.

Ke sledování událostí na úrovni samotné aplikace používáme tzv. modul třídy (Class Module). Vkládáme jej stejně jako standardní moduly s pomocí ikony na panelu nebo z menu Insert / Class Module). Excelem navržený název modulu Class1 doporučuji vzápětí ručně přepsat na smysluplnější. Děje se tak v okně Properties. V daném případě jsem zvolil název UdalostiAppClass.

Modul třídy

V hlavním okně modulu třídy vepíšeme hned zkraje následující kód:

Public WithEvents Aplikace As Application
Ano, jedná se o deklaraci veřejně dostupného objektu Aplikace typu aplikace, kdy klíčové slovo WithEvents říká, že si přejeme podporu událostí. V momentě, kdy tento kód jak se říká odentrujeme, levá horní roletka okna začne tento objekt nabízet k použití a její sestřička napravo doplní události pro tento objekt. Výchozí událostí je NewWorkbook (Excel ji hned nabídne při výběru položky Aplikace z roletky).
Objekt aplikace

Kód pro tuto proceduru může vypadat následovně:

Private Sub Aplikace_NewWorkbook(ByVal Wb As Workbook)
   MsgBox "Díky za každý nový sešit..."
End Sub

Ono to ale nestačí, zatím jsme si uložili recepturu na cukroví, musíme ještě podle ní dokázat péct. Odborně řečeno, musíme vytvořit tzv. novou instanci třídy. To už se děje ve standardním modulu.

Public UdalostiExcel As UdalostiAppClass

Sub IniUdalostiAppClass()
   Set UdalostiExcel = New UdalostiAppClass
   Set UdalostiExcel.Aplikace = Application
   MsgBox "Proběhla inicializace třídy."
End Sub

Nejprve provedeme deklaraci proměnné UdalostiExcel s odkazem na naší třídu UdalostiAppClass. Dále vytvoříme inicializační proceduru a nastavíme proměnnou. Bez klíčového slůvka New se to neobejde. Poté jasně stanovíme, že náš původně definovaný objekt Aplikace bude odkazovat na tu stávající spuštěnou aplikaci obsahující kód (Excel může být samostaně spuštěn vícekrát, ačkoliv běžně jsou sešity otevírané ve stále stejné instanci Excelu). Trochu se to zašmodrchalo, ale nebojte, to je vše, pečení může začít. Stačí jen tuto proceduru spouštět, než začneme využívat události aplikace. K tomu nám dopomůže například jiná událost - Workbook_Open (viz dále). Kompletní ukázku naleznete v jednom ze sešitů přílohy.

Pozn. Za roky zkoušení ve mě stále není stoprocentní důvěra v to, že sledované události neproběhnou pasivně a Excel zareaguje. Navíc k některým událostem může dojít současně a pak je těžké říci, co se stane. Uživatel je ostatně rovněž nevyzpytatelný a nikdy nevíte, ze které strany vám poklepe na rameno...

Události na úrovni sešitu

Událostí, které je sešit Excelu schopen zaznamenat, je skutečně hodně. Mezi nejpoužívanější patří Workbook_Open (otevření sešitu) a Workbook_BeforeClose (před uzavřením sešitu). Jako další se nabízí události ukládání sešitu, aktivace a deaktivace, tisk, změna okna a události pro listy tohoto sešitu vnímané jako kolekce. Kód probíhající na základě těchto událostí se zadává v patřičném modulu pro sešit (ThisWorkbook). Pokud jej nevidíte, rozbalte v levém okně strom pro daný projekt. Z levé roletky hlavního okna pak vyberte položku Workbook. Excel automaticky vloží obálku události pro otevření sešitu. Pokud si přejete jinou událost, pomůže vám v tom pravá roletka.

Vytváření procedur událostí na úrovni sešitu

Podívejme se na příklady.

Private Sub Workbook_Open()
   MsgBox "Vítejte v sešitu s ukázkami událostních procedur."
End Sub
Private Sub Workbook_BeforeClose(Cancel As Boolean)
   MsgBox "Snad Vám tento sešit byl k užitku. Pěkný den."
End Sub

Zatímco první procedura neočekává žádný parametr, druhá ano. Narozdíl od vlastních funkcí, přestože parametr není uvozen slovem Optional, je nepovinný. Jak si ukážeme později, parametry předáváme proceduře vlastní akcí. Cancel je výjimkou. Vyskytuje se i jinde a slouží k blokování dané akce. To znamená, že pokud jej nastavíme v této proceduře na True, uživatel nebude schopen sešit nenásilně zavřít. Akce je v daném případě dovedena do důsledku (ne všechny jsou takto dořešeny), neboť se nám nepodaří zavřít ani samotná aplikace.

Pozn. Velké množství událostí je typu "Before...". To znamená že náš kód navržený pro událostní procedury proběhne těsně předtím, než k události dojde. Je to logické, pokud si takovou akci přejeme zakázat.

Události na úrovni listů

Procedury, které mají proběhnout na základě událostí listu, umísťujeme do jejich patřičného modulu. Jsou to události pro konkrétní listy vnímané samostatně. Nejčastěji sem zařazujeme zpracování buněk daného listu na základě jejich změny. Z levé roletky hlavního okna vybereme Worksheet. Excel vloží úvodní kód přednastavené procedury pro událost SelectionChange (změna výběru oblasti buněk). My ale většinou potřebujeme událost Change (změna obsahu v oblasti buněk). Tuto událost vybereme v pravé roletce. Excel opět vloží obálku procedury.

Vytváření procedur událostí na úrovni listu

Opět se podívejte rovnou na příklad. Vysvětlivku najdete pod výpisem kódu.

Private Sub Worksheet_Change(ByVal Target As Range)
   Dim Oblast As Range
   
   'definice sledované oblasti
   Set Oblast = Range("B2:D5")
   
   'test výběru
   If Union(Oblast, Target).Address = Oblast.Address Then
      MsgBox "Změněna hodnota v buňce " & Target.Address(0, 0)
   End If

End Sub

Vidíme, že procedura očekává proměnnou Target typu Range (oblast). Tu jí předáváme samotnou akcí (nikoli programově), v daném případě editací buněk. Lidově řečeno, "ježibaba pozná, kde jí loupeme perníček". Běžně je třeba sledovat jen změnu určité oblasti buněk a ne celého listu. K tomu se používá jednoduchá úvaha o množinách. Předpokládejme, že vybraná buňka leží ve sledované oblasti. Pak adresa oblasti vzniklé sloučením editované buňky se sledovanou oblastí je totožná jako adresa sledované oblasti. Ke sloučení slouží funkce Union. Kód, jenž následuje po splnění podmínky je libovolný.

Pozn. Když už jsem načal přirovnání Excelu s ježibabou... ona nevidí do každého koutu. Nenachytala by nás třeba, jak jí chaloupku zdobíme grafiti. Totiž, Excel nebere jako událost změnu vizuálního formátování buňky (barevnost, ohraničení, číselný formát buňky apod.).

Další ukázka využívá události dvojkliku. Procedura opět očekává oblast a pravdivostní hodnotu pro proměnnou Cancel. Oblast tentokrát nezadáme (akce se bude vztahovat k celému listu), ale využijeme proměnnou Cancel, kterou nastavíme na True, čímž zablokujeme danou akci pro všechny buňky listu. Pozn. V případě dvojkliku je to spíše hračka (uživatel nadále může do buňky vstupovat přes řádek vzorců nebo přes schránku).

Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
   'zakázaná akce
   Cancel = True
End Sub

Události prvků ActiveX

Jak bylo již řečeno, i pro prvky pro formulář UserForm umožňují sledování určitých aktivit, kterých se na nich dopouštíme. Stejně tak samotný UserForm podporuje sledování událostí. Nejdůležitější je událostní procedura UserForm_Initialize, do níž zapisujeme kód zpracovávající prvky formuláře, než se rozhrne opona a uživatel jej spatří v plné kráse. Patří sem naplnění ComboBoxů položkami a přednastavení prvků obecně.

Private Sub UserForm_Initialize()
   'zde je kód posledních příprav před zobrazením formuláře
End Sub
Událostí pro prvky ActiveX je mnoho a tak si ukážeme klasický příklad události Click (klepnutí myší) zaškrtávacího políčka (CheckBox). Tato událost je pro většinu prvků výchozí, proto, pokud provedeme dvojklik na prvek v návrhovém zobrazení, Excel nás přenese do prostředí samotného kódu a dosadí obálku pro tuto událostní proceduru. I zde platí, že ostatní události pro daný prvek vybíráme z pravé roletky. Malá ukázka:

Private Sub CheckBox1_Click()
   MsgBox "Nech mě na pokoji, neklepej na mě!"
End Sub
Událost Click pro CheckBox

Pozn. Programová změna obsahu prvku má často za následek vyvolání události, přestože ručně k ní nedošlo. Na to je třeba si dávat pozor. Že k tomu, dochází, lze rozpoznat během krokování kódu.

Častá úloha aneb příklad na závěr

Lze zakázat uzavření formuláře klepnutím na křížek v pravém horním rohu? Ano, je třeba využít zmiňovaný parametr Cancel a událost formuláře QueryClose.

Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
   Cancel = True
End Sub

I zde ale řešení není stoprocentní, neboť existuje možnost přerušení stiskem Ctrl+Break. Je tedy nutné zařadit ještě někde jinde kód Application.EnableCancelKey = xlDisabled, nejlépe při samotné inicializace formuláře.

Sub UserForm_Initialize()
   'vlastní kód
   Application.EnableCancelKey = xlDisabled
End Sub

Pozor! V případě nekonečného cyklu nebo jinak chybujícího makra je možnost zrušení běhu procedury velmi důležitá. Nezbavujte se jí.

Sešit Excelu s příklady (komprese ZIP)

Officír pro časopis CHIP.