T-10 ユーザーフォームで「三択クイズ」を作る!2022.05.13
次の書籍の第1章~5章を公開しています。
「Excel VBA 逆引きで学ぶ ユーザーフォーム&コントロール」
ユーザーフォームで「三択クイズ」を作ります。
三択クイズは、マウス操作で回答を選択するものを多く目にしますが、今回はキーボード操作で回答を選択する方式とします。
キーボード操作の受け取りは、フォームのKeyDownイベントを利用します。
クイズ問題・回答の入力されているシートを指定することで、クイズの種類を切り替え可能とし、また問題の出題順や回答順は毎回ランダムに入れ替わる仕様にします。
【動画】
このような動作の実現にはいくつかの方法が考えられますが、最も簡単だと思われる方法で実装します。
サンプルファイル ダウンロード
準備 1
ユーザーフォームを挿入し、次のコントロールを追加します。
■Labelコントロール
役割:クイズの問題表示
オブジェクト名:Label_Q
個数:1
BackColor:&H8000000F&
BackStyle:1 - fmBackStyleOpaque
BorderStyle:0 - fmBorderStyleNone
■Labelコントロール
役割:回答表示
オブジェクト名:Label_A1 / Label_A2 / Label_A3
個数:3
BackColor:&H00FFFFFF& / &H8000000F& / &H8000000F&
BackStyle:1 - fmBackStyleOpaque
BorderStyle:0 - fmBorderStyleNone
フォームを起ち上げた時には、回答1が選択された状態(背景色が白)とするため、Label_A1の背景色は予め白くしておきます。
フォームに追加するのはLabelコントロールのみです。
Labelコントロールはフォーカスを受け取らないため、フォーカスはフォームに留まります。
そのためフォームのKeyDownイベントを利用することができるのです。
準備 2
問題と回答を入力するシートを準備します。シート名は任意です。
1行目は見出しで各列の役割は次のようになります。
A列 問題管理番号
B列 クイズ問題
C列 回答1(正解) 正解はこの列に入力
D列 回答2
E列 回答3
フォームが起ちあがったタイミングで、対象シートのクイズ(問題)数を取得し、その中から出題数分の問題をランダムに選択します。
C列に正解を入力しますが、回答はランダム順に表示されます。
サンプルコード
フォームモジュールに記述します。
Option Explicit '【定数】 Private Const NOQ As Long = 10 '出題数(Number of questions) Private Const SHN As String = "quiz1" '出題対象シート名 '【変数】 Private lngQCT As Long '出題回数:その時点の出題番号 Private lngSelN As Long '選択番号:選択した回答番号(1~3) Private lngOrder() As Long '出題問題行番号格納 Private strQuestion() As String '出題内容 記録用 Private strCorrectA() As String '正解テキスト 記録用 Private strCorIncor() As String '各回答○× 記録用 Private lngCrtACnt As Long '正解回数 Private sinTime As Single '開始時間格納
'■フォームイベント '+++ Initializeイベント +++ Private Sub UserForm_Initialize() Dim c As Long ' Dim lngRnd As Long 'ランダム整数値格納 Dim lngLastRow As Long 'データの最終行格納 Dim strMsg As String 'メッセージ用 '出題データの最終行取得 lngLastRow = Worksheets(SHN).Cells(Rows.Count, "B").End(xlUp).Row 'エラーチェック If lngLastRow = 1 Then strMsg = "問題が入力されていません": GoTo FIN If NOQ < 1 Or (lngLastRow - 1) < NOQ Then strMsg = "NOQ数が適切ではありません": GoTo FIN End If '重複のない問題番号をランダムに抽出し行番号としてlngOrderに代入(出題数分) Randomize ReDim f(1 To (lngLastRow - 1)) As Boolean ReDim lngOrder(1 To NOQ) Do lngRnd = Int(Rnd() * (lngLastRow - 1)) + 1 If Not f(lngRnd) Then f(lngRnd) = True c = c + 1 lngOrder(c) = lngRnd + 1 'データの1行目は見出しのため「1」加算 End If Loop Until NOQ <= c '選択番号初期設定/出題回数初期設定 lngSelN = 1 lngQCT = 1 '最初のクイズを出題 Call Set_Quiz '開始時間格納 sinTime = Timer Exit Sub FIN: MsgBox strMsg, vbExclamation, "終了します" Unload Me End Sub
'+++ KeyDownイベント +++ Private Sub UserForm_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, _ ByVal Shift As Integer) Select Case KeyCode Case vbKeyUp '方向キー上(↑) 整数値:38 lngSelN = lngSelN - 1 If lngSelN = 0 Then lngSelN = 3 Case vbKeyDown '方向キー下(↓) 整数値:40 lngSelN = lngSelN + 1 If lngSelN = 4 Then lngSelN = 1 Case vbKeyEscape 'Escキー 整数値:27 Unload Me Case vbKeyReturn 'Enterキー 整数値:13 '選んだ回答番号のラベルCaptionが正解テキスト同じ場合は正解数を加算 If Controls("Label_A" & lngSelN).Caption = strCorrectA(lngQCT) Then lngCrtACnt = lngCrtACnt + 1 strCorIncor(lngQCT) = "○" Else strCorIncor(lngQCT) = "×" End If If lngQCT = NOQ Then GoTo FIN '出題回数が出題数と同じになったら終了 lngQCT = lngQCT + 1 Call Set_Quiz '次のクイズを出題 Exit Sub Case Else Exit Sub End Select Call Set_BackColor(lngSelN) '回答ラベルの背景色を設定 Exit Sub FIN: '--- 終了メッセージ作成 --- Dim i As Long Dim strMsg As String For i = 1 To lngQCT '出題内容・正解・○×の各配列の値をつなげる strMsg = strMsg & "[" & i & "] " & strCorIncor(i) & " / " strMsg = strMsg & strQuestion(i) & " / " & strCorrectA(i) & vbLf Next MsgBox "正解数:" & lngCrtACnt & "/" & NOQ & " " & _ "正解率:" & Application.Round(lngCrtACnt / NOQ, 1) * 100 & "% " & _ "所用時間:" & Application.Round(Timer - sinTime, 1) & "秒" & vbLf & _ vbLf & strMsg Unload Me End Sub
'■サブルーチン '+++ 回答ラベルの背景色を設定 +++ '【引数】 ' n:背景色を白くするラベル番号 '++++++++++++++++++++++++++++++++ Private Sub Set_BackColor(ByVal n As Long) Dim i As Long For i = 1 To 3 Controls("Label_A" & i).BackColor = &H8000000F Next Controls("Label_A" & n).BackColor = &HFFFFFF End Sub
'+++ クイズ出題 +++ Private Sub Set_Quiz() Dim varPattern As Variant '回答表示順パターン Dim varSplit As Variant ' '回答表示順パターン格納 varPattern = Array("1,2,3", "1,3,2", "2,1,3", "2,3,1", "3,1,2", "3,2,1") 'パターンをランダムに取得し分割 varSplit = Split(varPattern(Int(Rnd() * 6)), ",") '【例】[2,1,3]→[2]/[1]/[3] '配列拡張 ReDim Preserve strQuestion(1 To lngQCT) '出題内容 ReDim Preserve strCorrectA(1 To lngQCT) '正解 ReDim Preserve strCorIncor(1 To lngQCT) '○× 'ラベルと配列に値を設定 Dim r As Long: r = lngOrder(lngQCT) With Worksheets(SHN) Label_Q.Caption = "[" & lngQCT & "] " & .Cells(r, "B") Label_A1.Caption = .Cells(r, 2 + Val(varSplit(0))) Label_A2.Caption = .Cells(r, 2 + Val(varSplit(1))) Label_A3.Caption = .Cells(r, 2 + Val(varSplit(2))) strQuestion(lngQCT) = .Cells(r, "B") strCorrectA(lngQCT) = .Cells(r, "C") End With 'Beep '音を鳴らす場合はコメントを外す End Sub
簡単な解説・補足
■イベント
今回使用しているイベントはフォームの2つのイベントです。
Initializeイベント
フォームが生成されるタイミングで発生するInitializeイベントでは、次の準備処理を行っています。
・出題データの最終行取得
・エラーチェック
・重複のない問題番号をランダムに抽出し行番号としてlngOrderに代入(出題数分)
この配列lngOrderに格納された順に問題が出題されます。
・選択番号初期設定/出題回数初期設定
・最初のクイズを出題
・開始時間格納
KeyDownイベント
KeyDownイベントは、フォーカスのあるフォームまたはコントロールで発生します。
そのためフォームに配置されたコマンドボタンやテキストボックスにフォーカスがある場合、フォームのKeyDownイベントは発生しません。
フォームのKeyDownイベントを利用するには、フォームにフォーカスが留まるようにすることがポイントです。
このサンプルでは、フォーカスを受け取らないラベルコントロールのみを利用することで、フォームにフォーカスが留まるようにしています。
KeyDownイベントでは、KeyCodeに応じて処理を分岐しています。
・[↑]:選択番号(変数lngSelN)を1減算します。選択番号が0になった場合は3に変更します。
・[↓]:選択番号を1加算します。選択番号が4になった場合は1に変更します。
・[Esc]:フォームを破棄して閉じます。
・[Enter]:正解・不正解の確認、終了確認、次のクイズ出題を行います。
上記以外のキーが押された場合は、何もしないで処理を抜けます。
※KeyCode
引数 KeyCodeはフォーム上で押されたキーのコードを返します。
返されるコードは整数値ですが、対応した組み込み定数が準備されています。
一般的にはそれを利用することが多いです。型としてはvbKey○○になります。
[ MSDN KeyCode組み込み定数一覧 ]
■サブルーチン
Set_BackColor
選択番号に対応するラベルの背景色を白くします。
Set_Quiz
回答パターンをランダムに取得し、取得したパターンを分割し順番を配列varSplitに格納しています。配列に格納された順番に応じて、回答をラベルに設定しています。
出題内容・正解・○×は、最後のメッセージで表示するため、出題時に配列を拡張し、値を記録しています。
■ショートカットでマクロ実行
ショートカットでマクロを実行するには、マクロウィンドウのオプションから、ショートカットを登録します。
[Alt]+[F8]で「マクロ」ウィンドウが表示されます。
「オプション」を選択すると、「オプション」ウィンドウが表示され、[Ctrl]+[ ]の部分に任意のキーを設定します。
ここで登録したショートカットは、Windows既定のショートカットより優先されます。
例えば[Ctrl]+[c]で何らかのマクロを登録すると、そのファイルが開いている間は、[Ctrl]+[c](コピー)の使用ができなくなるため注意が必要です。
書籍紹介140以上のサンプルファイル付き!
知りたいがすぐわかる! やりたいがすぐできる!
「Excel VBA 逆引きで学ぶ ユーザーフォーム&コントロール」(Kindle版)
ユーザーフォームを扱えると、VBAでできることが大きく広がります!
本書では、知りたいこと、やりたいことから、逆引きで学びを深められます。
■ 購入:amazon