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.