VB.NET Kalender erstellen – Teil 1
Inhaltsverzeichnis
- 1 VB.NET Kalender erstellen – Teil 1
- 2 VB.NET Kalender erstellen
- 3 Calendar-Klasse – VB.NET Kalender erstellen
- 4 Entry-Klasse – VB.NET Kalender erstellen
- 5 RowHeader-Klasse – VB.NET Kalender erstellen
- 6 Day darstellen
- 7 Weiter zu Teil 2 – VB.NET Kalender erstellen
VB.NET Kalender erstellen – Teil 1
Lerne in dem ersten Beitrag meiner zweiteiligen Reihe, wie Du deinen eigenen VB.NET Kalender erstellen kannst, sogar mit Terminen!
Ich habe das Thema Terminkalender, bzw. einen eigenen Kalender in VB.NET zu erstellen schonmal in einem ehemaligen Beitrag über den BackgroundWorker angesprochen.
Statt einem BackgroundWorker, oder einem Timer, werden wir in diesem Beispiel die moderne Async/Await-Variante verwenden.
Zweiter Teil!?
Du bist auf der Suche nach dem zweiten Teil – hier wirst Du fündig!
Vorwort
Ich würde Dich bitten, dass Du mich auf dir auffallende Fehler durch ein Kommentar hinweist.
Da es sich hierbei natürlich um einen etwas größeren Beitrag handelt, kann es leicht vorkommen, dass ich mal was vergesse.
Ich habe mir bei der Qualität des Codes viel Mühe gegeben, allerdings wird es immer vorkommen, dass jemand etwas anders macht, oder meint besser zu machen.
Es steht Dir daher selbstverständlich frei, den Code nach deinen Belieben anzupassen, bzw. zu modifizieren.
Bei meiner Planung und Durchführung des Beitrages habe ich mir natürlich auch irgendwo Gedanken über meine Zeit gemacht, daher musste ich hier und da Abstriche machen.
Ich denke aber, dass dieser Beitrag sehr gelungen ist und vielen Anfängern und auch Fortgeschrittenen VB.NET Entwicklern bei der Erstellung eines eigenen VB.NET Kalenders helfen kann.
Fragen – VB.NET Kalender erstellen
- Wie möchten wir den Kalender allgemein darstellen?
- Möchten wir verschiedene Ansichten wie Monats-, Wochen- und Tagesansicht?
- Auf welche Art, sollen die Termine dargestellt werden?
- Soll der Kalender auch eine Art Erinnerungsfunktion haben?
- Können Termine gespeichert werden und wenn ja wie?
- Sollen die Bestandteile responsiv sein?
- Wie komplex soll das Ganze werden?
- und und und..
Eigene Termine
Da jeder vermutlich die Termine mit anderen Daten versehen wird, habe ich hier bewusst ein Interface erstellt, Welches nur die Basis-Funktionalität eines Termins widerspiegelt.
Jeder kann somit seine eigene Termin (Appointment)-Klasse erstellen und mit weiteren Daten befüllen.
Dazu kann man entweder von der Appointment–Klasse erben, direkt darin weiter schreiben, oder eben seine eigene Klasse erstellen, die dann das IAppointment–Interface implementiert.
Aus den meisten Kalendern kennt man es, dass links außen so ein Kreis für z. B. die Priorität dargestellt wird.
Ebenso haben alle Termine ein Startdatum inkl. Uhrzeit und ein Enddatum plus Uhrzeit.
IAppointment
Public Interface IAppointment Property Circle As Color Property Start As Date Property [End] As Date Property Subject As String End Interface
Appointment
Public Class Appointment Implements IAppointment Public Property Circle As Color Implements IAppointment.Circle Public Property Start As Date Implements IAppointment.Start Public Property [End] As Date Implements IAppointment.End Public Property Subject As String Implements IAppointment.Subject Sub New() Circle = Color.Red Start = Date.Now [End] = Date.Now.AddHours(1) Subject = "Neuer Termin" End Sub End Class
Erweiterungsmethoden – VB.NET Kalender erstellen
Da wir in dem Beispiel ganz häufig auf irgendwelche Datums–Funktionen zurückgreifen werden müssen, die es vom .NET Framework (jedenfalls mir nicht bekannt) nicht gibt, bauen wir uns unsere eigenen Helfer.
Imports System.Globalization Imports System.Runtime.CompilerServices Public Module modDateExtensions Public Enum Days Monday = 1 Tuesday = 2 Wednesday = 3 Thursday = 4 Friday = 5 Saturday = 6 Sunday = 7 End Enum <Extension> Public Function FirstDayOfMonth(dt As Date) As Date Return New Date(dt.Year, dt.Month, 1, dt.Hour, dt.Month, dt.Second, dt.Millisecond) End Function <Extension> Public Function LastDayOfMonth(dt As Date) As Date Dim nextMonth As Integer If dt.Month = 12 Then nextMonth = 1 Else nextMonth = dt.Month + 1 End If Dim firstOfNextMonth = New Date(dt.Year, nextMonth, 1, dt.Hour, dt.Month, dt.Second, dt.Millisecond) Return firstOfNextMonth.AddDays(-1) End Function <Extension> Public Function CalendarWeek(dt As Date) As Integer Dim day = CultureInfo.InvariantCulture.Calendar.GetDayOfWeek(dt) If day >= DayOfWeek.Monday AndAlso day <= DayOfWeek.Wednesday Then dt = dt.AddDays(3) End If Return CultureInfo.InvariantCulture.Calendar.GetWeekOfYear(dt, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday) End Function <Extension> Public Function GetPreviousDay(dt As Date, day As Days) As Date Dim targetDate = dt Do targetDate = targetDate.AddDays(-1) Loop While Weekday(targetDate, FirstDayOfWeek.Monday) > day Return targetDate End Function <Extension> Public Function GetNextDay(dt As Date, day As Days) As Date Dim targetDate = dt Do targetDate = targetDate.AddDays(1) Loop While Weekday(targetDate, FirstDayOfWeek.Monday) < day Return targetDate End Function <Extension> Public Function Range(from As Date, [to] As Date, Optional inclusive As Boolean = True) As List(Of Date) Dim days = New List(Of Date) If inclusive Then Dim CurrentDate = from While CurrentDate <= [to] days.Add(CurrentDate) CurrentDate = CurrentDate.AddDays(1) End While Else Dim CurrentDate = from.AddDays(1) While CurrentDate < [to] days.Add(CurrentDate) CurrentDate = CurrentDate.AddDays(1) End While End If Return days End Function End Module
Wenn Dir bessere Varianten einfallen, um die genannten Methoden zu verbessern, bist Du natürlich gerne eingeladen, den Code zu verbessern.
Mir reichen Diese Implementierungen allerdings so, da Sie mir so in den Kopf gekommen sind und ich sie einfach so runtergeschrieben habe.
FirstDayOfMonth(dt)
Diese Funktion dient dazu, den ersten Tag eines Monats zu bekommen.
LastDayOfMonth(dt)
Mit dieser Funktion kommen wir an den letzten Tag eines Monats.
CalendarWeek(dt)
Holt die Nummer der Kalenderwoche anhand eines gegebenen Datums.
GetPreviousDay(dt, day)
Gibt den letzten angegebenen Tag wieder, der anhand des Day-Parameters übergeben wurde.
GetNextDay(dt, day)
Errechnet den nächsten angegebenen Tag wieder, der durch den Day-Parameter übergeben wurde.
Range(from, to, inclusive)
Gibt eine Liste von Tagen die zwischen den beiden Daten liegen wieder (exklusiv, oder inklusiv).
VB.NET Kalender erstellen
Tipp!
Da wir in diesem Beispiel etwas zeichnungsintensivere Controls haben, solltest Du die „DoubleBuffered„-Eigenschaft der Form und auch ggf. der Sub-Elemente auf True stellen.
Los geht’s
Dann legen wir mit der Erstellung unseres Kalenders mal los, der erste Schritt wäre, ein Steuerelement zu erstellen.
Calendar-Klasse – VB.NET Kalender erstellen
Mache dazu einfach einen Rechtsklick rechts außen im Projektmappen–Explorer auf dein Projekt und dann Hinzufügen->Benutzersteuerelement (Windows Forms).
Alternativ kannst Du oben im Menü auf Projekt->Benutzersteuerelement hinzufügen (Windows Forms) klicken.
Da es sich bei unserem Vorhaben um einen Kalender handelt, nennen wir das Steuerelement – wer hätte es gedacht – Calendar (natürlich englisch, in der Welt der Programmierung).
Public Class Calendar Private _date As Date = Date.Now Public Property [Date] As Date Get Return _date End Get Set(value As Date) _date = value RefreshData() End Set End Property Private Property RowPanels As List(Of TableLayoutPanel) Public Property RowHeaders As List(Of RowHeader) Public Property Days As List(Of Day) Sub New() InitializeComponent() Dock = DockStyle.Fill RowPanels = New List(Of TableLayoutPanel) From { tlpRow1, tlpRow2, tlpRow3, tlpRow4, tlpRow5 } RowHeaders = New List(Of RowHeader) Days = New List(Of Day) BuildUp() End Sub Private Sub BuildUp() GenerateHeaderRow() GenerateRows() End Sub Private Sub GenerateHeaderRow() GeneratePlaceholderBox() GenerateDayLabels() End Sub Private Sub GeneratePlaceholderBox() Dim placeholder = New PictureBox() With placeholder .Dock = DockStyle.Fill .BackColor = Color.White .Margin = New Padding(0) End With tlpHeader.Controls.Add(placeholder) End Sub Private Sub GenerateDayLabels() Dim dayNames = GetDayNames() For Each dayName In dayNames Dim lbl = New Label() With lbl .Text = dayName.Substring(0, 2).ToUpper() .Dock = DockStyle.Fill .AutoSize = False .TextAlign = ContentAlignment.MiddleCenter .ForeColor = Color.Black .Font = New Font(New FontFamily("Segoe UI"), 12, FontStyle.Regular) End With tlpHeader.Controls.Add(lbl) Next End Sub Private Function GetDayNames() As List(Of String) Dim anyMonday = Date.Parse("05.07.2021") Dim nextSunday = anyMonday.AddDays(6) Dim week = anyMonday.Range(nextSunday) Dim dayNames = New List(Of String) For Each day In week Dim dayName = day.ToString("ddd") dayNames.Add(dayName) Next Return dayNames End Function Private Sub GenerateRows() Dim days = GetDateRange() Dim index = 0 Dim number = 1 For i = 1 To 5 Dim rowIndex = i - 1 Dim startOfWeek = days(index) ' Dim endOfWeek = days(index).AddDays(6) GenerateRowHeader(rowIndex, startOfWeek) For j = 1 To 7 Dim colIndex = j - 1 GenerateDay(days(index), rowIndex, colIndex, number) index += 1 number += 1 Next Next End Sub Private Sub GenerateRowHeader(rowIndex As Integer, startOfWeek As Date) Dim rowHeaderHasTopBorder = rowIndex > 0 Dim rowHeader = New RowHeader(startOfWeek.CalendarWeek(), rowHeaderHasTopBorder) RowPanels(rowIndex).Controls.Add(rowHeader) RowHeaders.Add(rowHeader) End Sub Private Sub GenerateDay(dt As Date, rowIndex As Integer, colIndex As Integer, number As Integer) Dim day = New Day(dt) With day .HasTopBorder = rowIndex > 0 .HasRightBorder = colIndex < 6 .HasBottomBorder = rowIndex < 4 .HasLeftBorder = number Mod 7 <> 0 End With RowPanels(rowIndex).Controls.Add(day) Days.Add(day) End Sub Private Sub RefreshData() ClearEntries() RefreshDateLabel() RefreshRowHeaders() RefreshDays() End Sub Public Sub ClearEntries() For Each day In Days day.ClearEntries() Next End Sub Private Sub RefreshDateLabel() lblDate.Text = [Date].ToString("MMMM yyyy") End Sub Private Sub RefreshRowHeaders() Dim dates = GetDateRange() For i = 0 To 4 RowHeaders(i).CalendarWeek = dates(i * 7).CalendarWeek() Next End Sub Private Sub RefreshDays() Dim dates = GetDateRange() For i = 0 To dates.Count - 1 Dim day = Days(i) Dim dt = dates(i) day.Date = dt Next End Sub Public Function GetDateRange() As List(Of Date) Dim lastMonday As Date If Weekday([Date], FirstDayOfWeek.Monday) = WeekDays.Monday Then lastMonday = [Date] Else lastMonday = [Date].GetPreviousDay(WeekDays.Monday) End If Dim range = lastMonday.Range(lastMonday.AddDays(34)) Return range End Function Public Sub GoToToday() [Date] = Date.Now End Sub Public Sub GoForward() [Date] = [Date].LastDayOfMonth().AddDays(1) End Sub Public Sub GoBack() [Date] = [Date].FirstDayOfMonth().AddDays(-1).FirstDayOfMonth() End Sub Private Sub btnBack_Click(sender As Object, e As EventArgs) Handles btnBack.Click GoBack() End Sub Private Sub btnForward_Click(sender As Object, e As EventArgs) Handles btnForward.Click GoForward() End Sub Private Sub btnToday_Click(sender As Object, e As EventArgs) Handles btnToday.Click GoToToday() End Sub Public Event EntryClicked As EventHandler Public Event EntryDoubleClicked As EventHandler End Class
Eigenschaften
Date
Die Date Eigenschaft speichert das aktuelle Datum des Kalenders zwischen.
Obwohl der Kalender ja praktisch in unserem Fall nur Monat + Jahr anzeigt und damit arbeitet, lässt Er sich so bei Bedarf erweitern, ohne großartig was am Flow ändern zu müssen.
Im Setter der Date-Eigenschaft triggern wir automatisch das „Refreshen“, also Aktualisieren der Daten.
RowPanels
Diese Eigenschaft dient mehr oder weniger als Zwischenspeicher, um einfacher auf die Rows zugreifen zu können.
RowHeaders
Ebenfalls eine Art Cache/Zwischenspeicher, um einfacheren Zugriff zu erhalten.
Days
Auch hier handelt es sich um einen Zwischenspeicher.
Konstruktor
Im Konstruktor der Calendar–Klasse lege ich die Dock-Eigenschaft auf Fill fest, damit der Kalender standardmäßig seinen Container füllt.
Dann Befülle ich den „Row–Cache„, da die Rows an sich ja schon zu Designer-Zeit existieren und instanziiere anschließend die weiteren Cache–Listen, die gleich noch befüllt werden.
Zum Schluss wird einmalig das Grundgerüst des Kalenders erstellt, damit wir das nicht bei jedem Datums-Wechsel neu durchlaufen müssen.
Es wird später aus Performance–Gründen nur das aktualisiert, was auch aktualisiert werden muss, bzw. so wenig wie möglich.
Steuerelemente
splcMain
Das erste und zuerst wichtigste Solo-Steuerelement was das Calendar-Control bekommen sollte ist vermutlich der SplitContainer, den wir splcMain nennen werden.
Diesen SplitContainer verwenden wird, um den Kalender in 2 Bereiche zu unterteilen: Den oberen Bereich, wo einzelne Navigations-Elemente hineinkommen.
Und als nächstes den zweiten Bereich, wo die Navigation und die einzelnen Tage responsiv dargestellt werden.
Dazu wird eine Kombination aus verschiedenen Panels verwendet.
btnToday
Mit diesem Button navigieren wir an den heutigen Tag.
btnBack
Dieser Button lässt den Kalender auf den vorherigen Monat springen.
btnForward
Der Button btnForward lässt den Kalender auf die nächste Seite, sprich zu dem nächsten Monat „blättern“.
lblDate
Dieses Label gibt uns eine Information darüber, auf welcher „Seite“ wir uns praktisch befinden, indem es den Namen des Monat und eine Jahreszahl anzeigt.
pbBottomBorder
Um einen kleinen Rand anzuzeigen, habe ich dafür eine PictureBox mit BackColor missbraucht.
Damit die späteren Beispiele allerdings „sauberer“ bleiben, bin ich dort im Paint-Event manuell ans Zeichnen gegangen..
Entry-Klasse – VB.NET Kalender erstellen
Die Entry-Klasse steht für einen jeweiligen Eintrag im Kalender-Tag.
Public Class Entry Public Property Appointment As IAppointment Sub New(appointment As IAppointment) InitializeComponent() Me.Appointment = appointment lblTime.Text = appointment.Start.ToString("HH:mm") lblSubject.Text = appointment.Subject End Sub Private Sub tlpMain_CellPaint(sender As Object, e As TableLayoutCellPaintEventArgs) Handles tlpMain.CellPaint If e.Column = 0 Then Using brush = New SolidBrush(Appointment.Circle) e.Graphics.FillEllipse(brush, e.CellBounds) End Using End If End Sub Private Sub Entry_Clicked(sender As Object, e As EventArgs) Handles lblSubject.Click, lblTime.Click, tlpMain.Click, Me.Click RaiseEvent Clicked(sender, e) End Sub Private Sub Entry_DoubleClicked(sender As Object, e As EventArgs) Handles lblSubject.DoubleClick, lblTime.DoubleClick, tlpMain.DoubleClick, Me.DoubleClick RaiseEvent DoubleClicked(sender, e) End Sub Public Event Clicked As EventHandler Public Event DoubleClicked As EventHandler End Class
Eigenschaften
Appointment
Der Termin der hinter dem letztendlichen Eintrag steht – Kann irgendeine Implementierung des IAppointment-Interface sein!
Konstruktor
new(appointment)
Der Konstruktor erwartet einen Termin als Parameter (Achtung, irgendeine Implementierung des IAppointment Interfaces!)
Nachdem der Termin gesetzt ist, werden die Texte der Labels angepasst.
Methoden
tlpMain_CellPaint
Hier zeichnen wir in der ersten Spalte des TableLayoutPanels einen Kreis in der für den Termin angegebenen Farbe.
Entries klickbar machen
Um die Entries richtig klickbar zu machen, müssen wir auf alle GUI-Elemente reagieren, also wenn Diese geklickt werden.
Dafür habe ich zwei Ereignishandler erstellt, die die Klick- und Doppelklick-Ereignisse der Elemente auf dem Entry abfangen und als eigenes Event „weiterleiten„.
Vorschau auf die Tage
Auch wenn der rote Kreis hier nicht so ganz meinen Vorstellungen entspricht, ich lasse es erstmal so.
Du bist herzlich eingeladen, den Kreis anhand deiner Wünsche anzupassen!
RowHeader-Klasse – VB.NET Kalender erstellen
Im folgenden Schritt werden wir die RowHeader-Klasse, bzw. das RowHeader-Steuerelement erstellen.
Es dient als Spacer für die Reihen links außen und stellt mit einem passenden Hintergrund die Kalenderwoche dar.
Public Class RowHeader Private _calendarWeek As Integer Public Property CalendarWeek As Integer Get Return _calendarWeek End Get Set(value As Integer) _calendarWeek = value lblCalendarWeek.Text = value.ToString() End Set End Property Public Property HasTopBorder As Boolean Sub New(calendarWeek As Integer, Optional hasTopBorder As Boolean = True) InitializeComponent() Dock = DockStyle.Fill Me.CalendarWeek = calendarWeek Me.HasTopBorder = hasTopBorder End Sub Private Sub RowHeader_Paint(sender As Object, e As PaintEventArgs) Handles Me.Paint If HasTopBorder Then DrawTopBorder(e.Graphics) End Sub Private Sub DrawTopBorder(graphics As Graphics) graphics.DrawLine(Pens.Black, New Point(0, 0), New Point(Width - 1, 0)) End Sub End Class
Eigenschaften
CalendarWeek
Die CalendarWeek-Eigenschaft speichert die Kalenderwoche als Zahl und updatet im Setter automatisch den zugehörigen Label–Text.
Konstruktor
new(calendarWeek, hasTopBorder)
Im Konstruktor braucht ein RowHeader die Kalenderwoche als Zahl und die Info, ob er den Top-Rand zeichnen soll, oder nicht.
Methoden
RowHeader_Paint
Wir verwenden einen Paint-Ereignishandler, um den Top–Rand falls aktiviert zu zeichnen.
DrawTopBorder
Das Zeichnen des Top-Randes selbst.
Day darstellen
Jetzt erstellen wir die Kacheln um ein jeweiligen Tag darzustellen.
Auch dafür nehmen wir ein neues Benutzersteuerelement (Windows Forms) und fangen an zu designen.
Public Class Day Private _date As Date Public Property [Date] As Date Get Return _date End Get Set(value As Date) _date = value lblDayNumber.Text = value.Day End Set End Property Public Property Entries As List(Of Entry) Public Property HasTopBorder As Boolean Public Property HasRightBorder As Boolean Public Property HasBottomBorder As Boolean Public Property HasLeftBorder As Boolean Private Property BorderPen As Pen Sub New([date] As Date) InitializeComponent() Dock = DockStyle.Fill BorderPen = New Pen(Color.LightGray) Me.[Date] = [date] Me.Entries = New List(Of Entry) End Sub Public Sub ClearEntries() For Each entry In tlpInner.Controls.OfType(Of Entry) RemoveHandler entry.Clicked, AddressOf AnEntryClicked RemoveHandler entry.DoubleClicked, AddressOf AnEntryDoubleClicked entry.Appointment = Nothing Next tlpInner.Controls.Clear() End Sub Public Sub AddEntry(entry As Entry) Entries.Add(entry) AddHandler entry.Clicked, AddressOf AnEntryClicked AddHandler entry.DoubleClicked, AddressOf AnEntryDoubleClicked End Sub Public Sub RemoveEntry(entry As Entry) RemoveHandler entry.Clicked, AddressOf AnEntryClicked RemoveHandler entry.DoubleClicked, AddressOf AnEntryDoubleClicked Entries.Remove(entry) End Sub Private Sub Day_Paint(sender As Object, e As PaintEventArgs) Handles Me.Paint DrawBorders(e.Graphics) End Sub Private Sub DrawBorders(graphics As Graphics) If HasTopBorder Then DrawTopBorder(graphics) If HasRightBorder Then DrawRightBorder(graphics) If HasBottomBorder Then DrawBottomBorder(graphics) If HasLeftBorder Then DrawLeftBorder(graphics) End Sub Private Sub DrawTopBorder(graphics As Graphics) graphics.DrawLine(BorderPen, New Point(0, 0), New Point(Width, 0)) End Sub Private Sub DrawRightBorder(graphics As Graphics) graphics.DrawLine(BorderPen, New Point(Width - 1, 0), New Point(Width - 1, Height)) End Sub Private Sub DrawBottomBorder(graphics As Graphics) graphics.DrawLine(BorderPen, New Point(0, Height - 1), New Point(Width, Height - 1)) End Sub Private Sub DrawLeftBorder(graphics As Graphics) graphics.DrawLine(BorderPen, New Point(0, 0), New Point(0, Height)) End Sub Private Sub AnEntryClicked(sender As Object, e As EventArgs) OnEntryClicked(EventArgs.Empty) End Sub Protected Sub OnEntryClicked(e As EventArgs) RaiseEvent EntryClicked(Me, e) End Sub Private Sub AnEntryDoubleClicked(sender As Object, e As EventArgs) OnEntryDoubleClicked(EventArgs.Empty) End Sub Protected Sub OnEntryDoubleClicked(e As EventArgs) RaiseEvent EntryDoubleClicked(Me, e) End Sub Private Sub DayClicked(sender As Object, e As EventArgs) Handles lblDayNumber.Click, tlpMain.Click, tlpInner.Click OnClicked(EventArgs.Empty) End Sub Protected Sub OnClicked(e As EventArgs) RaiseEvent Clicked(Me, e) End Sub Private Sub DayDoubleClicked(sender As Object, e As EventArgs) Handles lblDayNumber.DoubleClick, tlpMain.DoubleClick, tlpInner.DoubleClick OnDoubleClicked(EventArgs.Empty) End Sub Protected Sub OnDoubleClicked(e As EventArgs) RaiseEvent DoubleClicked(Me, e) End Sub Public Event EntryClicked As EventHandler Public Event EntryDoubleClicked As EventHandler Public Event Clicked As EventHandler Public Event DoubleClicked As EventHandler End Class
Ein jeweiliger Tag bekommt verschiedene Eigenschaften, die wir brauchen werden.
Eigenschaften
Date
Eine Date–Eigenschaft mit selbst implementierten Getter und Setter, damit wir beim setzen des Datums, den Text des Labels anpassen können.
Entries
Damit wir eine einfache und sinnvolle Möglichkeit haben, auf die einzelnen Einträge zugreifen zu können, statt via „tlpInner.Controls“ und ähnlichen Frickeleien.
Achtung: Mit Entries sollte nur via vorgesehenen Methoden gearbeitet werden (AddEntry, ClearEntries und RemoveEntry), sonst funktionieren die Handler nicht korrekt etc.
Border-Eigenschaften
Dann haben wir noch einzelne Border–Eigenschaften, Welche man auch mit Flags als Zahlen, etc. lösen könnte, aber ich denke das Beispiel ist schon für viele Anfänger kompliziert genug!
BorderPen
Der Pen, also die Farbe, mit dem der Rand gemalt wird (Standard: LightGray).
Methoden
ClearEntries
Diese Methode ist ein Helper um alle Entries zu entfernen, dabei iteriert Sie alle Entries und säubert die Termin-Referenz und entfernt die Handler.
Zum Schluss werden alle Entries aus dem „Cache“ und aus der Controls–Auflistung des tlpInner gelöscht.
AddEntry(entry)
Diese Methode dient dazu, einen Eintrag mit passendem Handler und Zwischenspeicher in der Entries Auflistung hinzuzufügen.
Dabei beachtet Sie die Reihenfolge, sprich die Uhrzeit des Entries.
RemoveEntry(entry)
Entfernt den angegeben Entry aus der Controls-Auflistung und dem „Cache„.
Säubert ebenfalls die Handler und die Termin-Referenz.
DrawBorders & Co.
Wie der Name schon sagt, ist diese Methode und dessen Sub–Methoden dafür zuständig, die Ränder bei Bedarf zu zeichnen.
Click-Handler
Ansonsten gibt es noch nach .NET-Manier einige Handler, um das Arbeiten mit dem „Day“ zu vereinfachen, bzw. intuitiver zu gestalten.
Weiter zu Teil 2 – VB.NET Kalender erstellen
Wenn Du den zweiten Teil des Beitrages suchst, kannst Du Diesen hier finden!