ユーザーフォームの閉じる([×])ボタン制御

ユーザーフォームで指定項目を入力してもらってワークシートに戻って処理を行なう場合、「キャンセル」の処置を考えておかなければなりません。何もせずに閉じる[×]ボタンが押されてしまうかもしれないからです。



このようなサンプルを用意しました。
フォームの閉じる[×]ボタンの制御
(画像をクリックすると、このサンプルがダウンロードできます)
5種類のサンプルを用意し、それぞれユーザーフォーム上にOKボタンだけがあります。
ここでOKボタンが押されて終了したか、そうでないか(閉じる[×]ボタンやEscキー)で処理を切り分けるようにします。
以下、順に説明して行きます。

①キャンセルとして扱う。(OKは押されず終了)
もっとも普通の方法です。特に「キャンセル」のボタンは作りませんが、Publicな変数スイッチでOKボタンが押されたかを判断する方法です。

'***************************************************************************************************
'   ユーザーフォームの閉じるボタン制御                              Module1(Module)
'
'   作成者:井上治  URL:https://www.ne.jp/asahi/excel/inoue/ [Excelでお仕事!]
'***************************************************************************************************
'変更日付 Rev  変更履歴内容------------------------------------------------------------------------>
'04/01/03(1.00)新規作成
'19/10/24(1.10)64ビット版Office対応(CancelTEST4)、ソース整理
'***************************************************************************************************
Option Explicit
'===================================================================================================
Public swOK As Byte                                             ' ユーザーフォームからの結果通知

'***************************************************************************************************
'   ■■■ 公開プロシージャ(Public) ■■■
'***************************************************************************************************
'* 処理名 :ShowUserForm1
'* 機能  :ごく普通のフォーム操作(OKが押されたか後で判断)
'---------------------------------------------------------------------------------------------------
'* 返り値 :(なし)
'* 引数  :(なし)
'---------------------------------------------------------------------------------------------------
'* 作成日 :2004年01月03日
'* 作成者 :井上 治
'* 更新日 :2004年01月03日
'* 更新者 :井上 治
'* 機能説明:
'* 注意事項:
'***************************************************************************************************
Public Sub ShowUserForm1()
    '-----------------------------------------------------------------------------------------------
    ' テスト①フォーム
    CancelTEST1.Show
    ' OKボタンがクリックされたか
    If swOK = 1 Then
        ' OKがクリックされた時の処理
        MsgBox "「OK」が押されました。"
    Else
        ' OKがクリックされずにHideした時の処理
        MsgBox "キャンセルされました。"
    End If
    Unload CancelTEST1
End Sub
これは、ユーザーフォームを呼び出す標準モジュールの例です。
ユーザーフォームからも参照できるように、PublicOKが押されたかを判断させるためのスイッチを宣言しておきます。
ユーザーフォームの表示の後で上記のスイッチを判断してOKが押された場合と、そうでない場合に処理を切り分けます。




'***************************************************************************************************
'   ユーザーフォームの閉じるボタン制御                              CancelTEST1(UserForm)
'
'   作成者:井上治  URL:https://www.ne.jp/asahi/excel/inoue/ [Excelでお仕事!]
'***************************************************************************************************
'変更日付 Rev  変更履歴内容------------------------------------------------------------------------>
'04/01/03(1.00)新規作成
'***************************************************************************************************
Option Explicit

'***************************************************************************************************
'   ■■■ フォームイベント(Private) ■■■
'***************************************************************************************************
'* 処理名 :CommandButton1_Click
'* 機能  :OKボタンのクリックイベント
'---------------------------------------------------------------------------------------------------
'* 返り値 :(なし)
'* 引数  :(なし)
'---------------------------------------------------------------------------------------------------
'* 作成日 :2004年01月03日
'* 作成者 :井上 治
'* 更新日 :2004年01月03日
'* 更新者 :井上 治
'* 機能説明:
'* 注意事項:
'***************************************************************************************************
Private Sub CommandButton1_Click()
    '-----------------------------------------------------------------------------------------------
    swOK = 1
    Me.Hide
End Sub

