c++ - uso - que es una caja de dialogo en visual basic
Usar CreateDialog en VBA en un intento de crear cuadros de diálogo sin modo (4)
Esta respuesta, como la de Cheezsteak''s no trata directamente los problemas que tienes con CreateDialog
. Se dirige al objetivo final de crear un cuadro de diálogo no modal.
Mi sugerencia es usar un UserForm para lograr esto. Su método Show toma un parámetro opcional que determina si la forma de usuario se muestra o no como un formulario modal o no modal.
De la documentación de MSDN:
modal Opcional. Valor booleano que determina si el UserForm es modal o no modal.
- Crea un UserForm y diseña según tus necesidades .
En el código que crea la instancia de UserForm, simplemente páselo por la constante
vbModeless
.Option Explicit Private frm As UserForm1 Sub test2() Set frm = New UserForm1 frm.Show vbModeless End Sub
Si le preocupa sobrecargar su proyecto con formularios, no lo haga. Simplemente crea el formulario sobre la marcha .
Quiero crear un cuadro de diálogo emergente no modal en VBA 7.0. Hasta ahora, la ruta más prometedora parece ser CreateDialog
.
Primero probé CreateDialogW
y recibí el Entry point not found for CreateDialogW in DLL
.
Después de abrir la DLL, verifiqué que esta función no estaba en la lista. La referencia de MSDN vinculada anteriormente muestra User32 como el DLL para esta función y enumera los nombres de función CreateDialogW
y CreateDialogA
(Unicode / ansi respectivamente), pero no están listados en esta DLL en mi computadora (Win 7 professional, 64bit).
Entonces, mirando la lista de funciones que están en la DLL, vi las funciones CreateDialogParam
y CreateDialogIndirectParam
(versiones Ansi y Unicode de cada una).
Intenté seguir el MSDN y convertir los ejemplos C en VB, pero me falta algo en alguna parte y estoy atascado porque no sé lo que estoy haciendo mal. El código se compila y se ejecuta sin errores, pero no ocurre nada en la llamada a la API; se ejecuta, pero no ocurre nada.
Si alguien pudiera darme algunos consejos en la dirección correcta, lo agradecería enormemente. Mi solución actual apesta, y realmente me gustaría cerrar este proyecto.
Option Explicit
''Reference conversion of C to VB type declarations here
''http://msdn.microsoft.com/en-us/library/aa261773(v=vs.60).aspx
''Declare function to Win API CreateDialog function
''http://msdn.microsoft.com/en-us/library/ms645434(v=vs.85).aspx
Private Declare PtrSafe Function CreateDialog Lib "User32.dll" Alias "CreateDialogParamW" _
(ByVal lpTemplateName As LongPtr, _
ByRef lpDialogFunc As DIALOGPROC, _
ByVal dwInitParam As Long, _
Optional ByVal hInstance As Long, _
Optional ByVal hWndParent As Long) _
As Long
''Windows Style Constants
''http://msdn.microsoft.com/en-us/library/windows/desktop/ms632600(v=vs.85).aspx
Public Const WS_BORDER As Long = &H800000
Public Const WS_CAPTION As Long = &HC00000
Public Const WS_CHILD As Long = &H40000000
Public Const WS_CHILDWINDOW As Long = &H40000000
Public Const WS_CLIPCHILDREN As Long = &H2000000
Public Const WS_CLIPSIBLINGS As Long = &H4000000
Public Const WS_DISABLED As Long = &H8000000
Public Const WS_DLGFRAME As Long = &H400000
Public Const WS_GROUP As Long = &H20000
Public Const WS_HSCROLL As Long = &H100000
Public Const WS_ICONIC As Long = &H20000000
Public Const WS_MAXIMIZE As Long = &H1000000
Public Const WS_MAXIMIZEBOX As Long = &H10000
Public Const WS_MINIMIZE As Long = &H20000000
Public Const WS_MINIMIZEBOX As Long = &H20000
Public Const WS_OVERLAPPED As Long = &H0
Public Const WS_POPUP As Long = &H80000000
Public Const WS_SIZEBOX As Long = &H40000
Public Const WS_SYSMENU As Long = &H80000
Public Const WS_TABSTOP As Long = &H10000
Public Const WS_THICKFRAME As Long = &H40000
Public Const WS_TILED As Long = &H0
Public Const WS_VISIBLE As Long = &H10000000
Public Const WS_VSCROLL As Long = &H200000
Public Const WS_OVERLAPPEDWINDOW As Long = (WS_OVERLAPPED + WS_CAPTION + WS_SYSMENU + WS_THICKFRAME + WS_MINIMIZEBOX + WS_MAXIMIZEBOX)
Public Const WS_TILEDWINDOW As Long = (WS_OVERLAPPED + WS_CAPTION + WS_SYSMENU + WS_THICKFRAME + WS_MINIMIZEBOX + WS_MAXIMIZEBOX)
Public Const WS_POPUPWINDOW As Long = (WS_POPUP + WS_BORDER + WS_SYSMENU)
''Declare custom type for lpDialogFunc argument
''http://msdn.microsoft.com/en-us/library/windows/desktop/ms645469(v=vs.85).aspx
Public Type DIALOGPROC
hwndDlg As Long
uMsg As LongPtr
wparam As Long
lparam As Long
End Type
''MAKEINTRESOURCE Macro emulation
''http://msdn.microsoft.com/en-us/library/windows/desktop/ms648029(v=vs.85).aspx
''Bitwise function example found here: http://support.microsoft.com/kb/112651
''VB conversion found here: https://groups.google.com/forum/#!topic/microsoft.public.vb.winapi/UaK3S-bJaiQ _
modified with strong typing and to use string pointers for VB7
Private Function MAKEINTRESOURCE(ByVal lID As Long) As LongPtr
MAKEINTRESOURCE = StrPtr("#" & CStr(MAKELONG(lID, 0)))
End Function
Private Function MAKELONG(ByRef wLow As Long, ByRef wHi As Long)
''Declare variables
Dim LoLO As Long
Dim HiLO As Long
Dim LoHI As Long
Dim HiHI As Long
''Get the HIGH and LOW order words from the long integer value
GetHiLoWord wLow, LoLO, HiLO
GetHiLoWord wHi, LoHI, HiHI
If (wHi And &H8000&) Then
MAKELONG = (((wHi And &H7FFF&) * 65536) Or (wLow And &HFFFF&)) Or &H80000000
Else
MAKELONG = LoLO Or (&H10000 * LoHI)
''MAKELONG = ((wHi * 65535) + wLow)
End If
End Function
Private Function GetHiLoWord(lparam As Long, LOWORD As Long, HIWORD As Long)
''This is the LOWORD of the lParam:
LOWORD = lparam And &HFFFF&
''LOWORD now equals 65,535 or &HFFFF
''This is the HIWORD of the lParam:
HIWORD = lparam / &H10000 And &HFFFF&
''HIWORD now equals 30,583 or &H7777
GetHiLoWord = 1
End Function
Public Function TstDialog()
Dim dpDialog As DIALOGPROC
dpDialog.hwndDlg = 0
dpDialog.uMsg = StrPtr("TEST")
dpDialog.lparam = 0
dpDialog.wparam = 0
CreateDialog hInstance:=0, lpTemplateName:=MAKEINTRESOURCE(WS_POPUPWINDOW + WS_VISIBLE), lpDialogFunc:=dpDialog, dwInitParam:=&H110
End Function
Esto se puede hacer funcionar, aunque si intentas hacer que funcione es otra cuestión. Tengo una versión funcional que muestra un cuadro de diálogo vacío. No tengo más tiempo esta noche para terminar con los controles reales en el diálogo, pero estoy publicando con la esperanza de que te ayude a empezar.
Primero debe olvidarse de CreateDialog porque requieren que la plantilla de diálogo esté en la sección de recursos. Puede usar CreateDialogIndirectParam para crear un diálogo desde una plantilla de diálogo en memoria. Necesitarás esto:
Private Type DLGTEMPLATE
style As Long
dwExtendedStyle As Long
cdit As Integer
x As Integer
y As Integer
cx As Integer
cy As Integer
End Type
Private Type DLGITEMTEMPLATE
style As Long
dwExtendedStyle As Long
x As Integer
y As Integer
cx As Integer
cy As Integer
id As Integer
End Type
Private Type DLG
dlgtemp As dlgtemplate
menu As Long
classname As String
title As String
End Type
Private Declare PtrSafe Function CreateDialogIndirectParam Lib "User32.dll" Alias "CreateDialogIndirectParamW" _
(ByVal hInstance As Long, _
ByRef lpTemplate As DLGTEMPLATE, _
ByVal hWndParent As Long, _
ByVal lpDialogFunc As LongPtr, _
ByVal lParamInit As Long) _
As LongPtr
Const WM_INITDIALOG As Long = &H110
Const DS_CENTER As Long = &H800&
Const DS_SETFONT As Long = &H40
Const DS_MODALFRAME As Long = &H80
Const WS_EX_APPWINDOW As Long = &H40000
Entonces llámalo así:
Dim d As DLG
d.dlgtemp.style = DS_MODALFRAME + WS_POPUP + WS_VISIBLE + WS_CAPTION + WS_SYSMENU
d.dlgtemp.dwExtendedStyle = WS_EX_APPWINDOW
d.dlgtemp.cdit = 0
d.dlgtemp.x = 100
d.dlgtemp.y = 100
d.dlgtemp.cx = 200
d.dlgtemp.cy = 200
d.menu = 0
d.title = "Test"
d.classname = "Test"
CreateDialogIndirectParam 0, d.dlgtemp, 0, AddressOf DlgFunc, 0
con DlgFunc mirando algo como esto:
Public Function DlgFunc(ByVal hwndDlg As LongPtr, ByVal uMsg As LongPtr, ByVal wParam As LongPtr, ByVal lParam As LongPtr) As LongPtr
If uMsg = h110 Then '' = WM_INITDIALOG - you should make a const for the various window messages you''ll need...
DlgFunc = True
Else
DlgFunc = False
End If
End Function
Ha pasado más de una década desde la última vez que hice algo de esto. Pero si está decidido a seguir este camino, creo que este enfoque es el más prometedor: el siguiente paso es adaptar la estructura DLG para agregar algunos miembros DLGITEMTEMPLATE, establezca d.dlgtemp.cdit en la cantidad de controles en su diálogo, y comience a manejar mensajes de control en su DlgFunc.
No quiero restarle profundidad y una buena investigación, pero hay posibles soluciones para crear cuadros de diálogo dinámicos en VBA. Ese fue el problema original antes de que el invocador valientemente se sumergiera en el agujero del conejo con CreateDialog
. Así que esta respuesta es para el problema original de crear dinámicamente cuadros de diálogo sin modo en VBA y no cómo usar CreateDialog
. No puedo ayudar allí.
Como se mencionó anteriormente, se pueden crear cuadros de diálogo no modal usando un UserForm, pero no queremos formularios inútiles que ensucian el proyecto. La solución que he logrado utiliza la Biblioteca de Extensibilidad VBA de Microsoft. En resumen, creamos una clase que agrega una forma de usuario genérica al proyecto en construcción y elimina la forma de usuario en la terminación.
También tenga en cuenta que esto se prueba utilizando Excel VBA. No tengo SolidWorks, así que no puedo probarlo allí.
Crudo hecho como un módulo de clase.
Option Explicit
Private pUserForm As VBIDE.VBComponent
Private Sub Class_Initialize()
'' Add the userform when created ''
Set pUserForm = ThisWorkbook.VBProject.VBComponents.Add(VBIDE.vbext_ct_MSForm)
End Sub
Private Sub Class_Terminate()
'' remove the userform when instance is deleted ''
ThisWorkbook.VBProject.VBComponenets.Remove pUserForm
End Sub
Public Property Get UserForm() As VBIDE.VBComponent
'' allow crude access to modify the userform ''
'' ideally this will be replaced with more useful methods ''
Set UserForm = pUserForm
End Property
Public Sub Show(ByVal mode As Integer)
VBA.UserForms.Add(pUserForm.Name).Show mode
End Sub
Idealmente, esta clase estaría mejor desarrollada y permitiría un acceso más fácil a la modificación de la forma, pero por ahora es una solución.
Pruebas
Private Sub TestModelessLocal()
Dim localDialog As New Dialog
localDialog.UserForm.Properties("Caption") = "Hello World"
localDialog.Show vbModeless
End Sub
Debería ver aparecer y desaparecer una ventana ya que localDialog
sale del alcance. Se creó un UserForm1
en su VBProject y se eliminó.
Esta prueba creará un cuadro de diálogo persistente. Desafortunadamente, UserForm1
permanecerá en su VBProject como globalDialog
todavía está definido. Restablecer el proyecto no eliminará la forma de usuario.
Dim globalDialog As Dialog
Private Sub TestModeless()
Set globalDialog = New Dialog
globalDialog.UserForm.Properties("Caption") = "Hello World"
globalDialog.Show vbModeless
''Set globalDialog = Nothing closes window and removes the userform ''
''Set gloablDialog = new Dialog should delete userform1 after added userform2''
End Sub
Así que nunca lo use en el alcance del módulo.
En conclusión, es una solución fea pero es mucho menos fea que lo que el Asker estaba tratando de hacer.
Tienes un comienzo muy pobre en este proyecto. Usted CreateDialogParam completamente el orden de los argumentos para CreateDialogParam , observe cómo el argumento hInstance
es primero, el argumento dwInitParam
es el último.
Hundió por completo la declaración DIALOGPROC, es un puntero a la función. Eso requiere LongPtr
en la declaración y el operador AddressOf
cuando realiza la llamada.
Este fue solo el primer 1% de hacerlo funcionar. El siguiente problema es que tendrá que escribir un procedimiento de diálogo funcional (el objetivo de AddressOf
) que maneja las notificaciones que genera el diálogo. Cosas básicas, como reconocer que el usuario hizo clic en el botón Aceptar. Muy difícil de escribir cuando no sabes lo suficiente acerca de la programación de WinAPI, los pequeños errores son grandes problemas no diagnosticables en tiempo de ejecución.
Eso es solo algo pequeño, hay problemas mucho más grandes. El argumento lpTemplateName
es un obstáculo muy serio. Debe ser un identificador de recursos, del tipo generado por "rc.exe" y agregado al archivo ejecutable por el vinculador. No puede volver a vincular SolidWorks. Un diálogo no modal requiere ayuda del ciclo de mensajes, debe llamar a IsDialogMessage()
. No puede convencer a SolidWorks para que haga esta llamada por usted. Sin él, el diálogo se comporta mal en formas difíciles de diagnosticar, como tabular no funcionará.
Tienes que saber cuándo no tienes absolutamente ninguna posibilidad de hacerlo funcionar. No puedes hacer que funcione.