Newsgroup: it.comp.lang.visual-basic
Titolo: Menu' dinamici

Qualcuno di voi sa come aggiungere dinamicamente i menu' ad un form vb.
Mi spiego meglio: dovrei far in modo di creare una serie di menu' prendendo
la struttura dall'esterno (tipo file ini). Ho provato a creare un array di
menu' ma non mi permette di ottenere quello che mi serve, tipo:
Menu(1)                    Menu(2)                      Menu(3)
...
  SubMenu(1,1)            SubMenu(2,1)             SubMenu(3,1)
  SubMenu(1,2)            SubMenu(2,2)
  ....                               ....
ho creato quindi Menu(1) e SubMenu(1,1) come i primi elementi di due array
di menu' e cosi' posso creare la struttura di Menu(1) senza problemi
caricando un array di SubMenu(1,1), ma quando carico un nuovo elemento di
Menu(1) creando Menu(2) non mi crea SubMenu(2,1) e quindi non posso crearne
un array... :-(
Spero di essere stato abbastanza chiaro.
Grazie a tutti per l'interessamento.
Riccardo.

 Spero di essere stato abbastanza chiaro.
Si sei stato chiaro, ma non so come risolvere il problema.
L'unico consiglio che ti posso dare e' quella di fare
a mano un numero sufficiente di Menu di primo livello, e di renderli
invisibili.
poi quando leggi l'ini file e configuri i menu' rendi visibili soli i Menu'
di
primo livello necessari.
Es:
Menu1  (invisibile)
---- SubMenu1 (vettorizzato)
Menu2  (invisibile)
---- SubMenu1 (vettorizzato)
Menu3  (invisibile)
---- SubMenu1 (vettorizzato)


Ho avuto anch'io questo problema ma non l'ho potuto risolvere con il
controllo standar del visual basic, sono dovuto ricorrere alle api di
windows.
Io dovevo generare una specie di treeview con i menu, partendo da
delle voci di database. Ovviamente  non avrei potuto creare in
anticipo dei menu vuoti per poi modificarli a run-time, i lavoro
sarebbe divenuto immane e comunque difficile da controllare.
Cosi' mi sono dato una guardatina alle api ed ho risolto il problema.
Ti avviso che la cosa non e' semplicissima ma cerchero' di spiegarmi,
i passi fondamentali sono questi:
1)  generare i menu
2)  collegare i menu generati fra loro
3)  installare i menu nel menu della finestra
4)  intercettare i messaggi dei nuovi menu inseriti
i primi tre passi sono semplici, si utilizzano :
1) CreateMenu      che crea un menu vuoto, si memorizza il puntatore
che restituisce per le altre gestioni (se è zero c'e' stato u errore)
1) si inseriscono le voci
InsertMenu (puntatoreAlMenu_creato_sopra,
                   Nposizione_ o_ NumeroComandoID_Del_menu (dipende
dai flag),
                  Flag_del_menu (vedi M_FLAGS) vanno sommati se + di
uno,
                 CommandID_o_HMenu_di un altro menu(dipende se c'e'
MF_POPUP),
                  Stringa con voce del menu )
La stringa con la voce del menu deve avere l'ultimo carattere uguale a
vbNullChar 
 