'***************************************************************************************************
'* 処理名 :UserForm_Initialize
'* 機能  :ユーザーフォームの初期化
'---------------------------------------------------------------------------------------------------
'* 返り値 :(なし)
'* 引数  :(なし)
'---------------------------------------------------------------------------------------------------
'* 作成日 :2004年01月03日
'* 作成者 :井上 治
'* 更新日 :2004年01月03日
'* 更新者 :井上 治
'* 機能説明:
'* 注意事項:
'***************************************************************************************************
Private Sub UserForm_Initialize()
    '-----------------------------------------------------------------------------------------------
    swOK = 0
End Sub

'------------------------------------------<< End of Source >>--------------------------------------
これは、ユーザーフォームのイベントの記述です。
フォームの立ち上げ時にOK押下判定スイッチを初期化しておき、OKボタン(CommandButton1)がクリックされた時だけそのスイッチに1をセットしてフォームを消すようにします。
フォームが消されると上記の標準モジュールのShowメソッドの次に制御が戻りますが、そこでOK押下判定スイッチの値を判断して処理を切り分けることができます。

②キャンセルできなくする。
処理の途中でのフォームの表示などでは、その段階でのキャンセルでは都合が悪いことがあります。

'***************************************************************************************************
'   ユーザーフォームの閉じるボタン制御                              CancelTEST2(UserForm)
'
'   作成者:井上治  URL:https://www.ne.jp/asahi/excel/inoue/ [Excelでお仕事!]
'***************************************************************************************************
'変更日付 Rev  変更履歴内容------------------------------------------------------------------------>
'04/01/03(1.00)新規作成
'***************************************************************************************************
Option Explicit

'***************************************************************************************************
'   ■■■ フォームイベント(Private) ■■■
'***************************************************************************************************
'* 処理名 :CommandButton1_Click
'* 機能  :OKボタンのクリックイベント
'---------------------------------------------------------------------------------------------------
'* 返り値 :(なし)
'* 引数  :(なし)
'---------------------------------------------------------------------------------------------------
'* 作成日 :2004年01月03日
'* 作成者 :井上 治
'* 更新日 :2004年01月03日
'* 更新者 :井上 治
'* 機能説明:
'* 注意事項:
'***************************************************************************************************
Private Sub CommandButton1_Click()
    '-----------------------------------------------------------------------------------------------
    swOK = 1
    Me.Hide
End Sub

'***************************************************************************************************
'* 処理名 :UserForm_Initialize
'* 機能  :ユーザーフォームの初期化
'---------------------------------------------------------------------------------------------------
'* 返り値 :(なし)
'* 引数  :(なし)
'---------------------------------------------------------------------------------------------------
'* 作成日 :2004年01月03日
'* 作成者 :井上 治
'* 更新日 :2004年01月03日
'* 更新者 :井上 治
'* 機能説明:
'* 注意事項:
'***************************************************************************************************
Private Sub UserForm_Initialize()
    '-----------------------------------------------------------------------------------------------
    swOK = 0
End Sub

'***************************************************************************************************
'* 処理名 :UserForm_QueryClose
'* 機能  :ユーザーフォーム閉鎖動作
'---------------------------------------------------------------------------------------------------
'* 返り値 :(なし)
'* 引数  :(既定)
'---------------------------------------------------------------------------------------------------
'* 作成日 :2004年01月03日
'* 作成者 :井上 治
'* 更新日 :2004年01月03日
'* 更新者 :井上 治
'* 機能説明:
'* 注意事項:
'***************************************************************************************************
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
    '-----------------------------------------------------------------------------------------------
    ' 閉じる[×]ボタンか
    If CloseMode = vbFormControlMenu Then
        MsgBox "キャンセルはできません。"
        Cancel = True
    End If
End Sub

'------------------------------------------<< End of Source >>--------------------------------------
このような場合はユーザーフォームのイベントの記述に「UserForm_QueryClose」イベントを追加し、このように記述することでOKボタンをクリックせずに閉じることを許さないようにできます。

③後でキャンセルするか判断する。
キャンセルは許可するがキャンセルする場合は確認メッセージを表示してキャンセルして良いかを再確認するという処理です。

