Při výkladu funkcí (Function) ve VBA se na první místě zmiňuje, že na rozdíl od procedur (Sub) vracejí hodnotu a vyskytují se tak často na pravé straně příkazů přiřazení. A stejně jako procedury, které „tajně“ mohou propašovat ven hodnoty prostřednictvím veřejných proměnných nebo přebírání argumentů odkazem (ByRef), tak i funkce mají svá tajemství. Pokud je užijeme na listu, stávají se „dospělými“ uživatelskými funkcemi (UDF) – a měly by se krotit ve svém chování. Jejich výsledky neopouští místo, odkud jsou volány, tzn. buňky (viz Application.Caller), a jejich užití neovlivňuje ani prostředí aplikace. Je tomu tak vždy? Pojďme se podívat na jejich čertovské kousky, když se nikdo nedívá…
MsgBox, InputBox, FileDialog, …
1 2 3 4 | Function TESTOVKA() 'dialog (MsgBox) TESTOVKA = MsgBox("Baf!") End Function |
1 2 3 4 | Function TESTOVKA() As String 'dialog s textovým polem (InputBox) TESTOVKA = Application.InputBox("Kdo jsi?", "Identifikace") End Function |
1 2 3 4 5 6 7 8 9 | Function TESTOVKA() 'dialog pro výběr souboru With Application.FileDialog(msoFileDialogFilePicker) .AllowMultiSelect = False .Show 'vrací cestu k vybranému souboru TESTOVKA = .SelectedItems(1) End With End Function |
CommandBars a Dialogs
1 2 3 4 5 6 7 | Function TESTOVKA() 'zobrazení panelu 'nestandardní chování po uzavření 'zde Vložit symbol Application.CommandBars.FindControl(ID:=308).Execute TESTOVKA = "Hej!" End Function |
Vyvolávání panelů ve funkcích může způsobit neočekávané stavy. Excel často v danou chvíli jednoduše neočekává jejich zobrazení a neumí si poradit se sledem událostí v souvislosti s editací buňky. Vestavěné dialogy kolekce Dialogs se mi nepodařilo z funkcí vyvolat.
Komentáře
1 2 3 4 5 | Function TESTOVKA() 'komentář v buňce, lze nastavit i pro jinou buňku Application.Caller.AddComment "Jupí!" TESTOVKA = "Jejda!" End Function |
Zjednodušeně lze říci, že vykreslované objekty lze ve funkcích na listu použít. A ano, takový komentář lze například vložit do sousední buňky (Application.Caller.Offset(0, 1))
Ověření
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | Function TESTOVKA() 'ověření, lze nastavit i pro jinou buňku With Application.Caller.Validation .Delete .Add Type:=xlValidateList, AlertStyle:=xlValidAlertInformation, _ Operator:=xlBetween, Formula1:="první;druhá" .IgnoreBlank = True .InCellDropdown = True .InputTitle = "" .ErrorTitle = "" .InputMessage = "" .ErrorMessage = "" .ShowInput = False .ShowError = False End With TESTOVKA = "Šmarjá!" End Function |
Pro ověření samozřejmě platí, že výsledek funkce nesmí být v rozporu s pravidly ověřování.
Obrazce
1 2 3 4 5 | Function TESTOVKA() 'nakreslení tvaru ActiveSheet.Shapes.AddShape msoShapeCorner, 114, 30, 96, 90 TESTOVKA = "Nakresleno!" End Function |
Grafy
1 2 3 4 5 6 7 | Function TESTOVKA() 'vložení grafu ActiveSheet.Shapes.AddChart.Select ActiveChart.ChartType = xlColumnClustered ActiveChart.SetSourceData Source:=Range("List1!$C$7:$E$8") TESTOVKA = "Safra!" End Function |
Barva pozadí a ohraničení buňky
Nenašel jsem způsob, jak ve funkci úspěšně nastavit barvu pozadí buňky a to ani podmíněným formátem, který by jinak aplikovat šel. Omezené možnosti má i ohraničení buňky.
1 2 3 4 5 6 7 8 | Function TESTOVKA() 'ohraničení, nelze měnit typ a tloušťka čáry With Application.Caller.Borders(xlDiagonalUp) .LineStyle = xlContinuous .ColorIndex = 3 End With TESTOVKA = "Jaj!" End Function |
Jiné buňky, výběry, odskoky, aktivace
Ve funkcích listu nelze měnit výběry a provádět aktivace objektů (.Select, .Activate), ani řídit odskoky přes Applicaton.GoTo nebo hypertextové odkazy. Zakázány jsou také zápisy do oblastí buněk (Range, Cells) nebo manipulace s definovanými názvy (pojmenovanými vzorci).
Změny titulků, popisků aj.
Jednoznačně není možné říci, co lze a nelze měnit. Kupříkladu stavový řádek se ovlivnit z funkce listu nenechá, na druhou stranu titulek aplikace ano.
1 2 3 4 | Function TESTOVKA() Application.Caption = "Můj šéf je lump!" TESTOVKA = "Hmm!" End Function |
Filtry, Tabulky
Z funkcí listu není možné si pohrát s automatickým filtrem nebo třeba vložit Tabulku (Seznam). Na druhou stranu vás nechá setřídit oblast.
1 2 3 4 5 6 7 8 9 10 11 12 | Function TESTOVKA() 'setřídění oblasti With ActiveWorkbook.Worksheets("List1").Sort .SetRange Range("A1:A6") .Header = xlNo .MatchCase = False .Orientation = xlTopToBottom .SortMethod = xlPinYin .Apply End With TESTOVKA = "Aha!" End Function |
Volání procedury
Funkce se může odvolávat na jiné funkce a procedury. V tu chvíli pro ně platí stejná pravidla jako pro funkce, ze kterých jsou volány. Ve funkcích je stejně jako v procedurách možné užít v rámci parametrů ByRef, nicméně z „funkčního vězení“ ven nepropašujete žádný moták s hodnotou.
1 2 3 4 5 6 7 8 9 | Function TESTOVKA() 'volání procedury Call Procedura TESTOVKA = "Sláva!" End Function Sub Procedura() MsgBox "Hurá!" End Sub |
Nelze načasovat spuštění procedury (Application.OnTime), ani ve funkci přiřadit makro klávesové zkratce (Application.OnKey). Ve funkci listu není možné nastavit události (Applicaton.OnEntry, .OnDoubleClick, .OnCalculate aj.). Excel 4 makra (ExecuteExcel4Macro) jsou rovněž odstaveny na druhou kolej.
Průšvihy v zabezpečení
Ve funkcích listu je možné uplatnit metodu SendKeys pro odesílání virtuálních stisků kláves.
1 2 3 4 5 | Function TESTOVKA() 'text následovaný stiskem ENTER SendKeys "Jde to!~" TESTOVKA = "Vida!" End Function |
Sekvence je odeslána až po vrácení hodnoty a ukončení editace buňky.
1 2 3 4 5 6 7 | Function TESTOVKA() Dim strVzorec As String 'vložení vzorec do buňky vpravo strVzorec = Application.Caller.FormulaLocal SendKeys "{UP}{TAB}'" & strVzorec & "~" TESTOVKA = "Vzorec vpravo!" End Function |
Projde i sebedestrukce: SendKeys „{UP}^-~“.
Šokem byla pro mě snadná cesta spuštění prakticky libovolné aplikace přes funkci Shell.
1 2 3 4 5 | Function TESTOVKA() 'spuštění aplikace (Poznámkový blok) bez její aktivace ID = Shell("Notepad", vbMinimizedNoFocus) TESTOVKA = "ID" End Function |
Excel neklade odpor ani při aktivaci nainstalovaného doplňku.
1 2 3 4 5 | Function TESTOVKA() 'spuštění doplňku AddIns("Analytické nástroje").Installed = True TESTOVKA = "Chi chi!" End Function |
Ve funkcích listu je do jisté míry přípustné používat API.
1 2 3 4 5 6 7 8 9 10 11 | Private Declare Function GetUserName Lib "advapi32.dll" Alias "GetUserNameA" _ (ByVal lpBuffer As String, nSize As Long) As Long Function TESTOVKA() 'výpis aktuálního uživatele Windows Dim strUzivatel As String strUzivatel = String(100, Chr$(0)) GetUserName strUzivatel, 100 strUzivatel = Left$(strUzivatel, InStr(strUzivatel, Chr$(0)) - 1) TESTOVKA = strUzivatel End Function |
1 2 3 4 5 6 7 | Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) Function TESTOVKA() 'pozastavení kódu na 5 sekund Sleep 5000 TESTOVKA = "API!" End Function |
1 2 3 4 5 6 7 8 9 10 11 | Private Const SND_ASYNC = &H1 Private Const SND_NODEFAULT = &H2 Private Declare Function sndPlaySound Lib "winmm.dll" Alias "sndPlaySoundA" _ (ByVal lpszSoundName As String, ByVal uFlags As Long) As Long Function TESTOVKA() 'přehrání zvuku (asynchronně, nečeká na ukončení procedury) sndPlaySound "c:\Windows\Media\notify.wav", SND_ASYNC Or SND_NODEFAULT TESTOVKA = "Tutú!" End Function |
Ukázali jsme si, že vlastní funkce nejsou až takové puritánky, za jaké je učebnice Excelu vydávají. Dnešní cvičení berte ovšem za čistě experimentální. V řadě případů vám nikdo nezaručí, jak se Excel zachová poté, co mu hodíte na záda vykutálenou funkci. Problémy mohou nastat při přepočtu (viz také Application.Volatile), při volání událostí ze zásobníku atd.