Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
学ぶ フォームコントロールの操作 | ユーザーインタラクション
ビジネス自動化のためのExcel VBA

フォームコントロールの操作

メニューを表示するにはスワイプしてください

TextBoxesComboBoxes はほとんどのデータ入力に対応していますが、他にも特定の選択肢をより自然に表現できるコントロールがいくつかあります。これらの値を正しく読み取るには、注意すべきポイントがいくつか存在します。

図 5.5

CheckBoxes

CheckBox の Value プロパティは単純なブール値で、チェックされていれば True、そうでなければ False です:

If chkWelcomeEmail.Value = True Then
    MsgBox "A welcome email would be sent here."
End If

フォーム上の各 CheckBox は他の CheckBox とは完全に独立しており、chkWelcomeEmail をチェックしても chkNewsletter には影響しません。選択肢が排他的でない場合に理想的な動作です。

OptionButtons

OptionButton は逆の動作をします。同じグループ内では、1つを選択すると他のすべてが自動的に選択解除されます。これは、顧客が同時に Active と Inactive にはなれないため、スクリーンショットの Status 選択肢に最適です。どれが選択されているかを確認するには、それぞれの Value を個別にチェックします:

Dim status As String
If optActive.Value = True Then
    status = "Active"
ElseIf optInactive.Value = True Then
    status = "Inactive"
ElseIf optProspect.Value = True Then
    status = "Prospect"
End If

排他性は自動的に VBA によって処理されるため、1つを選択したときに他の2つを解除するコードを書く必要はありません。ただし、グループ分けが重要です。OptionButton は同じコンテナ(フォーム本体や配置された Frame コントロール)内でのみ排他的に動作します。そのため、同じフォーム上に Status と Priority など2つの OptionButton グループがある場合は、それぞれ異なる Frame 内に配置する必要があります。そうしないと、VBA はすべてを1つの排他的グループとして扱います。

ListBoxes

ListBox は、Figure 5.5 の City リストのように、複数の選択肢を一度に表示し、クリックして開く必要がありません。ComboBox と同様に .AddItem で項目を追加し、選択内容は .Value または .ListIndex で取得します:

Private Sub UserForm_Initialize()
    lstCities.AddItem "Chicago"
    lstCities.AddItem "Boston"
    lstCities.AddItem "Austin"
End Sub
 
Private Sub cmdAssign_Click()
    If lstCities.ListIndex = -1 Then
        MsgBox "Please select a city first."
        Exit Sub
    End If
    MsgBox "Assigning to: " & lstCities.Value
End Sub

ListIndex は選択された項目の位置を示し、最初の項目が 0 から始まります。何も選択されていない場合は -1 となり、cmdAssign_Click で都市が選択されていない場合のチェックに利用されています。.Value は選択されたテキスト自体を返し、通常は数値の位置よりも便利です。

Multi-Page Forms

MultiPage コントロール(Figure 5.5 の上部にある Details / Preferences タブ)は、関連するコントロールを同じ物理フォーム上の別々のページにまとめます。フォームにフィールドが多すぎて1画面に収まらない場合に便利です。各ページは位置で指定され、現在表示されているページ上のコントロールのみがユーザーにとって意味を持ちますが、どのページが表示されていてもすべてのコントロールはコードから参照可能です:

Private Sub MultiPage1_Change()
    If MultiPage1.Value = 1 Then
        MsgBox "Now viewing Preferences"
    End If
End Sub

MultiPage1.Value は ListIndex と同じく 0 から始まるカウント方式で、最初のページが 0、2番目(Figure 5.5 の Preferences)が 1 です。Change イベントはユーザーが別のタブをクリックするたびに自動的に発生します。