'***************************************************************************************************
'* 処理名 :ShowUserForm3
'* 機能  :キャンセルボタンを別に置いたフォーム操作
'---------------------------------------------------------------------------------------------------
'* 返り値 :(なし)
'* 引数  :(なし)
'---------------------------------------------------------------------------------------------------
'* 作成日 :2004年01月03日
'* 作成者 :井上 治
'* 更新日 :2019年10月24日
'* 更新者 :井上 治
'* 機能説明:
'* 注意事項:
'***************************************************************************************************
Public Sub ShowUserForm3()
    '-----------------------------------------------------------------------------------------------
    swOK = 0
    ' OKになるまで繰り返す
    Do Until swOK = 1
        ' テスト③フォーム
        CancelTEST3.Show
        ' OKボタンがクリックされたか
        If swOK = 1 Then
            ' OKがクリックされた時の処理
            MsgBox "「OK」が押されました。"
        Else
            ' OKがクリックされずにHideした時の処理
            If MsgBox("キャンセルが押されました。" & vbCr & _
                "終了しますか?", vbInformation + vbYesNo) = vbYes Then
                swOK = 1
            End If
        End If
    Loop
    Unload CancelTEST3
End Sub
ShowメソッドをOKになるまで繰り返すループ処理の中に入れます。
OKボタンがクリックされずにフォームが消された場合は「キャンセルが押されました。終了しますか?」をメッセージボックスに表示します。 メッセージボックスのボタンは「はい」「いいえ」なので、「はい」の時はOKと同様のスイッチをセットさせます。 「いいえ」だとループから脱出しないのでフォームが再表示されます。
フォームの閉じる[×]ボタンの制御③
このサンプルでは、キャンセルの処理にフォームの閉じる[×]ボタンだけでなく、Escキーも同様に扱うことにしてみます。フォーム内にキャンセルの役目のボタンを貼り付けて、そのボタンのプロパティでCancel=Trueにしておきます。
キャンセルが不要な場合は、デザイン上でフォームのサイズを大きくしておき、キャンセルの役目のボタンを表示させる領域外に貼り付けておくと良いでしょう。

'***************************************************************************************************
'   ユーザーフォームの閉じるボタン制御                              CancelTEST3(UserForm)
'
'   作成者:井上治  URL:https://www.ne.jp/asahi/excel/inoue/ [Excelでお仕事!]
'***************************************************************************************************
'変更日付 Rev  変更履歴内容------------------------------------------------------------------------>
'04/01/03(1.00)新規作成
'***************************************************************************************************
Option Explicit

'***************************************************************************************************
'   ■■■ フォームイベント(Private) ■■■
'***************************************************************************************************
'* 処理名 :CommandButton1_Click
'* 機能  :OKボタンのクリックイベント
'---------------------------------------------------------------------------------------------------
'* 返り値 :(なし)
'* 引数  :(なし)
'---------------------------------------------------------------------------------------------------
'* 作成日 :2004年01月03日
'* 作成者 :井上 治
'* 更新日 :2004年01月03日
'* 更新者 :井上 治
'* 機能説明:
'* 注意事項:
'***************************************************************************************************
Private Sub CommandButton1_Click()
    '-----------------------------------------------------------------------------------------------
    swOK = 1
    Me.Hide
End Sub

'***************************************************************************************************
'* 処理名 :CommandButton2_Click
'* 機能  :CANCELボタンのクリックイベント
'---------------------------------------------------------------------------------------------------
'* 返り値 :(なし)
'* 引数  :(なし)
'---------------------------------------------------------------------------------------------------
'* 作成日 :2004年01月03日
'* 作成者 :井上 治
'* 更新日 :2004年01月03日
'* 更新者 :井上 治
'* 機能説明:
'* 注意事項:
'***************************************************************************************************
Private Sub CommandButton2_Click()
    '-----------------------------------------------------------------------------------------------
    Me.Hide
End Sub

'***************************************************************************************************
'* 処理名 :UserForm_Initialize
'* 機能  :ユーザーフォームの初期化
'---------------------------------------------------------------------------------------------------
'* 返り値 :(なし)
'* 引数  :(なし)
'---------------------------------------------------------------------------------------------------
'* 作成日 :2004年01月03日
'* 作成者 :井上 治
'* 更新日 :2004年01月03日
'* 更新者 :井上 治
'* 機能説明:
'* 注意事項:
'***************************************************************************************************
Private Sub UserForm_Initialize()
    '-----------------------------------------------------------------------------------------------
    swOK = 0
    ' ユーザーフォームの高さを調整してCANCELボタンを隠す
    Me.Height = 74.25
