Zmožnost samodejnega snemanja makrov ima pri uporabnikih zelo različne ocene. Od velikega navdušenja, pa vse do mnenja, da je popolnoma neuporabna. Razkol v ocenah je posledica različnih pričakovanj. Excel si ne bo nikoli znal sam pisati programske kode. Zna pa zelo dobro posneti in nato izvajati naša opravila. Zelo poenostavljeno povedano: samodejno snemanje makrov deluje tako, da ga uporabnik vključi, izvaja opravila, ki jih želi posneti, in snemanje ustavi.

PREPROST ZGLED: POSTAVITEV STRANI

Preidimo k primeru. Želimo posneti makro, ki nam bo postavitev strani nastavil na Ležeče. Postopek je naslednji:
1. Iz menija Excela izberemo Orodja\Makro\Posnemi nov makro.
2. V priklicanem pogovornem oknu Snemanje makra kliknemo na gumb V redu. Če želimo, lahko vpišemo določene lastnosti makra, a kaj več kot ime makra ponavadi ne vpisujemo.
3. Od trenutka, ko se pojavi majhna orodna vrstica z gumbom za ustavitev snemanja, Excel snema naša opravila. Naše opravilo je nastaviti postavitev strani na »Ležeče«, zato prek menija Datoteka\Priprava strani nastavimo usmerjenost na Ležeče.
4. Kliknemo na gumb za ustavitev snemanja makra.

Makro smo tako posneli. Prikličemo ga tako, da odpremo urejevalnik (Visual Basic Editor). Najhitreje s kombinacijo Alt + F11. V oknu Project – VBAProject kliknemo na Modul1 (ali Modul2, če je Modul1 že pred tem obstajal). Poglejmo si kodo makra.

Sub Makro1()
'
' Makro1 Makro
' Makro ustvaril: Samo Rubin, dne 02.09.2007
'

'
With ActiveSheet.PageSetup
.PrintTitleRows = ""
.PrintTitleColumns = ""
End With
ActiveSheet.PageSetup.PrintArea = ""
With ActiveSheet.PageSetup
.LeftHeader = ""
.CenterHeader = ""
.RightHeader = ""
.LeftFooter = ""
.CenterFooter = ""
.RightFooter = ""
.LeftMargin = Application.InchesToPoints(0)
.RightMargin = Application.InchesToPoints(0)
.TopMargin = Application.InchesToPoints(1)
.BottomMargin = Application.InchesToPoints(1)
.HeaderMargin = Application.InchesToPoints(0)
.FooterMargin = Application.InchesToPoints(0)
.PrintHeadings = False
.PrintGridlines = False
.PrintComments = xlPrintNoComments
.PrintQuality = 600
.CenterHorizontally = False
.CenterVertically = False
.Orientation = xlLandscape
.Draft = False
.PaperSize = xlPaperA4
.FirstPageNumber = xlAutomatic
.Order = xlDownThenOver
.BlackAndWhite = False
.Zoom = 100
.PrintErrors = xlPrintErrorsDisplayed
End With
End Sub

PREDOLGO? SKRAJŠAJMO!

Novinci boste zagotovo presenečeni nad obsegom kode. Nastavili smo zgolj eno lastnost, a smo zanjo dobili 41 vrstic kode. Ne glede na obseg je makro zelo uporaben. Naredi to, kar želimo, in čeprav je zelo obsežen, je njegov čas izvajanja neznaten.

Ker pa je eden od namenov tega članka izboljšati razumevanje makrov, si zadajmo cilj, da makro skrajšamo. Posnemimo še en makro. Tokrat nastavimo postavitev strani na Pokončno. Postopek je enak zgoraj opisanemu. Ko pogledamo kodo, se moramo zelo potruditi, da sploh najdemo razliko med prvim in drugim makrom. Razlika je zgolj v vrstici: .Orientation = xlPortrait, prej se je ta glasila .Orientation = xlLandscape.

Excel pri samodejnem snemanju makrov ni posnel zgolj spremembe pri postavitvi strani, temveč je posnel vse lastnosti postavitve strani – tudi tiste, ki jih nismo spreminjali. Vsaka lastnost postavitve strani ima v makru svojo vrstico. Makro lahko tako bistveno skrajšamo, tako da obdržimo zgolj vrstico, ki se nanaša na usmerjenost strani (Orientation), druge pa odstranimo iz makra. Prenovljen makro (spremenili smo tudi komentar) se tako glasi:

