VBAのユーザーフォーム(UserForm)サイズを可変化させる方法

VBAでは「UserForm」というマクロ用の入力ウィンドウを作成することができます。
しかしながらUserFormは通常のウィンドウとは違い、拡大/縮小や最大化/最小化といったウィンドウサイズの変更をすることはできません。
場合によってはユーザーが自身の手でUserFormのサイズを変更したいということもあるでしょう。
という訳で今回はWindows APIを利用してUserFormサイズを可変化する方法を解説していきます。
今回学ぶことのできる内容は以下のとおりです。
Windows APIについて
GetActiveWindow関数の使い方
GetWindowLong関数の使い方
SetWindowLong関数の使い方
DrawMenuBar関数の使い方
上記の関数を使ってUserFormのサイズを可変化させる方法
内容は少し難しく感じるかもしれませんがUserFormサイズの可変化はサンプルコードのコピペだけで実現可能なので、コードの内容はどうでもいいという場合でもすぐに実装できるようになっています。
UserFormサイズを可変化するためのAPI関数
UserFormサイズを可変化するには下記の4つのWindows APIの関数を使います。
・GetActiveWindow関数 :現在アクティブなウィンドウのウィンドウハンドルを取得する
・GetWindowLong関数 :指定されたウィンドウに関する情報を取得する
・SetWindowLong関数 :指定されたウィンドウの属性を変更する
・DrawMenuBar関数 :ウィンドウのメニューバーを再描画する
各関数は下記のような順番で使用します。
② GetWindowLong関数で取得したUseFormのウィンドウ情報を取得
③ SetWindowLong関数で取得したウィンドウ情報に「可変化」に関する情報を追加
④ ウィンドウのメニューバーを再描画 (=③の内容を反映させる)
上記の処理を行うことで最終的に下のgif画像のようにUserFormのサイズを変更することができるようになります。(ウィンドウ右上の[最大化][最小化]のボタンはUserFormを表示とともに追加されます)
Windows APIとは
これら関数はWindows APIで用意されているものでVBAとは別のものです。Windows APIとは簡単にいえばWindowsに用意されている機能がまとまっているセットのようなものです。
Windows APIにはウィンドウサイズを変更する関数以外にも様々な関数があり、VBA上でそれらを呼び出すことで、Windowsに用意されている様々な機能を使用することができます。(たとえばWindowsのシステム効果音を鳴らしたり、指定した秒数だけ処理を止めることができます)
ちなみに名前からわかると思いますが、Windows APIではWindowsの機能を呼び出しているだけので基本的にはWindows以外のOSでは使用することはできません。
UserFormサイズを可変化する各関数の使い方
マウス操作の関数を使うにははじめに「Windous APIの関数を使うよ」と宣言する必要があります。
※宣言をしないと各関数は使えずエラーになるので書き忘れに注意しましょう。
使用しているWindowsが32bitか64bitかによって宣言時に書く内容が変わってきます。
以下のどちらかをコードの一番初め(Option Explicitの次の行あたり)に書いておくことで、そのモジュール内で各関数を使うことができるようになります。
‘【GetActiveWindow関数】
Declare PtrSafe Function GetActiveWindow Lib “user32” () As LongPtr
‘【GetWindowLong関数】
Declare PtrSafe Function GetWindowLong Lib “user32” Alias “GetWindowLongA” _
(ByVal hwnd As LongPtr, ByVal nIndex As Long) As Long
‘【SetWindowLong関数】
Declare PtrSafe Function SetWindowLong Lib “user32” Alias “SetWindowLongA” _
(ByVal hwnd As LongPtr, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
‘【DrawMenuBar関数】
Declare PtrSafe Function DrawMenuBar Lib “user32” (ByVal hwnd As LongPtr) As Long
‘【GetActiveWindow関数】
Declare Function GetActiveWindow Lib “user32” () As Long
‘【GetWindowLong関数】
Declare Function GetWindowLong Lib “user32” Alias “GetWindowLongA” _
(ByVal hwnd As Long, ByVal nIndex As Long) As Long
‘【SetWindowLong関数】
Declare Function SetWindowLong Lib “user32” Alias “SetWindowLongA” _
(ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
‘【DrawMenuBar関数】
Declare Function DrawMenuBar Lib “user32” (ByVal hwnd As Long) As Long
※操作環境のbitと構文があっていないとエラーになるので注意しましょう。
上記のどちらを書けばいいかわからない場合は以下のコードをコピペして、モジュールの最上部に書いておきましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#If Win64 Then '【GetActiveWindow関数】 Declare PtrSafe Function GetActiveWindow Lib "user32" () As LongPtr '【GetWindowLong関数】 Declare PtrSafe Function GetWindowLong Lib "user32" Alias "GetWindowLongA" _ (ByVal hwnd As LongPtr, ByVal nIndex As Long) As Long '【SetWindowLong関数】 Declare PtrSafe Function SetWindowLong Lib "user32" Alias "SetWindowLongA" _ (ByVal hwnd As LongPtr, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long '【DrawMenuBar関数】 Declare PtrSafe Function DrawMenuBar Lib "user32" (ByVal hwnd As LongPtr) As Long #Else '【GetActiveWindow関数】 Declare Function GetActiveWindow Lib "user32" () As Long '【GetWindowLong関数】 Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" _ (ByVal hwnd As Long, ByVal nIndex As Long) As Long '【SetWindowLong関数】 Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" _ (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long '【DrawMenuBar関数】 Declare Function DrawMenuBar Lib "user32" (ByVal hwnd As Long) As Long #End If |
この構文を書いておくことで自動的に使うことのできる方の構文が使用されます。
VBE上では使えない方の構文が赤色で表示される場合がありますが、実行に影響はありません。
各関数の宣言文は「Private/Public」を付けて各関数の有効範囲を指定することもできます。
・Private Declare PtrSafe Function~ :モジュール内で呼び出し有効
・Public Declare PtrSafe Function~ :モジュール外で呼び出し有効
また、各関数ではあらかじめ決められた値である”定数”を使用します。
これらも関数の宣言と同じ場所にまとめて記載しておく必要があるので注意しましょう。
1 2 3 4 5 |
'4つの定数を定義 Private Const GWL_STYLE = -16 Private Const WS_MAXIMIZEBOX = &H10000 '最大化ボタン Private Const WS_MINIMIZEBOX = &H20000 '最小化ボタン Private Const WS_THICKFRAME = &H40000 'ウィンドウサイズ可変化 |
UserForm可変化サンプルコード
これまでの内容をまとめたサンプルコードを紹介します。
サンプルコードはユーザーフォーム(UserFom1)上にコマンドボタン(CommandButton1)があることを想定しています。下のgif画像のようにユーザーフォームのサイズが変わるにつれてコマンドボタンのサイズも追従するようなコードとなっています。
つまりは、サンプルコードと同じ考えでコントロールを設定することで、ユーザーフォーム上のオブジェクトのサイズもフレキシブルに変化させることが可能になります。
サンプルコードは下記の2つです。
モジュールとUserFormの両者にコードを書く必要があるので注意してください。
また上記の通りUserFormだけでなくCommandButtonも作成しておいてください。
標準モジュールコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
Option Explicit Private Const GWL_STYLE = -16 Private Const WS_MAXIMIZEBOX = &H10000 Private Const WS_MINIMIZEBOX = &H20000 Private Const WS_THICKFRAME = &H40000 #If Win64 Then '【GetActiveWindow関数】 Declare PtrSafe Function GetActiveWindow Lib "user32" () As LongPtr '【GetWindowLong関数】 Declare PtrSafe Function GetWindowLong Lib "user32" Alias "GetWindowLongA" _ (ByVal hwnd As LongPtr, ByVal nIndex As Long) As Long '【SetWindowLong関数】 Declare PtrSafe Function SetWindowLong Lib "user32" Alias "SetWindowLongA" _ (ByVal hwnd As LongPtr, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long '【DrawMenuBar関数】 Declare PtrSafe Function DrawMenuBar Lib "user32" (ByVal hwnd As LongPtr) As Long Dim hwnd As LongPtr '64bitではLongPtr型 Dim rc As LongPtr '64bitではLongPtr型 #Else '【GetActiveWindow関数】 Declare Function GetActiveWindow Lib "user32" () As Long '【GetWindowLong関数】 Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" _ (ByVal hwnd As Long, ByVal nIndex As Long) As Long '【SetWindowLong関数】 Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" _ (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long '【DrawMenuBar関数】 Declare Function DrawMenuBar Lib "user32" (ByVal hwnd As Long) As Long Dim hwnd As Long '32bitではLong型 Dim rc As Long '32bitではLong型 #End If '-------------------------------------------------------------------------- Sub Main() UserForm1.Show End Sub '-------------------------------------------------------------------------- Public Sub ResizeForm() 'アクティブウィンドウ(UserForm1)を取得 hwnd = GetActiveWindow() '取得したウインドウのスタイルを取得 Dim style As Long style = GetWindowLong(hwnd, GWL_STYLE) '取得したウインドウのスタイルにサイズ可変+最大化ボタン+最小化ボタン追加 style = style Or WS_THICKFRAME Or WS_MAXIMIZEBOX Or WS_MINIMIZEBOX rc = SetWindowLong(hwnd, GWL_STYLE, style) 'ウインドウのスタイルを再描画 rc = DrawMenuBar(hwnd) End Sub |
各API関数の書き方は上記の通り(Sub ResizeForm)でほぼ定型文です。
各関数でどのような処理をしているかは上記コードのコメント文を参照してください。
34bit/64bitによる書き方の違いはありませんが変数の型だけは違うので注意しておきましょう。
UserFormコード
1 2 3 4 5 6 7 8 9 10 11 12 13 |
Private Sub UserForm_Activate() Call ResizeForm End Sub '------------------------------------------------------------------------- Private Sub UserForm_Resize() 'CommandButtonサイズの追従(If文はサイズが0以下になる発生するエラー対策) If Me.Width > 100 Then Me.CommandButton1.Width = Me.Width - 40 If Me.Height > 100 Then Me.CommandButton1.Height = Me.Height - 55 End Sub |
UserFormとCommandButtonを作成ののち、上記コードをコピペしてSub Main()を実行すれば先程のgif画像のように可変化できるUserFormを表示することができます。あとは作成するマクロに合わせて普段通りにコントロールや処理を追加していくだけです。
まとめ
今回はWindows APIを使ってUserFormのサイズを可変化する方法についてでした。
ユーザー視点に立つとウィンドウのサイズ変更機能はあると案外助かるものです。
サンプルコードのようにコントロールのサイズも追従させるとなると、多少コードは長くなってしまいますがかなりユーザーファーストなマクロにすることができます。
マクロによってはサイズ変更自体不要なものもあるので、作成する機能に合わせて実装してみるのも悪くことと思います。