VBでシンプルなカレンダーをつくってみます。概観は右図のようなもので、リンクラベルの Prev をクリックすると前月へ、Next をクリックすると翌月を表示します。また、 Reset をクリックすると現在の月を表示します。
まずMainFormをつくり、フォーム上に三つのパネルを配置します。|
MainForm ... MaximizeBox : False ... MinimmizeBox : False ... Size : 256, 256 ... FormBorderStyle : FixedSingle ... Text : VBCalendar PN_CAP(Panel) ... Dock : Top ... Size : 248, 32 PN_HEAD(Panel) ... Dock : Top ... Size : 248, 24 ... BorderStyle : Fixed3D PN_BODY(Panel) ... Dock : Fill ... BorderStyle : Fixed3D LB_CAP(Label) ... Dock : Fill ... Font : Arial, 16px, style=Bold ... TextAlign : MiddleLeft |
LNK_PREV(LinkLabel) ... Dock : Right ... Size : 40, 32 ... Font : MS UI Gothic, 12px ... LinkColor : Green ... Text : Prev ... TextAlign : MiddleCenter LNK_NEXT(LinkLabel) ... Dock : Right ... Size : 40, 32 ... Font : MS UI Gothic, 12px ... LinkColor : Green ... Text : Next ... TextAlign : MiddleCenter LNK_RESET(LinkLabel) ... Dock : Right ... Size : 48, 32 ... Font : MS UI Gothic, 12px ... LinkColor : Green ... Text : Reset ... TextAlign : MiddleCenter |
| 関数名 | 種類 | 機能 |
|---|---|---|
| SetCalendar | ユーザー定義 | 指定された年と月からカレンダーを作成する |
| OnLoad | イベント | フォームのロード時に週と日付ラベルを作成・追加する |
| LNK_PREV_LinkClicked | イベント | 'Prev' のクリックで前月を表示する |
| LNK_NEXT_LinkClicked | イベント | 'Next' のクリックで翌月を表示する |
| LNK_RESET_LinkClicked | イベント | 'Reset' のクリックで今月を表示する |
まず、グローバル変数(内部変数)を二つつくります。日付ラベルの配列である aDayLabel と、現在表示中の年月を格納しておくための dtCurrent です。aDayLabel は、ラベルの特定をする際、一方の dtCurrentは月を移動させる時に参照しますので必要となります。' フォームデザイナのコードは省略しています
' 変数
Private aDayLabel As Label() ' 日付ラベルの配列
Private dtCurrent As Date ' 現在表示中の年月を示すDateオブジェクト
' フォームのロードイベント
Protected Overrides Sub OnLoad(ByVal e As System.EventArgs)
Dim i As Integer
' 曜日ラベルの作成と配置
Dim aWeekDayName As String() = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}
For i = 0 To 6
Dim lb As New Label()
With lb
.Width = CInt(Math.Round(PN_HEAD.ClientSize.Width / 7))
.Height = CInt(Math.Round(PN_HEAD.ClientSize.Height))
.Left = (i Mod 7) * .Width
.Top = 0
.Font = New Font("Times", 12, FontStyle.Bold, GraphicsUnit.Pixel)
.Text = aWeekDayName(i)
.TextAlign = ContentAlignment.MiddleCenter
If i = 0 Then .ForeColor = Color.FromArgb(255, 204, 0, 0) ' 日曜は赤
If i = 6 Then .ForeColor = Color.FromArgb(255, 0, 0, 255) ' 土曜は青
End With
PN_HEAD.Controls.Add(lb) ' パネルに曜日ラベルを追加
Next
' 日付ラベルの作成と配置
ReDim aDayLabel(36)
For i = 0 To 36
aDayLabel(i) = New Label()
With aDayLabel(i)
.Width = CInt(Math.Round(PN_BODY.ClientSize.Width / 7))
.Height = CInt(Math.Round(PN_BODY.ClientSize.Height / 6))
.Left = (i Mod 7) * .Width
.Top = (i \ 7) * .Height
.Font = New Font("Arial", 15, FontStyle.Bold, GraphicsUnit.Pixel)
.TextAlign = ContentAlignment.MiddleCenter
If i Mod 7 = 0 Then .ForeColor = Color.FromArgb(255, 204, 0, 0) ' 日曜
If i Mod 7 = 6 Then .ForeColor = Color.FromArgb(255, 0, 0, 255) ' 土曜
End With
Next
PN_BODY.Controls.AddRange(aDayLabel) ' パネルに日付ラベルを追加
' 今月のカレンダーの表示
SetCalendar(Year(Now), Month(Now))
End Sub
' SetCalendar : カレンダーの作成
' ... 引数 y : 表示年 / 引数 m : 表示月 / 戻り値なし
Private Sub SetCalendar(ByVal y As Integer, ByVal m As Integer)
' 現在の行数(初期値6行)
Static currRows As Integer = 6
' 表示月の日数の取得(2月の場合はうるう年の判断をする)
Dim aDays As Integer() = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} ' 月毎の日数
Dim nDays As Integer = aDays(m - 1) ' 表示月の日数
If m = 2 Then
If y Mod 4 = 0 Then nDays = 29 ' 年が 4で割り切れればうるう年
If y Mod 100 = 0 Then nDays = 28 ' ただし、100で割り切れれば通常年
If y Mod 400 = 0 Then nDays = 29 ' ただし、400で割り切れればうるう年
End If
' 日付ラベルの更新
dtCurrent = New Date(y, m, 1) ' 変数を更新
Dim nWeek As Integer = Weekday(dtCurrent) - 1 ' 月初の曜日(日曜=0, 月曜=1 ... 土曜=6)
Dim i As Integer
For i = 0 To 36
If i >= nWeek AndAlso i < nDays + nWeek Then
aDayLabel(i).Text = (i - nWeek + 1).ToString ' 表示文字列
aDayLabel(i).Visible = True ' 可視化
Else
aDayLabel(i).Visible = False ' 不可視化
End If
Next
' 行数を比較してフォームの高さをカレンダーに合わせる
Dim nRows As Integer = CInt(Math.Ceiling((nDays + nWeek) / 7)) ' 行数の算出
Me.Height += (nRows - currRows) * aDayLabel(0).Height ' フォームの高さを調整
currRows = nRows ' 現在の行数を格納
LB_CAP.Text = " " & y.ToString & " - " & m.ToString ' 見出し(年月表示)を更新
End Sub
* 月の日数の算出やうるう年の判断は、今回は自前のコードで処理していますが、System.Globalization 名前空間には、Calendar クラス(抽象クラス)があり、暦の種類や地域ごとに派生クラスが用意されています。実際には、派生クラスである GregorianCalendar クラスやJapaneseCalendar クラスで、これらの値を求めることができます。
' Prevボタンのクリック : 前月へ移動
Private Sub LNK_PREV_LinkClicked(ByVal sender As Object, _
ByVal e As LinkLabelLinkClickedEventArgs) Handles LNK_PREV.LinkClicked
Dim d As Date = dtCurrent.AddMonths(-1)
SetCalendar(Year(d), Month(d))
End Sub
' Nextボタンのクリック : 翌月へ移動
Private Sub LNK_NEXT_LinkClicked(ByVal sender As Object, _
ByVal e As LinkLabelLinkClickedEventArgs) Handles LNK_NEXT.LinkClicked
Dim d As Date = dtCurrent.AddMonths(1)
SetCalendar(Year(d), Month(d))
End Sub
' Resetボタンのクリック : 今月へ移動
Private Sub LNK_RESET_LinkClicked(ByVal sender As Object, _
ByVal e As LinkLabelLinkClickedEventArgs) Handles LNK_RESET.LinkClicked
SetCalendar(Year(Now), Month(Now))
End Sub
なお、今回は日付を表示するだけのものですが、祝祭日の表示や画像の付加、メモの管理機能など、あとひと工夫で簡単に追加することができます。興味のある方は、いろいろアレンジしてみてください。