Sub Makro1()
' Makro nastavi usmerjenost strani
' na ležeče
With ActiveSheet.PageSetup
.Orientation = xlLandscape
End With
End Sub

Že tako močno skrajšan makro Makro1 je moč dodatno skrajšati. Struktura With-End je v našem primeru neprimerna. Njena uporabnost je v tem, da hitreje opravimo več operacij nad istim objektom. Beseda "hitreje" se nanaša tako na čas pisanja programske kode, kot na čas izvajanja le-te. A v našem primeru nastavljamo zgolj eno lastnost (Orientation), zato je prikladneje zapisati: ActiveSheet. PageSetup.Orientation = xlLandscape. Končni makro, ki nastavi usmerjenost strani na ležeče, se tako glasi:

Sub NastaviLezece()
' Makro nastavi usmerjenost strani na ležeče
ActiveSheet.PageSetup.Orientation = xlLandscape
End Sub

Samodejno snemanje makrov je zelo koristno tudi za spoznavanje objektov in lastnosti, ki jih Visual Basic for Application za Excel vsebuje. V zgornjem primeru smo spoznali kopico novih lastnosti, ki jih bomo s pridom uporabili v kakšnem drugem primeru, ko bo naša naloga spremeniti drugo nastavitev v postavitvi strani. Za novince je impresivno tudi sprotno spremljanje nastajanja kode. Če najprej prikličemo urejevalnik (Visual Basic Editor) in nato zaženemo snemanje makra, lahko spremljamo nastajanje kode.

OHRANITEV AKTUALNEGA DELOVNEGA ZVEZKA

Preidimo na nov primer. Želimo napisati makro, ki bo shranil aktualen delovni zvezek. Shranil pa ga bo pod dogovorjenim imenom. Kako bo dogovorjeno ime strukturirano, bomo določili pozneje. Začnimo tako, da posnamemo makro, ki odprt delovni zvezek shrani pod imenom Test.xls v mapo C:\Temp. Poglejmo si kodo samodejno posnetega makra:

Sub Makro2()
'
' Makro2 Makro
' Makro ustvaril: Samo Rubin, dne 02.09.2007
'

'
ChDir "C:\Temp"
ActiveWorkbook.SaveAs Filename:="C:\Temp\test.xls", FileFormat:=xlNormal, _
Password:="", WriteResPassword:="", ReadOnlyRecommended:=False, _
CreateBackup:=False
End Sub

Ključen ukaz v kodi makra je metoda SaveAs. Spomnimo: metode so akcije nad objekti. A do zdaj uporabljene metode so bili enostavnejše, saj niso imele argumentov. Argumenti podrobneje opredelijo delovanje metod in lastnosti – tudi lastnosti imajo argumente. V našem primeru je pomemben argument Filename, saj z njim določimo ime (in mapo) Excelove datoteke. Iz kode primera je razvidno, da imajo argumenti metod in lastnosti naslednjo zgradbo:

Objekt.metoda Argument1:= vrednost_arg_1, Argument2:= vrednost_arg_2, …
Argumentn:= vrednost_arg_n

Opišimo. Imenu metode sledijo argumenti, ki so poimenovani in ločeni z vejico. Vrednosti so argumentom prirejene z znakoma :=. Argumente delimo na obvezne in opcijske. Opcijske lahko izpustimo. Excel v tem primeru opcijskim argumentom priredi privzete vrednosti. V našem primeru bi lahko makro poenostavili in zapisali:

Sub Makro2()
ActiveWorkbook.SaveAs Filename:="C:\Temp\test.xls"
End Sub

Poleg odvečnih argumentov metode SaveAs smo iz makra odstranili tudi ukaz ChDir, saj ga vrednost argumenta Filename povsem nadomešča.

Argumenti metod in lastnosti povzročajo začetnikom nekaj težav pri razumevanju makrov tudi zato, ker dovoljujejo še eno sintakso, ki je krajša, a tudi manj nazorna. Argumente lahko namreč določimo tudi tako, da jih zapišemo (ločene z vejicami) v oklepajih za nazivom metode ali lastnosti. Natančneje povedano, v oklepajih navedemo vrednosti argumentov. Sintaksa je torej naslednja:

