VBカレンダー

VB SampleVB-Calendar
 VBでシンプルなカレンダーをつくってみます。概観は右図のようなもので、リンクラベルの Prev をクリックすると前月へ、Next をクリックすると翌月を表示します。また、 Reset をクリックすると現在の月を表示します。
 今回のサンプルでは、日付表示にラベルを使用しています。コーディングは量も少なく、むずかしいロジックもありませんので、VBを始めたばかりの人も容易につくれるのではないかと思います。
 [ INDEX ]
 ・フォームの設計
 ・初期化処理
 ・カレンダーの表示
 ・月の移動
 (作成環境 : Visual Basic .net 2002 / Framework SDK 1.0)

●フォームの設計
 まずMainFormをつくり、フォーム上に三つのパネルを配置します。
 ・PN_CAP ... 年月の表示と月移動ボタンを持つ
 ・PN_HEAD ... 曜日を表示
 ・PN_BODY ... 日付を表示

 PN_CAP上には一つのラベルと三つのリンクラベルを配置します。
 ・LB_CAP ... 年月を表示
 ・LNK_PREV ... 表示を前月へ移動させる
 ・LNK_NEXT ... 表示を翌月へ移動させる
 ・LNK_RESET ... 表示を今月に戻す
 さらに、PN_HEAD上に7つの曜日ラベル、PN_BODY上に37個の日付ラベルを配置しますが、これらは、フォームがロードされる時に動的に作成して追加します。
 各コントロールの主なプロパティ値は以下の通りです。
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' のクリックで今月を表示する
| ▲TOP |

●初期化処理
 まず、グローバル変数(内部変数)を二つつくります。日付ラベルの配列である aDayLabel と、現在表示中の年月を格納しておくための dtCurrent です。aDayLabel は、ラベルの特定をする際、一方の dtCurrentは月を移動させる時に参照しますので必要となります。
 ユーザー初期化は、OnLoad イベントで行っています。曜日ラベル(7個)と日付ラベル(37個)を作成してパネルに追加します(図参照)。すべての日付は、この37個の範囲内に収まります。
 最後に、今月のカレンダーを表示するために、SetCalendar 関数(次節参照)を呼び出しています。
' フォームデザイナのコードは省略しています

' 変数
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 関数を定義して行います。この関数は表示月の年と月を引数に持ちます。手順としては、表示月の日数を求めてから、日付ラベルを配置するわけですが、この時、配置位置を特定するため、月初の曜日が必要となります。ある日付から曜日を求めるには Weekday 関数を利用します。この関数は 日曜=1, 月曜=2, 土曜=6 というように整数型を返します。ただし、下記では、配置のインデックスが 0 から始まる都合上、1 を引いて使用しています。
' 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 クラスで、これらの値を求めることができます。
 Dim gcal As New System.Globalization.GregorianCalendar()    ' グレゴリオ暦
 Dim nDays As Integer = gcal.GetDaysInMonth(Year(Now), Month(Now))  ' 月の日数の取得
| ▲TOP |

●月の移動
 月の移動は、SetCalendar 関数に表示したい年月を引数に指定して呼び出すだけです。ここでは、月の増減に DateTime 構造体の AddMonths メソッドを利用しています。DateTime 構造体は、Date データ型(日付型)に対応する構造体です。この他にも、年の増減に使用する AddYears, 日の増減に使用する AddDays などのメソッドがあります。
 ちなみに、DateTime 構造体で表現できる値の範囲は、C.E. 0001 年 1 月 1 日の 00:00:00 から C.E. 9999 年 12 月 31 日の 23:59:59 までとなります。(C.E. = 紀元)
' 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
 なお、今回は日付を表示するだけのものですが、祝祭日の表示や画像の付加、メモの管理機能など、あとひと工夫で簡単に追加することができます。興味のある方は、いろいろアレンジしてみてください。

| ■HOME | ◆プログラムTop | ▲ページの先頭 |