タスク

  1. frmAddCustomer に Frame を追加し、その中に 3 つの OptionButton(optActive、optInactive、optProspect)を配置して、以前の cboStatus ComboBox を置き換え、上記の If...ElseIf ブロックを記述して選択内容を取得する。
  2. 2 つの独立した CheckBox(chkWelcomeEmail、chkNewsletter)を追加し、一方をチェックしても他方に影響しないことをテストで確認する。
  3. UserForm_Initialize で ListBox(lstCities)を作成し、何も選択されていない場合(ListIndex = -1)にエラーを表示するチェックを記述する。
  4. チャレンジ:Company Name、Contact、Email フィールドを「Details」という名前の MultiPage の 1 ページにまとめ、CheckBox と OptionButton を 2 ページ目「Preferences」に配置する。
ヒント
expand arrow

1. cboStatus を OptionButton の Frame で置き換える

  • フォームから cboStatus とそのラベルを削除 — 追加ではなく置き換え。
  • Frame コントロールをフォームにドラッグし、その枠内に 3 つの OptionButton をすべてドラッグして配置 — この囲い込みによって 1 グループとして排他的に動作。
  • 各 OptionButton の名前はプロパティウィンドウで個別に変更。Frame の Caption は「Status」などシンプルでよく、コードで参照しないなら名前は不要。
  • 選択内容の取得は、各 OptionButton の .ValueIf...ElseIf で個別に判定 — グループ内でどれが選択されているかを示す単一プロパティは存在しない。
  • cboStatus.Value はもう存在しないため、cmdSave_Click では OptionButton から status 変数を作成し、newRow に書き込む。

2. 2 つの独立した CheckBox を追加

  • これらはフォーム上に直接配置(Frame 内ではない) — CheckBox 同士が互いに影響を受けない。
  • テストでは、一方をチェックしてからもう一方をチェックし、どちらも独立して切り替わることを確認。

3. ListBox の追加とバリデーション

  • cboCity.AddItem と同様に UserForm_Initialize を繰り返して項目を追加。
  • ListIndex = -1 は「まだ何も選択されていない」状態 — 保存を進める前にこの条件をチェック。
  • このチェックは他の必須項目チェックと同じ errors 文字列に追加。

4. MultiPage チャレンジ

  • MultiPage コントロールをフォームにドラッグ — デフォルトで 2 ページある。
  • 各ページはプロパティウィンドウの Caption で名前変更。コード参照には (Name) プロパティを使用。
  • 既存コントロールをページにドラッグするだけで、コードの参照名は変わらない。
解答例
expand arrow
Private Sub UserForm_Initialize()
    cboCity.AddItem "Chicago"
    cboCity.AddItem "Boston"
    cboCity.AddItem "Austin"
    cboCity.AddItem "Seattle"
    cboCity.AddItem "Denver"

    lstCities.AddItem "Chicago"
    lstCities.AddItem "Boston"
    lstCities.AddItem "Austin"
    lstCities.AddItem "Seattle"
    lstCities.AddItem "Denver"
End Sub

Private Sub cmdCancel_Click()
    Unload Me
End Sub

