Solución:
De hecho, me he centrado bastante en las formas modales, porque eso es lo que se usa con más frecuencia. ¡Gracias por los comentarios sobre ese artículo!
Sin embargo, los principios son los mismos para las formas no modales: simplemente amplíe el Modelo-Vista-Presentador patrón descrito aproximadamente en el artículo vinculado y aquí.
La diferencia es que una forma no modal necesita un cambio de paradigma: ya no estás respondiendo a una secuencia preestablecida de eventos, sino que necesitas responder a algunos eventos asincrónicos. eventos eso puede suceder en cualquier momento, o no.
- Cuando se maneja un formulario modal, hay un “antes de mostrar” y luego un “después de ocultar” que se ejecuta inmediatamente después de que se oculta el formulario. Puede manejar cualquier cosa que suceda “mientras se muestra” mediante eventos.
- Cuando se maneja un formulario no modal, hay un “antes de mostrar”, y luego “mientras se muestra” y “después de mostrar” ambos deben manejarse a través de eventos.
Haga que su módulo de clase de presentador sea responsable de llevar a cabo la UserForm
ejemplo, a nivel de módulo y WithEvents
:
Option Explicit
Private WithEvents myModelessForm As UserForm1
El presentador Show
el método será Set
la instancia del formulario y mostrarla:
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
usted no desea que la instancia del formulario sea local para el procedimiento aquí, por lo que una variable local o a With
el bloque no puede funcionar: el objeto estará fuera de alcance antes de que usted lo desee. Es por eso que almacena la instancia en un campo privado, a nivel de módulo: ahora el formulario vive tanto como la instancia del presentador.
Ahora, debe hacer que el formulario “hable” con el presentador; la forma más fácil es exponer los eventos en el UserForm1
código subyacente: por ejemplo, si queremos que el usuario confirme la cancelación, agregaremos un ByRef
parámetro al evento, por lo que el controlador en el presentador puede pasar la información a la fuente del evento (es decir, al código del formulario):
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
Ahora el presentador puede manejar eso FormCancelled
evento:
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
Una forma no modal no típicamente Sin embargo, tiene botones “Aceptar” y “Cancelar”. Más bien, tendría una serie de funcionalidades expuestas, por ejemplo, una que abre algún diálogo modal UserForm2
que hace otra cosa; de nuevo, simplemente expones un evento y lo manejas en el presentador:
Public Event ShowGizmo()
Private Sub ShowGizmoButton_Click()
RaiseEvent ShowGizmo
End Sub
Y el presentador dice:
Private Sub myModelessForm_ShowGizmo()
With New GizmoPresenter
.Show
End With
End Sub
Tenga en cuenta que el modal UserForm2
es una preocupación de una clase de presentadores separada.
Por lo general, relaciono la vida útil de una instancia de formulario de usuario sin modo con la del libro de trabajo colocando código a lo largo de esas líneas detrás de 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
A continuación, puede consultar el código no modal a lo largo de su código utilizando, por ejemplo,
ThisWorkbook.MyForm.Show vbModeless
etc.
Para formularios no modales, use DoEvents junto con la propiedad de formulario de usuario personalizada.
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
‘/ Formulario de usuario
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