End Sub

'------------------------------------------<< End of Source >>--------------------------------------
これは、ユーザーフォームのイベント記述です。
キャンセルの役目のボタン(CommandButton2)は、OK押下判定スイッチをせずにフォームを消すようにします。
フォームはデザイン時に実際の大きさより大きくしているので、初期化段階で高さを調整しています。

④閉じる([×])ボタンを表示させない。
前々項でキャンセルを許可しないサンプルを説明しましたが、この方法では一旦フォームが消えてから「キャンセルできません」のメッセージが表示されます。
それでは、いっそのこと閉じる[×]ボタンを消してしまえばメッセージなどの対処も不要になります。

'***************************************************************************************************
'   ユーザーフォームの閉じるボタン制御                              CancelTEST4(UserForm)
'
'   作成者:井上治  URL:https://www.ne.jp/asahi/excel/inoue/ [Excelでお仕事!]
'***************************************************************************************************
'変更日付 Rev  変更履歴内容------------------------------------------------------------------------>
'04/01/03(1.00)新規作成
'19/10/24(1.10)64ビット版Office対応
'***************************************************************************************************
Option Explicit
'===================================================================================================
Private Const GWL_STYLE = (-16)
Private Const WS_SYSMENU = &H80000
' APIは条件付きコンパイルで64ビットWindowsにも対応
#If VBA7 Then
' ウィンドウに関する情報を返す
Private Declare PtrSafe Function GetWindowLong Lib "USER32.dll" _
    Alias "GetWindowLongA" _
    (ByVal hWnd As LongPtr, ByVal nIndex As Long) As LongPtr
' ウィンドウの属性を変更
Private Declare PtrSafe Function SetWindowLongPtr Lib "USER32.dll" _
    Alias "SetWindowLongPtrA" _
    (ByVal hWnd As LongPtr, _
     ByVal nIndex As Long, _
     ByVal dwNewLong As LongPtr) As LongPtr
' Activeなウィンドウのハンドルを取得
Private Declare PtrSafe Function GetActiveWindow Lib "USER32.dll" _
    () As LongPtr
' メニューバーを再描画
Private Declare PtrSafe Function DrawMenuBar Lib "USER32.dll" _
    (ByVal hWnd As LongPtr) As Long
#Else
' ウィンドウに関する情報を返す
Private Declare Function GetWindowLong Lib "USER32.dll" _
    Alias "GetWindowLongA" _
    (ByVal hWnd As Long, ByVal nIndex As Long) As Long
' ウィンドウの属性を変更
Private Declare Function SetWindowLong Lib "USER32.dll" _
    Alias "SetWindowLongA" _
    (ByVal hWnd As Long, ByVal nIndex As Long, _
     ByVal dwNewLong As Long) As Long
' Activeなウィンドウのハンドルを取得
Private Declare Function GetActiveWindow Lib "USER32.dll" _
    () As Long
' メニューバーを再描画
Private Declare Function DrawMenuBar Lib "USER32.dll" _
    (ByVal hWnd As Long) As Long
#End If

'***************************************************************************************************
'   ■■■ フォームイベント(Private) ■■■
'***************************************************************************************************
'* 処理名 :CommandButton1_Click
'* 機能  :OKボタンのクリックイベント
'---------------------------------------------------------------------------------------------------
'* 返り値 :(なし)
'* 引数  :(なし)
'---------------------------------------------------------------------------------------------------
'* 作成日 :2004年01月03日
'* 作成者 :井上 治
'* 更新日 :2004年01月03日
'* 更新者 :井上 治
'* 機能説明:
'* 注意事項:
'***************************************************************************************************
Private Sub CommandButton1_Click()
    '-----------------------------------------------------------------------------------------------
    swOK = 1
    Me.Hide
End Sub

'***************************************************************************************************
'* 処理名 :UserForm_Activate
'* 機能  :フォームがアクティブになるイベント
'---------------------------------------------------------------------------------------------------
'* 返り値 :(なし)
'* 引数  :(なし)
'---------------------------------------------------------------------------------------------------
'* 作成日 :2004年01月03日
'* 作成者 :井上 治
'* 更新日 :2004年01月03日
'* 更新者 :井上 治
'* 機能説明:
'* 注意事項:
'***************************************************************************************************
Private Sub UserForm_Activate()
    '-----------------------------------------------------------------------------------------------