Private Sub cmdSave_Click()
    Dim tbl As ListObject
    Dim newRow As ListRow
    Dim errors As String
    Dim status As String

    ' --- Reset field colors ---
    txtCompanyName.BackColor = vbWhite
    txtContact.BackColor = vbWhite
    txtEmail.BackColor = vbWhite

    ' --- Required field checks ---
    If Trim(txtCompanyName.Value) = "" Then
        errors = errors & "- Company Name is required" & vbNewLine
        txtCompanyName.BackColor = RGB(255, 199, 206)
    End If

    If Trim(txtContact.Value) = "" Then
        errors = errors & "- Contact is required" & vbNewLine
        txtContact.BackColor = RGB(255, 199, 206)
    End If

    If Trim(txtEmail.Value) = "" Then
        errors = errors & "- Email is required" & vbNewLine
        txtEmail.BackColor = RGB(255, 199, 206)
    End If

    ' --- Status via OptionButtons ---
    If optActive.Value = True Then
        status = "Active"
    ElseIf optInactive.Value = True Then
        status = "Inactive"
    ElseIf optProspect.Value = True Then
        status = "Prospect"
    Else
        errors = errors & "- Please select a Status" & vbNewLine
    End If

    ' --- ListBox check ---
    If lstCities.ListIndex = -1 Then
        errors = errors & "- Please select a city from the list" & vbNewLine
    End If

    If errors <> "" Then
        MsgBox "Please fix the following:" & vbNewLine & errors, vbExclamation
        Exit Sub
    End If

    ' --- All checks passed — safe to save now ---
    Set tbl = ThisWorkbook.Worksheets("Customers").ListObjects("tblCustomers")
    Set newRow = tbl.ListRows.Add

    newRow.Range(1, 1).Value = "C0" & (tbl.ListRows.Count + 100)
    newRow.Range(1, 2).Value = txtCompanyName.Value
    newRow.Range(1, 3).Value = txtContact.Value
    newRow.Range(1, 4).Value = txtEmail.Value
    newRow.Range(1, 5).Value = cboCity.Value
    newRow.Range(1, 6).Value = status

    If chkWelcomeEmail.Value = True Then
        MsgBox "A welcome email would be sent to " & txtEmail.Value & "."
    End If

    MsgBox txtCompanyName.Value & " was added successfully."
    Unload Me
End Sub

テストメモ:

  • 2(CheckBox)は専用のバリデーションなし — chkWelcomeEmailchkNewsletter を個別にクリックし、互いに影響しないことを確認。
  • 4(MultiPage)はレイアウト変更のみ — コード変更は不要。コントロール名はページ移動しても変わらない。
  • cboCity.Value は保存時(newRow.Range(1, 5))に引き続き使用され、lstCities はタスク要件に従い独立してバリデーションされるコントロール — 必要に応じて連携も可能。
すべて明確でしたか?

どのように改善できますか?

フィードバックありがとうございます!

セクション 5.  4

AIに質問する

expand

AIに質問する

ChatGPT

何でも質問するか、提案された質問の1つを試してチャットを始めてください

フォームコントロールの操作

TextBoxesComboBoxes はほとんどのデータ入力に対応していますが、他にも特定の選択肢をより自然に表現できるコントロールがいくつかあります。これらの値を正しく読み取るには、注意すべきポイントがいくつか存在します。

図 5.5

CheckBoxes

CheckBox の Value プロパティは単純なブール値で、チェックされていれば True、そうでなければ False です:

If chkWelcomeEmail.Value = True Then
    MsgBox "A welcome email would be sent here."
End If

フォーム上の各 CheckBox は他の CheckBox とは完全に独立しており、chkWelcomeEmail をチェックしても chkNewsletter には影響しません。選択肢が排他的でない場合に理想的な動作です。

OptionButtons

OptionButton は逆の動作をします。同じグループ内では、1つを選択すると他のすべてが自動的に選択解除されます。これは、顧客が同時に Active と Inactive にはなれないため、スクリーンショットの Status 選択肢に最適です。どれが選択されているかを確認するには、それぞれの Value を個別にチェックします:

Dim status As String
If optActive.Value = True Then
    status = "Active"
ElseIf optInactive.Value = True Then
    status = "Inactive"
ElseIf optProspect.Value = True Then
    status = "Prospect"
End If

排他性は自動的に VBA によって処理されるため、1つを選択したときに他の2つを解除するコードを書く必要はありません。ただし、グループ分けが重要です。OptionButton は同じコンテナ(フォーム本体や配置された Frame コントロール)内でのみ排他的に動作します。そのため、同じフォーム上に Status と Priority など2つの OptionButton グループがある場合は、それぞれ異なる Frame 内に配置する必要があります。そうしないと、VBA はすべてを1つの排他的グループとして扱います。

ListBoxes

