VBA

【VBA ユーザーフォームで×ボタンを制御】

VBA ユーザーフォーム

ユーザーフォーム

皆さんは、ユーザーフォームを使っていますか?
ユーザーフォームは、とても簡単に作成でき、入力作業も行いやすくなりますので、重宝されている方も多いのではないでしょうか。

皆さんも基本的なフォームの作成等は、出来ているかと思いますので、個人的な備忘録を兼ねて、ユーザーフォームを更に便利に使いこなす技を、記事にしていきたいと思います。

通常、ユーザーフォームを作成する場合、閉じるボタンを作成することが多いと思います。

閉じる際に、何か処理を入れたりしてある場合、×ボタンで閉じられると最終処理が走らなくなってしまいます。

そこで、今回は×ボタンの制御と、ユーザーフォームを閉じる際に発生するイベントなどについて書いていきます。

内容は、とても簡単なので是非みなさんもコードを書いて、動作を確かめてください!!

×ボタンを押された際に発生するイベント

ユーザーフォームを閉じようとすると、UserFormのQueryCloseイベント が発生します。

構文

Private Sub UserForm_QueryClose(Cancel AsIntegerCloseMode As Integer)

このイベントは、ユーザーフォームが閉じる前に発生し、閉じられ方のステータスを 、引数CloseMode へ格納します。

引数の説明

定数説明
vbFormControlMenu0UserForm のコントロールメニューで [閉じる] コマンドを選択した。
vbFormCode1Unload ステートメントが呼び出された。
vbAppWindows2Windows が終了しようとしている。
vbAppTaskManager3タスク マネージャー によって閉じられた 。

このイベントは、ユーザーフォームが閉じる前に、完了していないタスクがユーザー フォームに含まれていないかどうかを確認するために使用されます。
 たとえば、ユーザーが UserForm 内の新しいデータを保存していない場合、データの保存を求めるメッセージを表示し注意を促すことが可能です。

ユーザーフォームが閉じるときに、QueryClose イベント を使用し、 Cancel プロパティを True に設定することで、閉じる動作を取り消すことができます。

サンプルコード

Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
    If CloseMode = 0 Then
        MsgBox "×ではフォームを閉じることができません!!"
        Cancel = True
    End If
End Sub

このコードだと【引数0】を指定していますのでフォーム上の×に対してのIFとなります 。

Cancelに【True】を指定することでフォームを閉じる動作をキャンセルします。

【False】を指定した場合は、通常通りフォームを閉じます。

×ボタンを消してしまう

あまりお勧めは出来ませんが、×ボタンを消してしまうことも可能です。

Win32Apiを使用する方法で、コードが少しわかりにくくなってしまいます。
また、Win32Apiを使用すると、コードが複雑化してしまいがちなので注意して下さい。

サンプルコードをVBEに張り付けて、使用してみてください。

標準モジュール

'定数
Public Const GWL_STYLE As Long = -16
Public Const WS_SYSMENU As Long = &H80000

'API宣言
Public Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long

Public Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long) As Long

Public Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long

Public Declare Function DrawMenuBar Lib "user32" (ByVal hWnd As Long) As Long
 
Sub ShowForm()
  UserForm1.Show
End Sub

小難しいコードが並んでいるように感じますが、使用する関数を宣言しているだけです。

C++などで、DLLを自作して呼び出す時も、【Declare】を使用するので覚えておくと良いかもしれません。

UserForm1

Private Sub UserForm_Initialize()
  Dim hWnd As Long
  Dim lngWstyle As Long
  hWnd = FindWindow(vbNullString, Me.Caption)
  lngWstyle = GetWindowLong(hWnd, GWL_STYLE)
 
  SetWindowLong hWnd, GWL_STYLE, lngWstyle And (Not WS_SYSMENU)
  DrawMenuBar hWnd
End Sub

上のサンプルでは、×ボタンが消えるだけでショートカットキーの【ALT+F4】を押されると閉じることが出来てしまいます。

私は、 QueryCloseイベントで処理を分岐するのが一番正解だと思います。

おまけ:フォームに最大化・最小化ボタンを付ける

これは殆ど必要ないかと思いますが、こういう動作をさせたい人の為にサンプルを載せておきます。

ユーザーフォーム

Option Explicit

'ご自身の環境で宣言を変えてください。
'64bitのOffice環境の場合
Private Declare PtrSafe Function FindWindow Lib "user32" Alias "FindWindowA" _
  (ByVal lpClassName As String, ByVal lpWindowName As String) As LongPtr
Private Declare PtrSafe Function GetWindowLong Lib "user32" Alias "GetWindowLongA" _
  (ByVal hwnd As LongPtr, ByVal nIndex As Long) As Long
Private Declare PtrSafe Function SetWindowLong Lib "user32" Alias "SetWindowLongA" _
  (ByVal hwnd As LongPtr, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Declare PtrSafe Function DrawMenuBar Lib "user32" (ByVal hwnd As LongPtr) As Long
Private Declare PtrSafe Function WindowFromObject Lib "oleacc" Alias "WindowFromAccessibleObject" _
  (ByVal pacc As Object, phwnd As LongPtr) As LongPtr
'===================================================================
'32bitのOffice環境の場合
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
  (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" _
  (ByVal hwnd As Long, ByVal nIndex As Long) As Long
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" _
  (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Declare Function DrawMenuBar Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function WindowFromObject Lib "oleacc" Alias "WindowFromAccessibleObject" _
  (ByVal pacc As Object, phwnd As Long) As Long
'===================================================================

Const WS_MAXIMIZEBOX = &H10000
Const WS_MINIMIZEBOX = &H20000
Const WS_THICKFRAME = &H40000 'サイズ変更
Const GWL_STYLE = (-16)

'kStyleMaxMin関数
'ユーザーフォームに最大化最小化ボタンを付けサイズ変更可能にする
Sub kStyleMaxMin(uf As UserForm)
  Dim hwnd
  WindowFromObject uf, hwnd
  SetWindowLong hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) Or WS_MAXIMIZEBOX Or _
    WS_MINIMIZEBOX Or WS_THICKFRAME
  DrawMenuBar hwnd
End Sub

'kStyleMaxMin関数の使用例
Private Sub UserForm_Initialize()
  kStyleMaxMin Me
End Sub

'サイズ変更時イベント
Private Sub UserForm_Resize() 
  If Height < InsideHeight Then Exit Sub '最小化された
  '各コントロールの位置やサイズを調整するコードを記述
  'Debug.Print Height; Width
End Sub

サイズ変更イベントにコメントを入れてありますが、最大化を行ってもコントロールのサイズは変更されません。

自分でコードを記述する必要があります。

ユーザーフォームの閉じるボタンの対策まとめ

ユーザーフォームを、閉じるボタンで閉じられなくすると、どうやってユーザーフォームを閉じるの? といった疑問が湧いてしまします。

だったら、ユーザーフォームを他のフォームに移動させる! ユーザーフォームを閉じる!そういったボタンを作成すればいいです。
もしくは、ファイルを終了させるボタンを作成すれば、問題は解決します。
もちろん、VBAで作成できます。
エクセルVBAで、ユーザーフォームを閉じるボタンで閉じられなくすると、うっかり、誤ってボタンを押してしまって、閉じてしまったという失敗が防げます。ボタンで、操作をコントロールすると、不要な動作を制御できます。

皆さんも是非、使ってみてください。