#If VBA7 Then
    Dim hWnd As LongPtr
    Dim Wnd_STYLE As LongPtr
#Else
    Dim hWnd As Long
    Dim Wnd_STYLE As Long
#End If
    hWnd = GetActiveWindow()
    Wnd_STYLE = GetWindowLong(hWnd, GWL_STYLE)
    Wnd_STYLE = Wnd_STYLE And (Not WS_SYSMENU)
#If VBA7 Then
    SetWindowLongPtr hWnd, GWL_STYLE, Wnd_STYLE
#Else
    SetWindowLong hWnd, GWL_STYLE, Wnd_STYLE
#End If
    DrawMenuBar hWnd
End Sub

'***************************************************************************************************
'* 処理名 :UserForm_Initialize
'* 機能  :ユーザーフォームの初期化
'---------------------------------------------------------------------------------------------------
'* 返り値 :(なし)
'* 引数  :(なし)
'---------------------------------------------------------------------------------------------------
'* 作成日 :2004年01月03日
'* 作成者 :井上 治
'* 更新日 :2004年01月03日
'* 更新者 :井上 治
'* 機能説明:
'* 注意事項:
'***************************************************************************************************
Private Sub UserForm_Initialize()
    '-----------------------------------------------------------------------------------------------
    swOK = 0
End Sub

'------------------------------------------<< End of Source >>--------------------------------------
これは、ユーザーフォームのイベントの記述です。
API記述になりますが、一番最初に説明したものにAPI宣言記述とUserForm_Activateの記述を追加すれば実現できます。
OK押下判定スイッチはそのまま使っていますが、閉じる[×]ボタンが表示されないので、以後にOKがクリックされたか判断する必要はなくなります。
フォームの閉じる[×]ボタンの制御④
このように(左のフォーム)なります。(右はOKをクリックした時のメッセージ)

⑤フォームを閉じる前にキャンセルするか判断する。
前々項のサンプルで説明したキャンセルするかを再確認する方法ですが、これでは一旦フォームが消えてしまいます。消えてしまうのが都合が悪い場合がありますが、そのような場合はこの方法で解決できます。

'***************************************************************************************************
'   ユーザーフォームの閉じるボタン制御                              CancelTEST5(UserForm)
'
'   作成者:井上治  URL:https://www.ne.jp/asahi/excel/inoue/ [Excelでお仕事!]
'***************************************************************************************************
'変更日付 Rev  変更履歴内容------------------------------------------------------------------------>
'04/01/03(1.00)新規作成
'***************************************************************************************************
Option Explicit

'***************************************************************************************************
'   ■■■ フォームイベント(Private) ■■■
'***************************************************************************************************
'* 処理名 :CommandButton1_Click
'* 機能  :OKボタンのクリックイベント
'---------------------------------------------------------------------------------------------------
'* 返り値 :(なし)
'* 引数  :(なし)
'---------------------------------------------------------------------------------------------------
'* 作成日 :2004年01月03日
'* 作成者 :井上 治
'* 更新日 :2004年01月03日
'* 更新者 :井上 治
'* 機能説明:
'* 注意事項:
'***************************************************************************************************
Private Sub CommandButton1_Click()
    '-----------------------------------------------------------------------------------------------
    swOK = 1
    Me.Hide
End Sub

'***************************************************************************************************
'* 処理名 :CommandButton1_KeyDown
'* 機能  :OKボタンのキー押下イベント
'---------------------------------------------------------------------------------------------------
'* 返り値 :(なし)
'* 引数  :(既定)
'---------------------------------------------------------------------------------------------------
'* 作成日 :2004年01月03日
'* 作成者 :井上 治
'* 更新日 :2004年01月03日
'* 更新者 :井上 治
'* 機能説明:
'* 注意事項:
'***************************************************************************************************
Private Sub CommandButton1_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
    '-----------------------------------------------------------------------------------------------
    ' Escキー打鍵か
    If KeyCode = vbKeyEscape Then
        ' Escキー打鍵時の処理
        If FP_GetResult Then Me.Hide
    End If
