Kalendere i Excel med VBA
Man kan meget med makroer (VBA) i Excel, og jeg har moret mig med at lave et program, der kan lave kalendere med forskelligt udseende. En ny kalender placeres i et nyt regneark.
Det bruger flere af de værktøjer, der er til rådighed i VBA, men er ikke hyper indviklet, og regnearket med makroerne kan downloades her.
I det følgende skriver jeg om, hvad programmet kan, viser screenshots med kalender-layouts og forklarer programmets struktur, så det er nemmere at forstå og læse makroerne.
Helligdage
Brugeren skal vælge årstal, og så beregnes placeringen af påskedag og de deraf afledte helligdage. Det er skærtorsdag, langfredag, Kristi himmelfart osv.
Programmet kan "kun" beregne påskens placering (gregoriansk kalender) for årene 1900 - 2299, men mon ikke det rækker foreløbig?
Derudover er der 10 helligdage på faste datoer (fx juledag). Det er fortrinsvis kristne helligdage. Jeg har ikke styr på, hvad der er af helligdage i andre religioner og kulturer, men du kan definere op til 30 hellig- eller mærkedage efter eget valg.
Det kan være ramadan, fødselsdage, bryllupsdag, Slaget på Reden eller hvad ved jeg - der er frit slag, og 30 skulle dække de flestes behov.
Du kan frit vælge, om hver enkelt helligdags navn/tekst skal skrives i kalenderen, og om den skal markeres med fyldfarve. Fyldfarven vælger du selv med Excels indbyggede farvevælger.
Du kan på nul komma fem lave en kalender for dit fødselsår og se, på hvilken ugedag du blev født, eller du kan se, om julen i år XXXX bliver en arbejdsgiverjul eller ej, og hvornår påsken falder.
Koden læser brugerens systemindstillinger for at tage højde for datoformat (fx om dag er placeret før eller efter måned), første dag i ugen, ugenumre med mere. Brugerens standardsprog afgør navne på dage og måneder.
Det skulle med andre ord være ligegyldigt, hvor du er i verden, og hvilket sprog du taler - håber jeg da!
Der er for meget kode, til at jeg kan vise den her, men du kan som sagt downloade et regneark med det hele. Regnearket er zip-komprimeret, og kommentarerne er på engelsk.
Herunder viser jeg eksempler på, hvordan de forskellige kalendere ser ud, og jeg beskriver kodens opbygning - den er slet ikke så indviklet, som du måske tror.
Kalendertyper
Den første type er den velkendte med to "sider" med 6 måneder på hver. Det er typisk en lignende kalender, man kan downloade i PDF-format på nettet, men uden mulighed for egne mærkedage. Herunder et udsnit i formindsket størrelse.
Du kan lave en kalender, hvor dagene er "bokse", og hvor der er 7 på række i X rækker for hver måned (12 faneblade). Den viser jeg ikke noget screenshot af, men herunder ser du en (formindsket) kalender med rækker til dage og et navngivet faneblad for hver måned.
Den er velegnet til fx korte dagbogsnotater eller lignende, og tekstfeltet har tekstombrydning, så rækkehøjden automatisk justeres, hvis du skriver mere end 1 linje. Også her er der et faneblad til hver måned.
Endelig er der en kalender med kolonner til dage og et faneblad for hver måned. Antallet af rækker er brugerdefineret, eller man kan vælge 24 og få timetal på.
Jeg har brugt en lignende kalender på jobbet til at holde styr på timeforbrug, afspadsering o.l.
Kalenderprogrammet
Det meste af koden er skrevet i klassemoduler, hvoraf nogle er klassesamlinger (class collections), og inden du surfer skrigende bort, skal du vide, at opbygningen med klasser gør det meget mere overskueligt og enkelt, end det ellers ville være.
Når man som her har at gøre med 12 måneder og 360 + x dage, er det helt oplagt at bruge klasser og class collections. Så skal man kun skrive kode for én måned-klasse og for én dag-klasse.
Klasserne er så at sige småkageforme, og i en collection kan man have lige så mange småkager, man ønsker.
Det er helt analogt med den måde, som Excel og mange andre programmer er skruet sammen på. I Excel har du fx Workbook-objektet. Det rummer en collection af Worksheets, som igen indeholder flere collections ned til "elementarpartiklen", cellen, der også har forskellige properties.
En klasse, man selv definerer, kan man forsyne med properties, egenskaber, som egentlig svarer til variable, men som man kun behøver definere én gang, fordi de er knyttet til ("indkapslet" i) den enkelte klasse.
Således har en dag-klasse i kalenderprogrammet fx en property, bHoliday, som sættes til True/False, alt efter om det er en helligdag eller ej.
I andre sammenhænge kan klassestrukturen give en indbygget sikkerhed, netop fordi de forskellige properties er indkapslet i de enkelte klasser, hvor man så kan styre, hvem der har ret til at læse/skrive.
Opbygningen med klasser
Klasserne og klassesamlingerne ligger inden i hinanden som kinesiske æsker. Det er meget struktureret og gør det nemt at navigere og lokalisere præcis den måned eller dag, man skal have fat i.
Øverst i hierarkiet har vi klassen "Calendar", som repræsenterer det år, brugeren vælger. (Langt hovedparten af mine læsere læser de engelske sider, så jeg bruger engelsk til navne og kommentarer.)
Oven over calendar-klassen kunne man sagtens have en class collection, hvor calendarklassen så var en af flere, så den nye topklasse var en collection med flere år.
Det har jeg ikke lavet, men det ville ikke tage lang tid at programmere, i og med calendar-klassen allerede findes som et påskeæg med fyld.
Calendar-klassen indeholder langt de fleste af procedurerne og funktionerne, som bruges til at lave kalenderne. Den har en håndfuld properties, og én holder fx styr på dagens placering i en dato afhængigt af brugerens systemindstillinger.
Calendar-klassen er samtidig en class collection af clMonth-klasser, og det overrasker nok ingen, at der er 12 clMonth-klasser i samlingen, og at rækkefølgen i samlingen starter med januar og slutter med december.
Det er også praktisk, når man skal gennemløbe samlingen med en løkke, fx:
Så har man med lM hele tiden styr på, hvilken måned (1 - 12) der behandles. Da Calendar-klassen er en class collection, kan man finde/adressere fx marts måned ved at skrive:
Calendar.Item(3)
eller
Me.Item(3)
hvis det er kode i Calendar-klassen selv.
Hver af de 12 månedsklasser er en class collection af clDay-klasser - samme antal som er i den pågældende måned. Hver dagklasses nummer i samlingen svarer til datoen, og man kan konstruere hele datoen med dagens nummer, "ejer-månedens" nummer og årstallet. Datoen er naturligvis en af dagklassens properties.
Da hver måned er en class collection af dag-klasser, kan man altså adressere fx 4. marts med:
Calendar.Item(3).Item(4)
Og man kan definere, at den 25. december er en helligdag ved at skrive:
Calendar.Item(12).Item(25).bHoliday = True
Calendar-klassen har ud over klassesamlingen med måneder et andet klasseobjekt under sig, nemlig clHolidays.
clHolidays er en class collection af clHoliday-klasser. Hver clHoliday i samlingen repræsenterer en helligdag (eller mærkedag).
Brugeren definerer via en userform navnene på de enkelte helligdage, om navnet skal skrives, og om dagen skal have fyldfarve. Disse indstillinger gemmes i hver helligdags properties, så de kan læses, når kalenderen skal laves.
Første gang programmet kører, er "Mine indstillinger" ikke gemt, så programmet tjekker brugerens landekode i systemindstillingerne.
Hvis den matcher en af de få prædefinerede (Danmark, Nordamerika, Tyskland, Frankrig, Italien og Spanien), indlæses de prædefinerede helligdage, og ellers bruges Nordamerika som default, indtil brugeren har gemt sine egne indstillinger.
Userformen
Som sagt er der en userform (se screenshot nær toppen af siden), hvor brugeren kan redigere sine indstillinger for helligdage.
Jeg vil ikke gøre så meget ud af den her, du kan selv studere koden. Jeg vil dog gøre opmærksom på det smarte i at putte de forskellige typer af kontrolelementer (labels, textboxe, checkboxe) i forskellige frames.
Når userformen starter, gennemløbes de forskellige frames, og de forskellige typer af kontrolelementer puttes i collections, så rækkefølgen matcher helligdagenes rækkefølge i clHolidays.
Når brugeren så klikker "Save", skal man ikke gennemløbe kontrolelementerne på userformen, men i stedet - og meget nemmere - gennemløber man de forskellige collections med kontrolelementerne.
Da rækkefølgen matcher rækkefølgen af helligdage i vores class collection, er det pærelet at vise de gamle indstillinger samt gemme brugerens nye indstillinger til videre brug.
Den teknik vil jeg måske skrive en særlig side om, men nu kan du selv studere den i formularens kode.
Brug koden, som du vil, og til hvad du vil. God fornøjelse!
Rettelse 11. januar 2021:
Kalenderen med 6 måneder per side fik ikke altid de rigtige ugenumre, dvs. ugenumrene som bruges af systemet jævnfør brugerens systemindstillinger.
Relateret:
|