Lezione
di Sistemi Informativi 3 del 26/04/2001
EVENTI
ASSOCIATI ALLE VARIE OPERAZIONI SUI FORM
Initialize
Quando viene referenziato per la prima volta un form viene lanciato l’evento initialize. Questo evento corrisponde al primo momento in cui si utilizza il riferimento ad un determinato form. In questa fase viene riservata la memoria corrispondente alle variabili definite nel form.
La dichiarazione di un nuovo form è simile alla dichiarazione di un oggetto appartenente ad una classe:
- per un oggetto di una classe X uso l’istruzione
Dim O as
clsX
Set O =
New clsX
- per un form uso l’istruzione (di fatto questa istruzione è implicita per i form creati normalmente, questi vengono istanziati quando referenziati dal programma e non è necessario dichiararli esplicitamente):
Dim
Form1 as New Form1
Load
Quest’evento viene lanciato quando viene per la prima
volta referenziato uno dei controlli presenti all’interno di un form. In questa
fase posso accedere ai controlli di un form. Esempio:
Form1.Text1.Text
Resize
Quest’evento viene lanciato quando si modificano le dimensioni di un form.
Activate
Quest’evento corrisponde all’attivazione di un form per eventuale utilizzazione da parte di un utente. Esempio: quando ho 2 form in visualizzazione, si deve cliccare su uno dei 2 per rendere attivo il form d’interesse.
Deactivate
Quest’evento viene lanciato quando un form è attivo e ne viene selezionato un altro che a sua volta sarà reso attivo: in tal modo il form inizialmente attivo verrà disattivato generando quest’evento.
Paint
Quest’evento viene lanciato ogni volta che l’area di un form viene parzialmente coperta da un altro form. Quando un form si sovrappone parzialmente ad un altro.
Query_unload(Cancel,
UnloadMode) e Unload(Cancel)
Questi eventi vengono lanciati quando si sta per scaricare un form: viene prima lanciato un evento Query_Unload e poi un evento Unload. Al termine dell'operazione di Unload, i controlli del form sono tutti reinizializzati e non sarà più utilizzabile se non ricaricandolo con Load. Cancel è una variabile intera che permette di bloccare l'operazione di Unload - per impedire il proseguimento dell'unload basta dare a Cancel un valore diverso da 0. UnloadMode rappresenta la modalità attraverso la quale si effettua l’operazione di unload, ossia la modalità con cui è stato richiesto lo scaricamento di un form (da utente, da codice o mediante task manager). Esempio:
Unload Form1 --> questo form sarà chiuso e non più utilizzabile fino a prossima apertura del form stesso.
Per rendere maggiormente chiara questa distinzione tra eventi e il loro utilizzo è possibile eseguire il seguente progetto di cui verrà riportato il codice. Questo è un progetto Standard Exe che ha come oggetto di avvio il form1. Per aiutare nella comprensione di ciò che avviene nelle varie operazioni eseguite sui form, e quindi sugli eventuali eventi generati sono stati introdotti dei MsgBox e delle istruzioni di Debug.Print in maniera da visualizzare volta per volta la modalità di chiusura del form (attraverso MsgBox) e l’evento d’interesse (attraverso Debug.Print).
Codice
Form1
Option
Explicit
' questo
e' il form1
Dim f As
Form2
Private
Sub Command1_Click()
Unload f
End Sub
Private
Sub Form_Load()
Me.Caption = NomeProgramma &
"1"
Set f = New Form2
f.Text1.Text = Text1.Text
f.Show vbModeless
End Sub
Private
Sub Form_Terminate()
' Set f = Nothing
End Sub
Codice
Form2
Option
Explicit
' questo e' il
2
Private Sub
Command1_Click()
Form1.Text1.Text = Text1.Text
End Sub
Private
Sub Form_Activate()
Debug.Print "Form_Activate"
End Sub
Private
Sub Form_Deactivate()
Debug.Print "Form_Deactivate"
End Sub
Private Sub
Form_Initialize()
Debug.Print "Form_Initialize"
End Sub
Private
Sub Form_Load()
Debug.Print "Form_Load"
Me.Caption = NomeProgramma &
"2"
End Sub
Private
Sub Form_Paint()
Debug.Print "Form_Paint"
End Sub
Private Sub
Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
Debug.Print "Form_QueryUnload"
Select Case UnloadMode
Case vbFormControlMenu
MsgBox
"vbFormControlMenu"
Cancel = True
Case vbFormCode
MsgBox "vbFormCode"
Case vbAppWindows
MsgBox "vbAppWindows"
Case vbAppTaskManager
MsgBox
"vbAppTaskManager"
Cancel = True
End Select
End Sub
Private
Sub Form_Resize()
Debug.Print "Form_Resize"
End Sub
Private
Sub Form_Terminate()
MsgBox "Form_Terminate"
End Sub
Private
Sub Form_Unload(Cancel As Integer)
MsgBox "Form_Unload"
End Sub
Codice Modulo Base
Option
Explicit
Public
Const NomeProgramma = "Prova Form "
PROPERTY BAG
Sono strumenti che si utilizzano per rendere persistenti i dati di una classe. Servono per gestire in maniera efficiente le proprietà di un oggetto, possono contenere ciò che si vuole, anche collection e dictionary. Possono essere un ottimo strumento per la clonazione di oggetti visto che mediante esse si possono copiare tutte le proprietà di un oggetto di una determinata classe creando un altro oggetto identico a quello di cui si è creata la property bag. Questi strumenti sono utilizzabili solo per gli eseguibili di tipo ActiveX e servono per automatizzare l’operazione di rendere dei dati persistenti. In questo caso il progetto dovrà quindi essere di tipo Exe o Dll ActiveX. All’interno di questo progetto dovrà esserci una classe pubblica, singolo o multi uso. Questa classe (e non quelle private o PublicNotCreatable) può essere resa persistable ossia la classe può attivare il supporto intrinseco alla persistenza dei dati. Supponiamo allora di avere una classe con tali caratteristiche con 2 proprietà: Nome e Cognome di tipo stringa. Procedendo come sopra si nota come si abbiano tre eventi nuovi da gestire, di seguito riportati con relativi esempi:
- INIT
PROPERTIES
nome =
"not assigned"
cognome
= "not assigned"
Questo evento viene generato quando si crea una nuova istanza di un oggetto della classe
- WRITE
PROPERTIES (PropBag As PropertyBag)
PropBag.WriteProperty "Nome", nome, [Default]
Questo evento viene generato quando una istanza di un oggetto della classe sta per essere salvata. L'evento viene generato per indicare all'oggetto che è necessario salvare il proprio stato per poterlo ripristinare successivamente.
- READ
PROPERTIES (PropBag As PropertyBag)
PropBag.ReadProperty("Nome")
Questo evento viene generato quando una istanza di un oggetto della classe deve ripristinare lo stato di un precedente oggetto.
Vediamo di seguito il codice del programma per realizzare questi strumenti. Anche in questo caso mediante l’istruzione Debug.Print è possibili seguire di volta in volta il susseguirsi delle varie operazioni.
Codice
della Classe
Option
Explicit
Public
nome As String
Public
cognome As String
Private
Sub Class_InitProperties()
Debug.Print "INitprop"
nome = "not assigned"
cognome = "not assigned"
End Sub
Private
Sub Class_ReadProperties(PropBag As PropertyBag)
Debug.Print "Readprop"
' l'oggetto ha ricevuto la richiesta di
ripristinare le proprietà contenute in PropBag
nome = PropBag.ReadProperty("MyNome")
cognome =
PropBag.ReadProperty("MyCognome")
End Sub
Private
Sub Class_WriteProperties(PropBag As PropertyBag)
Debug.Print "Writeprop"
' l'oggetto ha ricevuto la richiesta di
salvare le proprie proprietà in PropBag
PropBag.WriteProperty "MyNome", nome
PropBag.WriteProperty "MyCognome", cognome
End Sub
Codice del Modulo Base
Private
Sub main()
Dim pb
As PropertyBag
Dim
pers1, pers2 As Class1
Set
pers1 = New Class1
pers1.nome = "Tiziano"
pers1.cognome = "Rider"
Set pb =
New PropertyBag
pb.WriteProperty
"p1", pers1 ' con questa operazione viene automaticamente
generato l'evento
WriteProperties per l'oggetto pers1
Set
pers1 = Nothing
Set pers2 = pb.ReadProperty("p1")--> Non è necessaria l’operazione
Set pers2 = New Class1 in quanto
con quest’operazione viene fatta
in maniera automatica
Debug.Print
pers2.nome
Debug.Print
pers2.cognome
End Sub
La property bag si può usare anche per oggetti non ActiveX perdendo però l’automaticità nel rendere persistenti i dati, operazione che dovrà essere quindi gestita manualmente creando una property get e let che restituisca un array di byte che potrà essere in seguito salvato. Si supponga di creare a questo proposito un progetto di tipo Standard Exe formato da un Modulo Base e da un Modulo di Classe e con una Sub Main come oggetto di avvio.
Codice della Classe
Dim m_snome As String
Public
Property Let nome(RHS As String)
m_snome = RHS
End
Property
Public
Property Get stato() As Byte()
Dim pb As PropertyBag --> property bag
temporanea
Set pb = New PropertyBag
pb.WriteProperty "nome", m_snome
stato = pb.Contents--> Contents è una proprietà che
restituisce un array di byte
del contenuto di una property
bag “fotografata” nel momento
in cui eseguo quest’istruzione
End
Property
Public Property
Let stato(RHS() As Byte)
Dim pb As PropertyBag
Set pb = New PropertyBag
pb.Contents = RHS()
m_snome = pb.ReadProperty("nome")
End Property
Codice del Modulo Base
Dim o As
Class1
Dim o1
As Class1
Dim b()
As Byte
Public
Sub main()
Set o = New
Class1
o.nome = "Indiano"
b() =
o.stato
Set o =
Nothing
Set o1 =
New Class1
o1.stato = b()
End Sub
CONTROLLI UTENTE DI TIPO ACTIVEX (Costruzione di un OCX)
Per creare un controllo utente si parte con un nuovo progetto di tipo Controllo ActiveX. Fatto ciò si entra nelle proprietà del progetto appena creato per dare un nome consistente al progetto stesso; si setta inoltre lo start up del progetto come “nessuno”.
Con il controllo utente viene introdotto un nuovo attore nella realtà dei software oltre allo sviluppatore (colui che realizza il programma) e all’utente (colui che utilizza il programma), ossia l’autore dei controlli (il realizzatore degli OCX che lo sviluppatore utilizza nel programma).
Nella fase di implementazione del controllo si deve prevedere che esso deve mettere a disposizione verso l’esterno le proprietà degli oggetti in esso contenuti. Nell’esempio trattato supponiamo di occuparci solo delle seguenti proprietà del nostro controllo formato per ora da una casella di testo, da una label e la una linea di separazione:
Text –->
Tbox
Caption -->
Label
Font -->
Tutti
Backcolor --> Tutti
Forecolor --> Tutti
In Visual Basic esiste un wizard per la creazione guidata dei controlli ActiveX (che deve essere importato negli Add-Ins dell'ambiente prima di essere utilizzato). Il compito del Wizard è principalmente quello di automatizzare le operazioni necessarie perché dall'esterno del controllo ActiveX sia possibile accedere ad alcune proprietà degli oggetti contenuti nel controllo (come per esempio leggere o modificare il contenuto di un TextBox incorporato nel controllo). Di seguito vengono riportate alcune fasi del wizard.
Alla fine della procedura precedente si avrà del codice di base all’interno del quale si potranno fare tutte le modifiche necessarie per la realizzazione finale del controllo: vengono generate delle property let e get per il controllo delle proprietà degli oggetti del controllo e vengono definite delle proprietà per la gestione delle property bag. Di seguito viene riportato un esempio di codice generato con il wizard di Visual Basic e modificato per il controllo da creare.
Sono omessi commenti inseriti dal Wizard, che servono al medesimo per una eventuale modifica al codice generato. Esempio di commenti rimossi:
'AVVISO: NON RIMUOVERE O MODIFICARE LE SEGUENTI RIGHE DI COMMENTO
'MappingInfo=Label1,Label1,-1,BackColor
I controlli ActiveX fanno uso del meccanismo di persistenza basato su Property Bag per registrare il proprio stato da una sessione all'altra. In questo caso, gli eventi UserControl_Initialize() e UserControl_InitProperties si differenziano per i dati che caricano. Quando il controllo viene utilizzato per la prima volta (inserimento) viene lanciato l’init, per tutte le altre volte (riapertura) si ha il read_properties, in chiusura del controllo si ha il write_properties. In fase di esecuzione si ha il read_properties. Il programma seguente mostra delle MsgBox per ogni fase che avviene in un dato istante. Si possono inserire dei cicli per agevolare l’operazione di cambiamento delle proprietà degli oggetto del componente facendo attenzione che gli oggetti coinvolti nel ciclo supportino tutti quella proprietà.
Option
Explicit
Private
m_sTitolo As String
Public
Property Get Titolo() As String
Titolo = m_sTitolo
End Property
Public
Property Let Titolo(RHS As String)
m_sTitolo = RHS
Label2.Caption = m_sTitolo ' la Label2 viene usata per visualizzare il titolo
End Property
Public
Property Get BackColor() As OLE_COLOR
BackColor = Label1.BackColor
End
Property
Public
Property Let BackColor(ByVal New_BackColor As OLE_COLOR)
Dim ctl
As Control
On Error Resume Next
For Each ctl In Controls
If TypeName(ctl) = "Label"
Then
ctl.BackColor() = New_BackColor
End If
Next
' UserControl.BackColor = New_BackColor
PropertyChanged "BackColor"
End Property
Public
Property Get ForeColor() As OLE_COLOR
ForeColor = Label1.ForeColor
End
Property
Public
Property Let ForeColor(ByVal New_ForeColor As OLE_COLOR)
Dim ctl
As Control
On Error Resume Next
For Each ctl In Controls
ctl.ForeColor() = New_ForeColor
Next
Line1.BorderColor() = New_ForeColor
PropertyChanged "ForeColor"
End
Property
Public
Property Get Font() As Font
Set Font = Label1.Font
End
Property
Public
Property Set Font(ByVal New_Font As Font)
Dim ctl
As Control
On Error Resume Next
For Each ctl In Controls
Set ctl.Font = New_Font
Next
PropertyChanged "Font"
End Property
Public
Property Get Caption() As String
Caption = Label1.Caption
End
Property
Public
Property Let Caption(ByVal New_Caption As String)
Label1.Caption() = New_Caption
PropertyChanged "Caption"
End
Property
Public
Property Get Text() As String
Text = Text1.Text
End
Property
Public
Property Let Text(ByVal New_Text As String)
Text1.Text() = New_Text
PropertyChanged "Text"
End
Property
Private
Sub UserControl_AmbientChanged(PropertyName As String)
Caption = Ambient.DisplayName
BackColor = Ambient.BackColor
End Sub
Private
Sub UserControl_Initialize()
MsgBox "Initialize"
End Sub
Private
Sub UserControl_InitProperties()
If Ambient.UserMode Then
MsgBox "InitProperties - Esecuzione"
Else
MsgBox "InitProperties -
Design"
End If
BackColor = &H8000000F
ForeColor = &H80000012
Set Font = Ambient.Font
Caption = "MioUC1"
Text = "Text1"
Titolo = "Senza Titolo"
End Sub
'Carica i valori delle proprietà dalla posizione di memorizzazione.
Private
Sub UserControl_ReadProperties(PropBag As PropertyBag)
If Ambient.UserMode Then --> Permette di vedere ci si trova in modalità utente (esecuzione) o meno (Design)
MsgBox "ReadProperties - Esecuzione"
Else
MsgBox "ReadProperties -
Design"
End If
BackColor =
PropBag.ReadProperty("BackColor", &H8000000F)
ForeColor =
PropBag.ReadProperty("ForeColor", &H80000012)
Set Font = PropBag.ReadProperty("Font", Ambient.Font)
Caption = PropBag.ReadProperty("Caption", "Label1")
Text =
PropBag.ReadProperty("Text", "Text1")
Titolo = PropBag.ReadProperty("Titolo")
End Sub
'Scrive i valori delle proprietà nella posizione di memorizzazione.
Private
Sub UserControl_WriteProperties(PropBag As PropertyBag)
MsgBox "WriteProperties"
Call
PropBag.WriteProperty("BackColor", BackColor, &H8000000F)
Call PropBag.WriteProperty("ForeColor",
ForeColor, &H80000012)
Call
PropBag.WriteProperty("Font", Font, Ambient.Font)
Call
PropBag.WriteProperty("Caption", Caption, "Label1")
Call
PropBag.WriteProperty("Text", Text, "Text1")
PropBag.WriteProperty "Titolo", m_sTitolo
End Sub
Una volta creato il codice non resta che selezionare “Crea ControlloUtente.ocx”. Il controllo non può essere eseguito, ma deve essere inserito in un form di un progetto Exe Standard e poi eseguire quest’ultimo. Per fare ciò si apre un progetto con le modalità sopra descritte, si sceglie la voce Componenti dal menù progetto, si fa scorrere l’elenco fino a trovare l’ocx desiderato, lo si seleziona e si da l’Ok: in questo modo l’icona del controllo apparirà nella casella degli strumenti. Quando il controllo verrà inserito nel form si genereranno tutti gli eventi relativi all’inserimenti di un controllo in un form (inizializzazione del controllo e delle relative proprietà).
Si può notare come cambiando le proprietà del form si cambiano automaticamente anche le proprietà del controllo inserito in quel form. Questo avviene attraverso l'evento AmbientChanged, attivato dal contenitore del controllo ogni volta che ne viene cambiata una proprietà.
-Extender: è il contenitore in cui viene messo il controllo utente prima di essere inserito nel form; un extender è accessibile anche dall’interno del controllo utente in modo ad esempio da visualizzare il nome del extender all’interno del controllo:
Caption
= Extender.Name --> vale solo se il contenitore all’interno del quale viene
messo il controllo supporta a procedura Name.
L'oggetto Extender permette di accedere ad alcune delle proprietà che il controllo avrà nell'ambito in cui sarà inserito. Oltre a Name, che è una proprietà sempre presente in un Extender, alcuni ambiti utilizzano altre proprietà negli Extender - ad esempio: Top, Left etc. Il problema dell'usare l'oggetto Extender dall'interno del controllo ActiveX è che solitamente l'autore del controllo non può determinare in quale ambito verrà usato il controllo stesso (un form, una pagina Web o altro) - e quindi non è in generale possibile sapere quali proprietà saranno disponibili tramite l'oggetto Extender.
-Ambient: questo oggetto, accessibile dall'interno del controllo ActiveX permette di accedere alle proprietà del contenitore in cui viene inserito il controllo (ad esempio, il form). Si ha la possibilità di gestire l’evento ambient_changed lanciato quando viene modificata qualche proprietà dell’ambiente. Si deve fare attenzione a dove uso l’oggetto ambient: se lo usassi in initialize ad esempio l’oggetto in questione non esisterebbe ancora.
Un operazione simile ad ambient_changed è il metodo PropertyChanged. Questo metodo viene richiamato nel codice di un controllo ActiveX nelle routine associate alla variazione di una proprietà. L'operazione svolta da questo metodo di sistema è notificare al contenitore che sono state modificate delle proprietà del controllo e che occorre: aggiornare la finestra delle proprietà del controllo; salvare le proprietà del controllo quando verrà chiuso il contenitore; etc.