ListBox は、Figure 5.5 の City リストのように、複数の選択肢を一度に表示し、クリックして開く必要がありません。ComboBox と同様に .AddItem で項目を追加し、選択内容は .Value または .ListIndex で取得します:

Private Sub UserForm_Initialize()
    lstCities.AddItem "Chicago"
    lstCities.AddItem "Boston"
    lstCities.AddItem "Austin"
End Sub
 
Private Sub cmdAssign_Click()
    If lstCities.ListIndex = -1 Then
        MsgBox "Please select a city first."
        Exit Sub
    End If
    MsgBox "Assigning to: " & lstCities.Value
End Sub

ListIndex は選択された項目の位置を示し、最初の項目が 0 から始まります。何も選択されていない場合は -1 となり、cmdAssign_Click で都市が選択されていない場合のチェックに利用されています。.Value は選択されたテキスト自体を返し、通常は数値の位置よりも便利です。

Multi-Page Forms

MultiPage コントロール(Figure 5.5 の上部にある Details / Preferences タブ)は、関連するコントロールを同じ物理フォーム上の別々のページにまとめます。フォームにフィールドが多すぎて1画面に収まらない場合に便利です。各ページは位置で指定され、現在表示されているページ上のコントロールのみがユーザーにとって意味を持ちますが、どのページが表示されていてもすべてのコントロールはコードから参照可能です:

Private Sub MultiPage1_Change()
    If MultiPage1.Value = 1 Then
        MsgBox "Now viewing Preferences"
    End If
End Sub

MultiPage1.Value は ListIndex と同じく 0 から始まるカウント方式で、最初のページが 0、2番目(Figure 5.5 の Preferences)が 1 です。Change イベントはユーザーが別のタブをクリックするたびに自動的に発生します。

タスク

  1. frmAddCustomer に Frame を追加し、その中に 3 つの OptionButton(optActive、optInactive、optProspect)を配置して、以前の cboStatus ComboBox を置き換え、上記の If...ElseIf ブロックを記述して選択内容を取得する。
  2. 2 つの独立した CheckBox(chkWelcomeEmail、chkNewsletter)を追加し、一方をチェックしても他方に影響しないことをテストで確認する。
  3. UserForm_Initialize で ListBox(lstCities)を作成し、何も選択されていない場合(ListIndex = -1)にエラーを表示するチェックを記述する。
  4. チャレンジ:Company Name、Contact、Email フィールドを「Details」という名前の MultiPage の 1 ページにまとめ、CheckBox と OptionButton を 2 ページ目「Preferences」に配置する。
ヒント
expand arrow

1. cboStatus を OptionButton の Frame で置き換える

  • フォームから cboStatus とそのラベルを削除 — 追加ではなく置き換え。
  • Frame コントロールをフォームにドラッグし、その枠内に 3 つの OptionButton をすべてドラッグして配置 — この囲い込みによって 1 グループとして排他的に動作。
  • 各 OptionButton の名前はプロパティウィンドウで個別に変更。Frame の Caption は「Status」などシンプルでよく、コードで参照しないなら名前は不要。
  • 選択内容の取得は、各 OptionButton の .ValueIf...ElseIf で個別に判定 — グループ内でどれが選択されているかを示す単一プロパティは存在しない。
  • cboStatus.Value はもう存在しないため、cmdSave_Click では OptionButton から status 変数を作成し、newRow に書き込む。

2. 2 つの独立した CheckBox を追加

  • これらはフォーム上に直接配置(Frame 内ではない) — CheckBox 同士が互いに影響を受けない。
  • テストでは、一方をチェックしてからもう一方をチェックし、どちらも独立して切り替わることを確認。

3. ListBox の追加とバリデーション

  • cboCity.AddItem と同様に UserForm_Initialize を繰り返して項目を追加。
  • ListIndex = -1 は「まだ何も選択されていない」状態 — 保存を進める前にこの条件をチェック。
  • このチェックは他の必須項目チェックと同じ errors 文字列に追加。

