別のブックでユーザーフォームを起動する

作成元以外のブックでユーザーフォームを起動する方法について

Dim WithEvents Bkob As Workbook

Sub SetBook()
If Bkob Is Nothing Then
Set Bkob = zzzワークシート取得("テストシート").Parent
Else
Set Bkob = Nothing
End If
End Sub

Private Sub Bkob_Activate()
UserForm1.Show
End Sub

Private Sub Bkob_BeforeClose(Cancel As Boolean)
Unload UserForm1
Set Bkob = Nothing
End Sub

このコードは、ユーザーフォームを作成・保存しているブックのThisWorkbookモジュールの最上部にコピペしてください
※WithEventsは標準モジュールでは利用できません(エラーが発生します)

また、このコード中にzzzワークシート取得という関数を使用しています
これは開かれているブック全てのシートから引数に指定された文字列に完全一致するシートを取得する関数です
以下の記事にて公開・解説していますので、併せてそちらも確認・コピペしてください
コピペ先は本記事コードに連続して入力してもらって問題ありません

コードのコピーペーストの例
コードのコピーペーストの例

この画像のような感じで、連続してコピペしてもらって大丈夫です
もちろん関数の方だけ、標準モジュールに記載しても構いません
同じブック(プロジェクト)内であれば、どこでもOKです

使用場面

ユーザーフォームは作成したブックに依存しています
ユーザーフォームを開いた状態で、ブックを最小化するとフォームも一緒に最小化されます
また、別のブックをアクティブ状態にしたときはそのアクティブブックより背面に表示されます

ユーザーフォームを作成したブックではなく、別のブックで利用したい場面では通常の起動の仕方では出来ません

ブックのマクロの保存状態の確認
マクロの保存状態

この画像の様に、「TestBook.xlsx」にはマクロは保存されていません
そもそもマクロが有効なファイル形式ではありません
このブックに対してユーザーフォームを依存させたい訳です
そのユーザーフォームは「Wordpress.xlsm」に作成されています

よくある方法で、ブック自体を非表示にしてフォームだけを表示させる方法がありますが、この方法はExcel自体を非表示にすることで実現されます
この場合、表示処理を行う前に処理が強制終了した場合にExcelがバックグラウンドのプロセスに残ったままになってしまいます
かといって最小化をしてもフォームも引っ張られて、一緒に非表示になります
また、ウィンドウの位置をディスプレイ画面の外側に移動させたりする方法もありますが、これも上記同様で元に戻すコードの実行が必須になります

なによりこれらすべてに共通するのが、別のブックをアクティブにした時点でフォームは背面表示となり、なんの意味もなくなってしまう点です
この場合にはWinAPIによる画面の表示順の調整が必要になります
これは現在ではBit依存による対応が必要である点とMsgBox関数等のメッセージの前に表示されていた場合は操作不能のような状態になってしまいます

そこで利用するのが、例コードの流れになります
まずWithEventsを利用して、別のブックを取得させます
そのブックのActivateイベントでフォームのShowメソッドを実行します

こうすることにより、ユーザーフォームの依存先を処理作成のブック以外のブックにすることができます
処理自体は処理ブックに保存されていますが、実際にフォームを起動して処理を行うブックが別で指定できます

依存先が対象のブックになっているので、そのブックの最小化やアクティブ状態を共有することが出来ます

コード解説

この方法はWithEventsを理解することが必要ですので、理解できていない場合は以下の記事を確認してください

Dim WithEvents Bkob As Workbook

モジュールレベルの変数宣言を行っています
ここでWithEventsを利用することで、このオブジェクトにイベント処理を作成することが出来るようになります

通常WithEventsを利用する場合はクラスを使用しますが、このコードの様に代入する対象が1つと確定している場合は特に必要ありません
ただ標準モジュールには記載することが出来ないので、ユーザーフォームやThisWorkbookなどのオブジェクトモジュール内に作成します

この1行により、後続のイベント処理が有効になります