Objekt.metoda (vrednost_arg_1, vrednost_arg_2, , ,vrednost_arg_5)

Pri tovrstnem podajanju argumentov Excel iz vrstnega reda vrednosti argumentov ugotovi, za kateri argument gre, zato je vrstni red zelo pomemben. Pozorni pa moramo biti tudi na opcijske argumente. Njihovih vrednosti nam ni treba navajati, a če nedefiniranemu argumentu sledi definiran, moramo vnesti vejico, tako da ohranimo vrstni red.

Naš makro, ki smo ga še dodatno skrajšali, takole je videti:

Sub Makro2()
ActiveWorkbook.SaveAs ("C:\Temp\test.xls")
End Sub

Preden nadaljujemo s primerom, se pomudimo še pri konstantah. Vrednost nekaterih argumentov niso poljubne. Če smo v našem primeru za vrednost argumenta Filename lahko določili poljubno besedilo, torej poljubno mapo in ime datoteke, pa tega ne moremo storiti za argument FileFormat. Z njim določimo obliko oz. tip datoteke. Če bi želeli delovni zvezek shraniti kot besedilno datoteko, bi argumentu FileFormat priredili konstanto xlTextMSDOS. Excelove konstante v makrih prepoznamo po predponi xl. V makrih jih pogosto uporabljamo. Poglejmo, na primer, nazaj v prvi makro, ki jih vsebuje kar nekaj.

Vrnimo se k primeru. Datoteko želimo shraniti pod dogovorjenim imenom. Ime je sestavljeno tako, da najprej zapišemo datum shranjevanja v obliki yyyy_mm_dd (datum v tej obliki omogoča prikladno razvrščanje), sledi vezaj in nato podatek o imenu in priimku prodajalca, ki je zapisan v celici A1. Med imenom in priimkom ne želimo imeti presledka. V primeru s spodnje slike in dogovoru, da shranjujemo 17. septembra letos, se bo ime datoteke glasilo 2007_09_17-JanezNovak.xls.

Iz primera makra Makro2 smo se naučili, kako makro shraniti. Pri drugem delu, tvorbi ustreznega imena datoteke, pa si s samodejnim snemanjem makrov ne moremo pomagati, zato makro ustvarimo na »star« način - s tipkanjem. Za osnovo vzemimo kar makro Makro2. Najprej ga preimenujemo, nato definiramo spremenljivko ImeDat, ki bo vsebovala ime datoteke, in ji priredimo testno vrednost.

Sub Shrani()
Dim ImeDat As String
ImeDat = "C:\Temp\test.xls"
ActiveWorkbook.SaveAs (ImeDat)
End Sub

Začnimo z datumom. Če spremenljivki ImeDat priredimo vrednost, ki jo vrne funkcija Date, bo datum shranjevanja sicer pravilen, a ne bo v obliki, ki jo želimo. Za preoblikovanje uporabimo funkcijo Format: ImeDat = Format(Date,"yyyy_mm_dd"). Obliko datuma določa torej drugi argument funkcije Format. Okrajšave, ki določajo leto, mesec in dan, so enake kot pri datumskih funkcijah delovnega zvezka, zato jih hitro osvojimo. Da bomo makro lahko uspešno preizkusili, dodajmo imenu datoteke, ki je trenutno sestavljena zgolj iz datuma, še končnico .xls. Za dodajanje niza znakov uporabimo že znano funkcijo &. Vmesna različica makra je tako naslednja:

Sub Shrani()
Dim ImeDat As String
ImeDat = Format(Date,"yyyy_mm_dd") & ".xls"
ActiveWorkbook.SaveAs (ImeDat)
End Sub

Makro poženemo. Datoteka se, če makro poganjamo npr. 17. septembra 2007, shrani pod imenom "2007_09_17.xls". Pozabili smo nastaviti mapo, kamor se datoteka shranjuje. Da je makro preglednejši, mapo zapišemo kot konstanto. Za določitev konstante uporabimo rezervirano besedo Const.

Sub Shrani()
Const ImeMape = "C:\Temp\"
Dim ImeDat As String
ImeDat = ImeMape & Format(Date, "yyyy_mm_dd") & ".xls"
ActiveWorkbook.SaveAs (ImeDat)
End Sub