2)3) per collegare i menu fra loro, basta ovviamente creare  prima i
sotto-menu e poi inserirli in una voce di menu con insert e il  flag
MF_POPUP, lo stesso per collegarli al menu principale, volendo per
questo si possono utilizzare le altre funzioni per individuare le
altre voci.
Per inserire il menu principale a runtime usa il visualizzatore di api
di VB e cerca la funzione setMenu.
4)Per l'intercettazione dei messaggi  basta che installi il
controllore dei messaggi con la funzione: SubClassWindow
Devi passarli l'handler della finestra che intendi controllare ( si
usa il metodo hWnd delle form es. SubClassWindow(Form1.hWnd,
objptr(mioOggetto)) e il puntatore all'oggetto che gestisce i menu
oppure niente se utilizzi il metodo ce ti descrivero' + avanti.
Questa funzione installa la funzione NewProcHWnd come gestore
datti un'occhiata al codice qui sotto, ricordati solo che la funzione
che intercetta i messaggi ( nel mio caso NewProcHWnd) deve avere
questa intestazione:
Function NomeFunzione(ByVal HandlerFInestra As Long, ByVal Message As
Long, ByVal wParm As Long, ByVal lParm As Long) As Long
Essa deve ritornare 0 se il messaggio è stato gestito altrimenti <> 0
Io comunque eseguo sempre anche la vecchia procedura dei messaggi(come
vedi nel codice) e restituisco il valore di quest'ultima. Comunque
Dipende dall'applicazione.
i parametri della funzione sono innanzitutto l'handler della finestra
che ha generato il messaggio(che ovviamnete deve corrispondere a
quello della tua finestra), il tipo di messaggio ( nel nostro caso
WM_COMMAND che quello che viene generato dai menu cliccati e
WM_MENUSELECT che viene generato dai menu selezionati)
lparm e wParm dipendono poi dal messaggio, nel nostro caso lparm
contiene l'handelr del menu selezionato mentre wParm i flag (parte
alta) e il CommandID (parte bassa) del menu.
Devo farti notare che se la parte alta di wParm è &HFFFF allora e'
stato schiacciato escape oppure e' un sottomenu, purtroppo ho notato
che anche se schiacci sul menu succede che ti trovi &hFFFF e nessun
commandID percio' memorizzo sempre l'ultimo menu selezionato
(probabilmente ho tralaciato di leggere qualcosa nella documentazione
delle api).
Per quanto riguarda  il controllo  basta solo che tu implementi una
funzione Friend che possa essere richiamata dal gestore dei messaggi,
puoi ache utilizzare una funzione generica e mettere direttamente il
nome senza usare setWindowLong , getWindowLong con  GWL_USERDATA e
copyMemory
il codice che ti allego dovrebbe funzionare, l'ho tratto da un
tentativo di costruzione di un controllo per i menu stile treeview.
Tentativo andato male xche' non sono riuscito a costruire un oggetto
contenitore simile ai Nodes o ai ListItems (se hai suggerimenti per
questo sono bene accetti). A sua volta il codice del controllo l'ho
tratto dalla mia prima implementazione (questa pero' andata a buon
fine) della gestione dei menu in un programma tuttora funzionante.
Mi rimane solo una nota: il debug del programma diventa rognoso
infatti l'installazione del gestore dei messaggi interferisce con
quello dell'ambiente Vb risultato appena metti un BreakPoint e
l'esecuzione si ferma ad esso  non riesci + a proseguire (o lo fai per
pochi step)
Soluzione disabilita la gestione dei messaggi temporaneamente (basta
che commenti la riga setWindowLong ...)
ed esegui il programma normalmente, i menu funzioneranno perfettamente
solo che non risponderanno al comando, questo  devi testarlo
separatamente.
L'unica cosa che non riesci a testare sara' proprio il gestore dei
messaggi, per vedere che cosa fa puoi solo mandare i dati (message e
parms) ad una label e vedere che dati sono (ovviamente con il gestore
installato e senza  breakpoint)
Altro problema la chiusura del programma ( con un unload me per
esempio) sotto l'ambiente VB genera un errore di protezione e chiude
il compilatore e il programma stesso. Questo non succede una volta
compilato il programma ed eseguito Stand-Alone. Per questo ti
consiglio di chiudere il programma solo con la X della finestra in
alto a dx, e' l'unico modo per non impallare il compilatore.
Altra nota se hai il cd-rom di vb sotto tools-unsuported-systray trovi
un esempio di routine di gestione dei messaggi (per altri messaggi
pero' quelli del systray ovviamente) che puo' esserti d'esempio.
Questo e' un controllo esempio della Microsoft ma ha gli stessi
problemi che ti ho fatto notare sopra. La gestione dei messaggi
interferisce con l'ambiente.
Spero di esserti stato d'aiuto e di essere stato abbastanza chiaro
(non e' facile gestire le api da VB). Per qualsiasi domanda scrivimi.
Ciao
'__________________ modulo di gestione dei messaggi___________
Public Const MAX_LEN = 64                  ' dimensioni mazzime
etichetta menu
Public Enum MF_FLAGS
  MF_BYCOMMAND = &H0&               ' specifica che nPosition è un
Command ID di un menu già presente, il nuovo menu viene posizionato
prima
  MF_BYPOSITION = &H400&            ' nPosition è la posizione,
nPosition=-1 alla fine del menu
'
  MF_POPUP = &H10&                  ' indica che il menu è popup e che
nIDNewItem è l'handler invece del command ID
  MF_SEPARATOR = &H800&             ' l'item è un separatore non serve
nIDNewItem
  MF_STRING = &H0&                  'lpszNewItem contiene una stringa
Null-terminated (interp. di default)
  MF_OWNERDRAW = &H100&             'lpszNewItem contiene un
identificatore a 32 per una struttura dati aggiuntiva (vedi Doc)
  MF_CHECKED = &H8&                 ' ovvio significato
  MF_UNCHECKED = &H0&
  MF_GRAYED = &H1&
  MF_DISABLED = &H2&
  MF_ENABLED = &H0&
' MF_MENUBARBREAK = &H20&           'separatore di menu verticale
End Enum
' costanti di CallWindowProc
Public Enum GWL_INDEX
      GWL_USERDATA = (-21&)
      GWL_WNDPROC = (-4&)
End Enum
Private Const WM_COMMAND = &H111
Private Const WM_MENUSELECT = &H11F
'strutture per l'individuazione del'ultimo menu evidenziato
Private Type tyLastMenu
  CommandID As Long
  MenuFlags As Integer
  MenuHandler As Long
End Type
'riferimento per l'ultimo menu selezionato (non cliccato)
Private LastMenu As tyLastMenu
'handler alla vecchia procedura
Private OldProcHWnd As Long
Private OldUserData As Long
'handler alla finestra dei menu
Private WindowOwnerHandler As Long
'oggetto che ha allocato i menu
'Private ObjCaller As Object
'Handlers ai menu generati
' crea il menu vuoto , restituisce l'handele al menu creato
Public Declare Function CreateMenu Lib "user32" () As Long
'inserisce una nuova voce nel menu, restituisce 0 se  tutto e' ok
Public Declare Function InsertMenu Lib "user32" Alias "InsertMenuA"
(ByVal HMenu As Long, ByVal nPosition As Long, ByVal Flags As
MF_FLAGS, ByVal wIDNewItem As Long, ByVal lpNewItem As Any) As Long
'rimuove il menu una voce di menu
Public Declare Function RemoveMenu Lib "user32" (ByVal HMenu As Long,
ByVal nPosition As Long, ByVal wFlags As MF_FLAGS) As Long
'distrugge il menu (di cui handler), non distrugge i sottomenu a
'questo collegati
Public Declare Function DestroyMenu Lib "user32" (ByVal HMenu As Long)
As Long
'ritorna l'handeler del sottomenu nPos (-izione) del menu HMenu (=
handler menu)
Public Declare Function GetSubMenu Lib "user32" (ByVal HMenu As Long,
ByVal nPos As Long) As Long
'ritorna l'handler al menu principale della  finestra
Public Declare Function GetMenu Lib "user32" (ByVal hwnd As Long) As
Long
'fa un refresh dei menu della finestra
Public Declare Function DrawMenuBar Lib "user32" (ByVal hwnd As Long)
As Long
'modifica una voce di menu esistente
Public Declare Function ModifyMenu Lib "user32" Alias "ModifyMenuA"
(ByVal HMenu As Long, ByVal nPosition As Long, ByVal wFlags As
MF_FLAGS, ByVal wIDNewItem As Long, ByVal lpString As Any) As Long
'funzione per rimpiazzare il gestore dei messaggi della finestra o
inserire nuovi dati utente
Public Declare Function SetWindowLong Lib "user32" Alias
"SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As GWL_INDEX, ByVal
dwNewLong As Long) As Long
'installa un nuovo menu nella finestra
Declare Function SetMenu Lib "user32" Alias "SetMenu" (ByVal hwnd As
Long, ByVal hMenu As Long) As Long
'ritorna l'handler al menu principale della finestra
Declare Function GetMenu Lib "user32" Alias "GetMenu" (ByVal hwnd As
Long) As Long
'funzione per leggere il gestore dei messaggi o i dati associati alla
finestra
Public Declare Function GetWindowLong Lib "user32" Alias
"GetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As GWL_INDEX) As
Long
'richiama un gestore di messaggi
Public Declare Function CallWindowProc Lib "user32" Alias
"CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hwnd As Long,
ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
'per copiare dei puntatori
Public Declare Sub CopyMemory Lib "Kernel32" Alias "RtlMoveMemory"
(pDest As Any, pSource As Any, ByVal ByteLen As Long)
'Nuovo gestore messaggi
Function NewProcHWnd(ByVal Hwind As Long, ByVal Message As Long, ByVal
wParm As Long, ByVal lParm As Long) As Long
' funzione che intercetta i messaggi
Dim ObjAddr As Long
Dim Label3 As Label
'intercetta l'ultimo messaggio valido dei menu
  If WM_MENUSELECT = Message And Hwind = WindowOwnerHandler Then
    If (wParm And &HFFFF0000) <> &HFFFF0000 Then
     With LastMenu
        .CommandID = wParm And &HFFFF&
        .MenuFlags = (wParm And &HFFFF0000) \ &H10000
        .MenuHandler = lParm
     End With
    End If
' verifica se il comando arriva dal menu
  ElseIf WM_COMMAND = Message And ((&HFFFF0000 And wParm) = 0) _
                              And Hwind = WindowOwnerHandler Then 
'********************solo se gestito da oggetto
'recupera l'oggetto che gestirà i messaggi
     Dim TmpAddr As Long
     Dim Ctrl As MenuCtrl 'oggetto che gestirà i messaggi
     TmpAddr = GetWindowLong(WindowOwnerHandler, GWL_USERDATA)
     CopyMemory Ctrl, TmpAddr, 4&
'procedura di tipo Friend presente nell'oggetto per gestire i messaggi
     Ctrl.MenuEvent LastMenu.CommandID, LastMenu.MenuFlags,
LastMenu.MenuHandler
     CopyMemory Ctrl, 0&, 4&
'******************* solo se gestito da oggetto
'**************gestione semplice
' GestioneMsgMenu   LastMenu.CommandID, LastMenu.MenuFlags,
LastMenu.MenuHandler
'**************gestione semplice
  End If 
'richiama il vecchio gestore dei messaggi della finestra per la
gestione degli altri messaggi (senza questa il resto dei controlli non
funziona +)
  NewProcHWnd = CallWindowProc(OldProcHWnd, Hwind, Message, wParm,
lParm)
End Function
sub  GestioneMsgMenu( commandID as long, flags as long,MHandler as
long)
'codice di risposta ai menu
end sub
Public Sub SubClassWindow(ByVal WindowHandler As Long, ByVal ObjAddr
As Long) '  , ByVal obj As Long)
  WindowOwnerHandler = WindowHandler
  'installazione della procedura di gestione menu
  OldProcHWnd = SetWindowLong(WindowOwnerHandler, GWL_WNDPROC,
AddressOf NewProcHWnd)
'installa un puntatore ad un oggetto che gestirà i messaggi dei menu
'se si utilizza una funzione interna a questo modulo  non e'
necessaria questa riga
  OldUserData = SetWindowLong(WindowOwnerHandler, GWL_USERDATA,
ObjAddr)
End Sub
Public Sub UnSubClassWindow()
'ripristina le condizioni iniziali
  SetWindowLong WindowOwnerHandler, GWL_WNDPROC, OldProcHWnd
  SetWindowLong WindowOwnerHandler, GWL_USERDATA, OldUserData
End Sub
'__________fine modulo gestione messaggi ___________
'__________________esempio di creazione menu
sub ProvaMenu( byval Hwnd as long)
const MY_COMMAN_ID=100  'identificatore menu
 dim Menu,MenuPopUp as long
 dim Description as string
  MenuPopUp=CreateMenu()
  Menu = CreateMenu()
 Description = "&Menu Prova" & vbNullChar
 If InsertMenu(Menu, -1&, MF_BYCOMMAND + MF_ENABLED + MF_STRING ,
MY_COMMAN_ID, Description) = 0 Then
     MsgBox "Win32 api error, prbably no system resurces.", vbOKOnly +
vbCritical, "System Error"
  Description = "&PopUp Prova" & vbNullChar
  If InsertMenu(Menu, -1&, MF_BYCOMMAND + MF_ENABLED + MF_STRING +
MF_POPUP,  MenuPopUp, Description) = 0 Then
     MsgBox "Win32 api error, prbably no system resurces.", vbOKOnly +
vbCritical, "System Error"
  End If
  setMenu hwnd, Menu
 DrawMenuBar Hwnd
'eventuale installazione gestore messaggi
' SubClassWindow Hwnd ' versione senza l'oggetto
End Sub
Massimo D'Antoni 
mailto:
No_spa...@iol.it
REMOVE the NoSpam addres string to answer

primo livello necessari.
Stesso consiglio.
Ciao
Bye 
Daniele
+--------------------------------------+
 E-mail to:<ld...@NOSPAMtuttocitta.it>
        (togliere...NOSPAM)
     (please remove...NOSPAM)
+---------------------------------------+

Nel mio sito dovresti trovare un' ocx che fa al tuo caso. Scusa, ma non mi
ricordo il nome, comunque ci sono le descrizioni,e dovrebbe essere quella
che dice che puo rinominare anche la caption del men+APk-.
Ciao, Gianni Zacchia
--
+AF8AXwBfAF8AXwBfAF8AXwBfAF8AXwBfAF8AXwBfAF8AXwBfAF8AXwBfAF8AXwBfAF8AXwBfAF8-
Hai visitato il mio sito? Lo puoi trovare a questo indirizzo:
 
http://free.imd.it/giazack/
Qu+AOw- puoi trovare tutto quello che cerchi su Visual Basic 5. Trucchi, faq,
ocx, active-x, link, sorgenti.
TUTTO? Si, addirittura come creare finestre circolari o di altre forme.
Riccardo Sale ha scritto nel messaggio
+ADw-6kj323+ACQ-cbh+ACQ-1+
AEA-server-b.cs.interbusiness.it
+AD4-...
+AD4-Qualcuno di voi sa come aggiungere dinamicamente i menu' ad un form vb.
+AD4-Mi spiego meglio: dovrei far in modo di creare una serie di menu' prendendo
+AD4-la struttura dall'esterno (tipo file ini). Ho provato a creare un array di
+AD4-menu' ma non mi permette di ottenere quello che mi serve, tipo:
+AD4-
+AD4-Menu(1)                    Menu(2)                      Menu(3)
+AD4-...
+AD4-  SubMenu(1,1)            SubMenu(2,1)             SubMenu(3,1)
+AD4-  SubMenu(1,2)            SubMenu(2,2)
+AD4-  ....                               ....
+AD4-
+AD4-ho creato quindi Menu(1) e SubMenu(1,1) come i primi elementi di due array
+AD4-di menu' e cosi' posso creare la struttura di Menu(1) senza problemi
+AD4-caricando un array di SubMenu(1,1), ma quando carico un nuovo elemento di
+AD4-Menu(1) creando Menu(2) non mi crea SubMenu(2,1) e quindi non posso crearne
+AD4-un array... :-(
+AD4-Spero di essere stato abbastanza chiaro.
+AD4-
+AD4-Grazie a tutti per l'interessamento.
+AD4-
+AD4-Riccardo.
+AD4-
+AD4-