4. MultiPage チャレンジ

  • MultiPage コントロールをフォームにドラッグ — デフォルトで 2 ページある。
  • 各ページはプロパティウィンドウの Caption で名前変更。コード参照には (Name) プロパティを使用。
  • 既存コントロールをページにドラッグするだけで、コードの参照名は変わらない。
解答例
expand arrow
Private Sub UserForm_Initialize()
    cboCity.AddItem "Chicago"
    cboCity.AddItem "Boston"
    cboCity.AddItem "Austin"
    cboCity.AddItem "Seattle"
    cboCity.AddItem "Denver"

    lstCities.AddItem "Chicago"
    lstCities.AddItem "Boston"
    lstCities.AddItem "Austin"
    lstCities.AddItem "Seattle"
    lstCities.AddItem "Denver"
End Sub

Private Sub cmdCancel_Click()
    Unload Me
End Sub

Private Sub cmdSave_Click()
    Dim tbl As ListObject
    Dim newRow As ListRow
    Dim errors As String
    Dim status As String

    ' --- Reset field colors ---
    txtCompanyName.BackColor = vbWhite
    txtContact.BackColor = vbWhite
    txtEmail.BackColor = vbWhite

    ' --- Required field checks ---
    If Trim(txtCompanyName.Value) = "" Then
        errors = errors & "- Company Name is required" & vbNewLine
        txtCompanyName.BackColor = RGB(255, 199, 206)
    End If

    If Trim(txtContact.Value) = "" Then
        errors = errors & "- Contact is required" & vbNewLine
        txtContact.BackColor = RGB(255, 199, 206)
    End If

    If Trim(txtEmail.Value) = "" Then
        errors = errors & "- Email is required" & vbNewLine
        txtEmail.BackColor = RGB(255, 199, 206)
    End If

    ' --- Status via OptionButtons ---
    If optActive.Value = True Then
        status = "Active"
    ElseIf optInactive.Value = True Then
        status = "Inactive"
    ElseIf optProspect.Value = True Then
        status = "Prospect"
    Else
        errors = errors & "- Please select a Status" & vbNewLine
    End If

    ' --- ListBox check ---
    If lstCities.ListIndex = -1 Then
        errors = errors & "- Please select a city from the list" & vbNewLine
    End If

    If errors <> "" Then
        MsgBox "Please fix the following:" & vbNewLine & errors, vbExclamation
        Exit Sub
    End If

    ' --- All checks passed — safe to save now ---
    Set tbl = ThisWorkbook.Worksheets("Customers").ListObjects("tblCustomers")
    Set newRow = tbl.ListRows.Add

    newRow.Range(1, 1).Value = "C0" & (tbl.ListRows.Count + 100)
    newRow.Range(1, 2).Value = txtCompanyName.Value
    newRow.Range(1, 3).Value = txtContact.Value
    newRow.Range(1, 4).Value = txtEmail.Value
    newRow.Range(1, 5).Value = cboCity.Value
    newRow.Range(1, 6).Value = status

    If chkWelcomeEmail.Value = True Then
        MsgBox "A welcome email would be sent to " & txtEmail.Value & "."
    End If

    MsgBox txtCompanyName.Value & " was added successfully."
    Unload Me
End Sub

テストメモ:

  • 2(CheckBox)は専用のバリデーションなし — chkWelcomeEmailchkNewsletter を個別にクリックし、互いに影響しないことを確認。
  • 4(MultiPage)はレイアウト変更のみ — コード変更は不要。コントロール名はページ移動しても変わらない。
  • cboCity.Value は保存時(newRow.Range(1, 5))に引き続き使用され、lstCities はタスク要件に従い独立してバリデーションされるコントロール — 必要に応じて連携も可能。
すべて明確でしたか?

どのように改善できますか?

フィードバックありがとうございます!

セクション 5.  4
some-alt