Nadaljujmo s primerom. Imenu datoteke želimo dodati podatek o prodajalcu, katerega ime je zapisano v celici A1, ter pred imenom še vezaj. Za hiter preizkus vrstico makra, kjer določimo vrednost spremenljivke ImeDat, dopolnimo tako, da se glasi: ImeDat = ImeMape & Format(Date, "yyyy_mm_dd") & "-" & Cells(1, 1).Value & ".xls". Če makro preizkusimo, se datoteka shrani pod imenom "2007_09_17-Prodajalec: Janez Novak.xls". Za ta preizkus in za vse nadaljnje privzemimo, da se opravljajo 17. septembra 2007.

Naslednji korak do rešitve je odprava niza "Prodajalec: ". Uporabimo funkcijo Mid. Funkcijo najdemo tudi med funkcijami delovnega zvezka, kjer sodi med pogosto uporabljene. Funkcija Mid izlušči iz besednega niza podniz. Sestavljajo jo trije argumenti: Mid(besedni_niz, začetek, [dolžina]). Prvi argument predstavlja besedni niz, iz katerega želimo izluščiti želeni podniz. V našem primeru bomo zapisali Cells(1,1).Value. Argument začetek določa zaporedno številko znaka v besednem nizu, od katere naprej želimo izluščiti podniz. Mi bomo navedli število 13, saj je prvi znak imena prodajalca na 13. mestu. Z zadnjim argumentom (dolžina) določimo število znakov v podnizu. Argument ni obvezen, če ga izpustimo, funkcija vrne vse preostale znake. Naš primer zahteva ravno to. Velja še poudariti, da se argumenti funkcij delovnega zvezka ločujejo s podpičjem, medtem ko se v jeziku VBA ločujejo z vejicami.