End Sub

'***************************************************************************************************
'* 処理名 :UserForm_Initialize
'* 機能  :ユーザーフォームの初期化
'---------------------------------------------------------------------------------------------------
'* 返り値 :(なし)
'* 引数  :(なし)
'---------------------------------------------------------------------------------------------------
'* 作成日 :2004年01月03日
'* 作成者 :井上 治
'* 更新日 :2004年01月03日
'* 更新者 :井上 治
'* 機能説明:
'* 注意事項:
'***************************************************************************************************
Private Sub UserForm_Initialize()
    '-----------------------------------------------------------------------------------------------
    swOK = 0
End Sub

'***************************************************************************************************
'* 処理名 :UserForm_QueryClose
'* 機能  :ユーザーフォーム閉鎖動作
'---------------------------------------------------------------------------------------------------
'* 返り値 :(なし)
'* 引数  :(既定)
'---------------------------------------------------------------------------------------------------
'* 作成日 :2004年01月03日
'* 作成者 :井上 治
'* 更新日 :2004年01月03日
'* 更新者 :井上 治
'* 機能説明:
'* 注意事項:
'***************************************************************************************************
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
    '-----------------------------------------------------------------------------------------------
    ' 閉じる[×]ボタンか
    If CloseMode = vbFormControlMenu Then
        Cancel = Not FP_GetResult
    End If
End Sub

'***************************************************************************************************
'   ■■■ サブ処理(Private) ■■■
'***************************************************************************************************
'* 処理名 :FP_GetResult
'* 機能  :キャンセルするかを確認
'---------------------------------------------------------------------------------------------------
'* 返り値 :キャンセル判定(Boolean)
'* 引数  :(なし)
'---------------------------------------------------------------------------------------------------
'* 作成日 :2004年01月03日
'* 作成者 :井上 治
'* 更新日 :2004年01月03日
'* 更新者 :井上 治
'* 機能説明:
'* 注意事項:
'***************************************************************************************************
Private Function FP_GetResult() As Boolean
    '-----------------------------------------------------------------------------------------------
    ' 確認メッセージ表示
    If MsgBox("キャンセルが押されました。" & vbCr & _
              "終了しますか?", vbInformation + vbYesNo) = vbYes Then
        FP_GetResult = True
    Else
        FP_GetResult = False
    End If
End Function

'------------------------------------------<< End of Source >>--------------------------------------
これは、ユーザーフォームのイベントの記述です。
一番最初に説明したものにCommandButton1_KeyDownUserForm_QueryCloseの記述を追加すれば実現できます。
このサンプルでは、OKボタンが「Default=True」になっているので、EnterEscキーのキーイベントはOKボタン上で発生します。ここでEscキー(KeyCode=27)での処理をKeyDownイベントで追加します。
同様に「UserForm_QueryClose」イベントも追加します。
再確認の処理は共通なので、別のFunctionプロシージャにまとめてあり、上記の2つのイベント記述から呼び出します。
UserForm_QueryClose」イベントではキャンセルしない場合が「True」となるので、Functionプロシージャからの引き渡しに「Not」をつけて引き渡します。



ちなみに、最後の「FP_GetResult」は説明上で上記のように記述しましたが、 If文を排除して、

'***************************************************************************************************
'   ■■■ サブ処理(Private) ■■■
'***************************************************************************************************
'* 処理名 :FP_GetResult
'* 機能  :キャンセルするかを確認
'---------------------------------------------------------------------------------------------------
'* 返り値 :キャンセル判定(Boolean)
'* 引数  :(なし)
'---------------------------------------------------------------------------------------------------
'* 作成日 :2004年01月03日
'* 作成者 :井上 治
'* 更新日 :2020年01月09日
'* 更新者 :井上 治
'* 機能説明:
'* 注意事項:
'***************************************************************************************************
Private Function FP_GetResult() As Boolean
    '-----------------------------------------------------------------------------------------------
    ' 確認メッセージ表示
    FP_GetResult = MsgBox("キャンセルが押されました。" & vbCr & _
                          "終了しますか?", vbInformation + vbYesNo) = vbYes
End Function
このように記述しても同じ結果になります。
FP_GetResult」の結果がBooleanであることを理解できるとこの記述が理解できると思います。