イントロ:
私は、ユーザーフォームを表示することがベストプラクティスであることを認識しています。
QueryClose
を処理します(If CloseMode = vbFormControlMenu ...
)Unload Me
を実行せず、臆病なMe.Hide
命令のみを実行します([x] -itとCancel = True
を介した最終的な自己破壊を防止した後).IsCancelled=True
)便利なリンク
優れた概要 "UserForm1.Show?"は https://rubberduckvba.wordpress.com/2017/10/25/userform1-showにあります/ および多数の例でSO回答(Mathieu Guindon別名Mat's MugおよびRubberDuckへのthx)。
さらなる選択(►2019年5月1日現在の編集)
1)モーダルユーザーフォームの実例
私が理解している限り(そして私が学ぼうとしている限り)、次のコードはモーダルUFに対しては問題ないはずです:
ケース1a).. UFインスタンスのローカル変数を使用、よく見られるように:
Public Sub ShowFormA
Dim ufA As UserForm1
Set ufA = New UserForm1
' show userform
ufA.Show ' equivalent to: ufA.Show vbModal
' handle data after user okay
If Not ufA.IsCancelled Then
' do something ...
End If
' >> object reference destroyed expressly (as seen in some examples)
unload ufA
End Sub
ケース1b)..ローカル変数はありませんが、With New
コードブロックを使用しています:
' ----------------------------------------------------------
' >> no need to destruct object reference expressly,
' as it will be destroyed whenever exiting the with block
' ----------------------------------------------------------
With New UserForm1
.Show ' equivalent to: ufA.Show vbModal
' handle data after user okay
If Not .IsCancelled Then
' do something ...
End If
End With
2)問題
[〜#〜] modeless [〜#〜]UserFormインスタンスを使用すると問題が発生します。
さて、withブロックメソッド(1bを参照)は、オブジェクト参照をx-itした後に破棄するのに十分なはずです。
With New UserForm1
.Show vbModeless ' << show modeless uf
End With
しかし、私がしようとすると
Unload
命令の後にローカル変数(「ufA」など)を使用してバプテスマを受けた場合は、Show
フォームにフォームがMODELESSであるという正確な理由により、すべてのコード行が一度に実行されます。
3)質問
A)MODELESSフォームの呼び出しコードによる正しく報告されたUserFormのキャンセル、およびb)ローカル変数を使用している場合の(必要な?)アンロードをどのように処理できますか?
確かに、私はモーダルフォームにかなり焦点を当ててきました-それが最も一般的に使用されているものだからです。その記事へのフィードバックをありがとう!
ただし、原則は非モーダルフォームでも同じです。リンクされた記事で大まかに概説されているModel-View-Presenterパターンと ここ を拡張するだけです。
違いは、非モーダルフォームにはパラダイムシフトが必要なことです。事前に設定された一連のイベントに応答しなくなります。むしろ、非同期イベントに応答する必要があります。いつでも発生する可能性があります。
プレゼンタークラスモジュールに、モジュールレベルおよびUserForm
でWithEvents
インスタンスを保持する責任を持たせます。
Option Explicit
Private WithEvents myModelessForm As UserForm1
プレゼンターのShow
メソッドはフォームインスタンスをSet
し、それを表示します。
Public Sub Show()
'If Not myModelessForm Is Nothing Then
' myModelessForm.Visible = True 'just to ensure visibility & honor the .Show call
' Exit Sub
'End If
Set myModelessForm = New UserForm1
'...
myModelessForm.Show vbModeless
End Sub
あなたしないフォームインスタンスをここのプロシージャに対してローカルにしたいので、ローカル変数またはWith
ブロックは機能しません:オブジェクトはスコープ外になりますあなたがそれを意味する前に。これが、インスタンスをモジュールレベルのプライベートフィールドに保存する理由です。これで、フォームはプレゼンターインスタンスと同じように存続します。
ここで、フォームをプレゼンターに「話す」必要があります。最も簡単な方法は、UserForm1
コードビハインドでイベントを公開することです。たとえば、ユーザーにキャンセルを確認してもらいたい場合は、ByRef
を追加します。プレゼンターのハンドラーが情報をイベントソースに戻す(つまり、フォームコードに戻す)ことができるように、パラメーターをイベントに設定します。
Option Explicit
'...private fields, model, etc...
Public Event FormConfirmed()
Public Event FormCancelled(ByRef Cancel as Boolean)
'returns True if cancellation was cancelled by handler
Private Function OnCancel() As Boolean
Dim cancelCancellation As Boolean
RaiseEvent FormCancelled(cancelCancellation)
If Not cancelCancellation Then Me.Hide
OnCancel = cancelCancellation
End Function
Private Sub CancelButton_Click()
OnCancel
End Sub
Private Sub OkButton_Click()
Me.Hide
RaiseEvent FormConfirmed
End Sub
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
If CloseMode = VbQueryClose.vbFormControlMenu Then
Cancel = Not OnCancel
End If
End Sub
これで、プレゼンターはそのFormCancelled
イベントを処理できます。
Private Sub myModelessForm_FormCancelled(ByRef Cancel As Boolean)
'setting Cancel to True will leave the form open
Cancel = MsgBox("Cancel this operation?", vbYesNo + vbExclamation) = vbNo
If Not Cancel Then
' modeless form was cancelled and is now hidden.
' ...
Set myModelessForm = Nothing
End If
End Sub
Private Sub myModelessForm_FormConfirmed()
'form was okayed and is now hidden.
'...
Set myModelessForm = Nothing
End Sub
ただし、非モーダルフォームには通常「OK」ボタンと「キャンセル」ボタンはありません。むしろ、いくつかの機能が公開されます。たとえば、他のことを行うモーダルダイアログUserForm2
を表示する機能があります。ここでも、イベントを公開して、プレゼンターで処理します。
Public Event ShowGizmo()
Private Sub ShowGizmoButton_Click()
RaiseEvent ShowGizmo
End Sub
そしてプレゼンターは行きます:
Private Sub myModelessForm_ShowGizmo()
With New GizmoPresenter
.Show
End With
End Sub
モーダルUserForm2
は、別のプレゼンタークラスの懸念事項であることに注意してください。
モードレスフォームの場合は、カスタムuserformプロパティと組み合わせてDoEventsを使用します。
Sub test()
Dim frm As New UserForm1
frm.Show vbModeless
Do
DoEvents
If frm.Cancelled Then
Unload frm
Exit Do
End If
Loop Until False
MsgBox "You closed the modeless form."
'/ Using With
With New UserForm1
.Show vbModeless
Do
DoEvents
If .Cancelled Then Exit Do
Loop Until False
End With
MsgBox "You closed the modeless form (with)"
End Sub
'/ユーザーフォーム
Private m_bCancelled As Boolean
Public Property Get Cancelled() As Boolean
Cancelled = m_bCancelled
End Property
Public Property Let Cancelled(ByVal bNewValue As Boolean)
m_bCancelled = bNewValue
End Property
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
Me.Cancelled = True
Cancel = 1
Me.Hide
End Sub
私は通常、ThisWorkbookの背後にある行に沿ってコードを配置することにより、モードレスユーザーフォームインスタンスの存続期間をワークブックの存続期間に結び付けます。
Option Explicit
Private m_MyForm As UserForm1
Private Sub Workbook_BeforeClose(Cancel As Boolean)
If Not m_MyForm Is Nothing Then
Unload m_MyForm
Set m_MyForm = Nothing
End If
End Sub
Friend Property Get MyForm() As UserForm1
If m_MyForm Is Nothing Then
Set m_MyForm = New UserForm1
End If
Set MyForm = m_MyForm
End Property
次に、コード全体でモードレスコードを参照できます。
ThisWorkbook.MyForm.Show vbModeless
等.