Vrstica makra, kjer prirejamo vrednost spremenljivki ImeDat, je zdaj videti takole: ImeDat = ImeMape & Format(Date, "yyyy_mm_dd") & "-" & Mid(Cells(1, 1).Value, 13) & ".xls". Na tem mestu bi lahko zaključili, a smo nakopali težave z željo, da v imenu datoteke med imenom in priimkom prodajalca ni presledka. Da bo makro nazornejši, uvedemo novo spremenljivko Prod in ji priredimo ustrezen del vrednosti celice A1 (Prod = Mid(Cells(1, 1).Value, 13). Da pa ne izgubimo rdeče niti, kako zgleda, po uvedbi nove spremenljivke, naš makro Shrani, ga zapišimo:

Sub Shrani()
Const ImeMape = "C:\Temp\"
Dim ImeDat As String
Dim Prod As String
Prod = Mid(Cells(1, 1).Value, 13)
ImeDat = ImeMape & Format(Date, "yyyy_mm_dd") & "-" & ImeProd & ".xls"
ActiveWorkbook.SaveAs (ImeDat)
End Sub

Za iskanje določenega znaka, ali tudi podniza znotraj besednega niza uporabimo funkcijo InStr. Med funkcijami delovnega zvezka jo bomo zaman iskali, čeprav sta na voljo dve, katerih delovanje lahko enačimo s funkcijo InStr. To sta funkciji Find in Search. Zgradba funkcije InStr je naslednja: InStr([pričetek,] besedni_niz1, besedni_niz2 [, primerjava]). Prvi in zadnji argument nista obvezna in se redko uporabljata. Argument pričetek uporabimo, kadar ne želimo preiskati celotnega besednega niza. Z njim določimo mesto v besednem nizu, od katerega naprej preiskujemo. Zadnji argument določa način primerjanja: binarno ali tekstualno. Argument besedni_niz1 opredeli besedilo, ki bo preiskovano. V našem primeru bo to spremenljivka Prod. Da iščemo presledek, pa bomo določili z argumentom besedni_niz2. Mesto v spremenljivki Prod, kjer se nahaja presledek, dobimo, če zapišemo: Instr(Prod," ").

Ime prodajalca je zapisano v spremenljivki Prod od prvega znaka do presledka: Mid(Prod, 1, InStr(Prod, " ") - 1). Priimek pa od presledka do konca znakov v spremenljivki Prod: Mid(Prod, InStr(Prod, " ") + 1).

Zaključimo naš primer z zapisom celotnega makra. Makro Shrani, shrani aktualen delovni zvezek pod izbranim imenom. Izbrano ime se začne z datumom zapisanim v obliki yyyy_mm_dd, sledi vezaj in nato ime in priimek osebe zapisane v celici A1, pri čemer se (en!) presledek med imenom in priimkom izloči.

Sub Shrani()
Const ImeMape = "C:\Temp\"
Dim ImeDat As String
Dim Prod As String
Prod = Mid(Cells(1, 1).Value, 13)
ImeDat = ImeMape & Format(Date, "yyyy_mm_dd") & "-" _
& Mid(Prod, 1, InStr(Prod, " ") - 1) _
& Mid(Prod, InStr(Prod, " ") + 1) & ".xls"
ActiveWorkbook.SaveAs (ImeDat)
End Sub

ŠE NEKATERE FUNKCIJE ZA DELO Z BESEDILI

Do zdaj smo spoznali tri funkcije za delo z besedili: &, Mid in InStr. Predstavimo še nekaj pogosto uporabljenih.

Left(besedni_niz, dolzina), Right(besedni_niz, dolzina).
Funkciji vrneta prvih nekaj oziroma zadnjih nekaj znakov iz besednega niza. Koliko znakov vrneta, določa argument dolzina. Funkcija Left je v bistvu okrajšava funkcije Mid, s katero bi dobili enak rezultat, če bi zapisali Mid (besedni_niz, 1, dolzina)

Len(besedni_niz/spremenljivka)
Z uporabo funkcije ugotovimo, koliko znakov vsebuje besedni niz oziroma spremenljivka.

LCase(besedni_niz), UCase(besedni_niz)
Pogosto ni vseeno, ali je neko besedilo zapisano z malimi ali velikimi črkami (na primer pri geslih). Funkcija LCase nam vse črke iz besednega niza spremeni v male (če so že prej bile male, male ostanejo), funkcija UCase pa črke iz besednega niza spremeni v velike (če so že prej bile velike, velike ostanejo).

Trim (besedni_niz)
Pogosta težava uporabnikov je, da celice vsebujejo poleg vrednosti še nekaj presledkov pred ali za vrednostjo. Če celica A1 vsebuje vrednost "December ", uporabnik predvideva, da je vrednost celice "December". Težava nastopi, ko vrednost celice A1 vrednotimo z eno od številnih funkcij delovnega zvezka. Takrat "December" ni enako "December " in funkcije ne dajejo pričakovanih rezultatov. Funkcija Trim odstrani presledke pred besedilnim nizom in za njim, a, za razliko od istoimenske funkcije delovnega zvezka, presledke med več nizi pusti nedotaknjene.

Končajmo članek s še enim primerom makra. Tokrat bo naša naloga na videz enostavna. Izpisati želimo sporočilo, ki vsebuje dva stavka. Če je jedro makra: MsgBox ("Datoteka ni na voljo! Pokličite interno 999.") , bo Excel izpisal sporočilo, kot ga prikazuje spodnja slika.

Uporabniku pa želimo prikazati sporočilo, kjer bosta stavka vsak v svoji vrstici. Poenostavljeno povedano: med stavka želimo vriniti "Enter". Tipke Enter pa ne moremo vnesti v besedilo. Zato potrebujemo posebno funkcijo Chr(koda). Zopet je med funkcijami delovnega zvezka funkcija z enako funkcionalnostjo, a nekoliko drugačnim imenom: Char(koda).

Vse črke, števke, ločila in nekateri posebni znaki imajo v standardu ASCII ustrezno kodo. Funkcija Asc(znak) vrne ustrezno kodo, a nam bo tokrat priskočila na pomoč funkcija Chr, ki dela ravno nasprotno. Na spletu je mnogo strani, kjer so predstavljene t.i. ASCII kode, zato nam ne bo težko poiskati, da je "Enter" (carriage return) predstavljen s kodo 13. Prikažimo uporabo funkcije Chr z makrom PrelomiBesedilo():

Sub PrelomiBesedilo()
MsgBox ("Datoteka ni na voljo!" & Chr(13) & "Pokličite interno 999.")
End Sub

Samo Rubin