Sub SetBook()
If Bkob Is Nothing Then
Set Bkob = zzzワークシート取得("テストシート").Parent
Else
Set Bkob = Nothing
End If
End Sub

このプロシージャは、宣言した変数への代入と解放を行う処理です
オブジェクトが取得済みかどうかで分岐処理を行っています

単純に、取得されていれば解放して、取得されていなければ取得を行うものです
この取得処理を行う際に、別ブックのシートを指定することで処理ブック以外をこの変数に代入させることが出来ます

なお、取得関数に関しては記事に解説がありますのでここでは割愛します
この関数はWorksheetを返す関数で、取得を行いたいのはブックなので、Parentプロパティを使用して返されたWorksheetを含むブックを取得しています

このプロシージャをボタン等によりユーザーのタイミングで実行できるようにします
と、いうのもこの処理では対象のブックが存在する必要があります
ですが、イベント等の自動化を行ってしまうと、その取得のタイミングで対象ブックを開いていない可能性があるからです

Private Sub Bkob_Activate()
UserForm1.Show
End Sub

作成した変数のActivateイベント処理です
ここで指定のフォームを起動しています

Activateイベントなので、別のブックからこのブックへフォーカスが移動してきたときに発生することになります
このことからも処理用ブックで一度ボタン等の処理実行を行うことで、自然な流れで対象ブックでフォームを起動することが出来ます

また、ここでは割愛していますが、Showメソッドがアクティブになるたびに実行されると、表示位置が初期状態の場所に移動します
入力内容が失われることは無いので、気にしなければ大したことではないのですが、表示位置がリセットされることが気になる場合は、ここの処理でフォームが起動されているかの判定処理を入れておけばいいです
判定処理に関しては以下の記事を確認してください

Private Sub Bkob_BeforeClose(Cancel As Boolean)
Unload UserForm1
Set Bkob = Nothing
End Sub

作成した変数のBeforeCloseイベントです
ブックが閉じられる前に発生するイベントですが、この対象ブックが閉じられるということはフォーム表示処理が不要になることになるので、ここでフォームの終了と変数の解放を行っています

変数の解放は、処理用ブックでも行っているので2Way仕様ということになります
この解放は、処理用ブックの変数であるため、対象ブックを終了しても自動的に解放されることはありません
必ず明示的に解放を行っておきましょう

あと、当然ですが処理用ブックを閉じた場合はフォームも強制終了して処理の実行も不能になります
処理の仕様上あまり注意が必要とは思いませんが、間違って終了してしまわないように組み上げてください

使用するメリット

マクロ処理を作成していないブックに対して、自由に処理をユーザーが任意のタイミングで実行できることが最大のメリットだと思います

例えば同じフォーマットのブックに全てにフォーム処理を作成するのではなく、処理用ブックを1つ作成してそれを開いてから作業ブックを開くようにすればメンテナンス性も大きく向上します

また、作業用ブックにはマクロを保存する必要が無くなるのでマクロ無効の通常ブックとして扱うことが出来ます
場合によってはマクロを保存してはいけないブックも会社規定により存在するかもしれません
そんな場合でも、ユーザーフォームを利用した処理が利用できます

さらに、フォーム等の処理のメンテナンスを行った際に作業用ブックを上書きする必要が無いため、データの上書きを気にする必要もなくなります
そもそも処理用ブックにはデータが存在しないからです

今回の例では特定のブックに対しての依存先変更でしたが、WithEventsの変数をシート型にして特定のシートでのみ起動されるフォームとすることも可能です

Excel間でのフォームの表示問題なら、この方法で対応可能です
別のアプリ間での表示問題になると、やはりWinAPIを利用するしかなくなりますが、それはまた別の話になります

ユーザーフォームの依存先の変更は、比較的応用の幅が広いと思います
マウスホイールが利用できない等、使い勝手が少し時代遅れになってきたユーザーフォームですが、まだまだ使える場面が多いですね

最後に実際にこれらの処理を組み込んだ場合の動きの動画になります
コード解説が行われているわけでは無いので、参考程度に確認してください

コードを適用したときの実際の動きの動画です