Introduzione:
Subito dopo aver postato il mio 'microlavoro' sull'accesso ai controlli di un form da un thread diverso, mi sono reso conto che forse per la maggior parte di coloro che iniziano a confrontarsi con Vb.Net, il problema principale, fonte di tante discussioni sui vari forum, è un più banale accesso ai controlli di un form dal codice di un form diverso. La domanda tipica è qualcosa del genere: "Clicco su button1 di Form1, apro Form2 e da Form2 voglio cambiare il contenuto di una textbox in Form1. Come faccio?".
Partendo da questa esigenza, mi sono convinto a spendere un po' di tempo per quello che a molti sembrerà un argomento banale, ma che all'inizio ha creato qualche problema ai più (me compreso
).
Applicabilità:
Alcuni concetti di base sono validi in tutte le versioni di Vb.Net, ma il lavoro è inteso globalmente valido per VB2005 in tutte le sue versioni.
Premessa:
Quando più avanti si parlerà familiarmente di 'vedere' o 'condividere' un oggetto, si intenderà quello che più tecnicamente viene definito avere un riferimento all'oggetto e ci si occuperà sempre di oggetti di tipo 'reference' (la maggior parte di ciò che si usa in VB).
Gli oggetti di tipo 'reference' vengono allocati in una speciale area di memoria detta 'Heap'. Se ad una variabile viene assegnato un oggetto esistente, non si ha una ricostruzione di un oggetto identico nella variabile (cioè nello spazio di memoria ad essa dedicato), ma semplicemente si comunica alla variabile che essa è associata a quell'oggetto. E' come se la variabile divenisse un 'puntatore' all'area di memoria in cui risiede l'oggetto. Attraverso le informazioni contenute nella variabile, sulla rilocazione dell'oggetto nell'heap, noi possiamo raggiungerlo e manipolarlo.
VB2005 e le istanze di default:
Probabilmente per venire incontro a chi, abituato a VB6, voleva poter avere un riferimento a un Form semplicemente scrivendone il nome, in VB2005 è stata introdotta l'istanza di default. Se avete un Form di nome Form1 e un altro di nome Form2, potete aprire Form2 da Form1, semplicemente con l'istruzione:
Form2.Show()
Così come dall'interno di Form2 potete cambiare il 'Text' di Form1 con questo codice:
Form1.Text = "Visto!"
Infatti, le istanze di default sono già state create e sono globalmente visibili.
Però non tutti utilizzano una sola istanza di un form e inoltre se si impara a rendere visibile l'istanza non predefinita di un form, questo vale anche per una connessione, o quanto altro possiamo avere in animo di condividere nel nostro progetto.
Dichiarazione Pubblica:
Per rendere visibile una istanza di una determinata classe, non è strettamente necessario che questa sia 'costruita' in una zona pubblica, come ad esempio in un modulo, ma è necessario che questa sia 'dichiarata' come 'public' in un modulo, ad esempio:
Module Module1
Public F1 As Form1
End Module
La chiamata al metodo costruttore (new), può avvenire anche altrove. Rifacendosi all'esempio dei nostri Form1 e Form2, potremmo avere:
Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
F1 = New Form1 'Qui si costruisce l'istanza F1 di Form1
Form2.Show()
End Sub
End Class
Public Class Form2
Private Sub Form2_Load(ByVal sender AsObject, ByVal e As System.EventArgs) Handles Me.Load
F1.Show() 'L'istanza F1 è visibile anche da qui
End Sub
End Class
Come si può vedere, il fatto di avere dichiarato l'istanza F1 come pubblica in un modulo, mi consente di poterla prima costruire in Form1 e poi maneggiare in Form2. Essa risulta infatti visibile ad entrambi (cioè entrambi i Form possono avere un riferimento all'istanza F1). Ovviamente, prima un'istanza va costruita (new) e solo dopo ci si può lavorare.
Costruzione Pubblica:
Volendo si può optare anche per la costruzione pubblica dell'istanza, tuttavia, anche se l'istanza verrà creata la prima volta che verrà 'riferita', qualora venisse distrutta, essa andrà comunque ricostruita esplicitamente, quindi questo metodo è consigliabile solo se si prevede di non distruggere l'istanza fino al termine dell'applicazione.
Module Module1
Public F1 As New Form1
End Module
Passaggio di un riferimento tra Form:
Supponiamo che da Form1, o da un altro Form della mia applicazione, io voglia aprire Form2 e fare in modo che Form2 modifichi il testo (Text) proprio del Form che l'ha aperto.
In questo caso mi basterà creare un opportuno campo pubblico in Form2 e assegnare a quel campo un riferimento al Form che sta per aprire (Show) la Form2, es:
Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Form2.FormCheMiHaAperto = Me
Form2.Show()
End Sub
EndClass
Public Class Form2
Public FormCheMiHaAperto As Form
Private Sub Form2_Load(ByVal sender AsObject, ByVal e As System.EventArgs) Handles Me.Load
FormCheMiHaAperto.Text = "Io ti ho aperto!"
End Sub
End Class
In questo esempio sono state usate le istanze di default (Form1 e Form2), ma funziona benissimo con qualunque altra istanza.
Nel caso si debba accedere ad un metodo, o proprietà, o controllo (come ci si propone di fare nell'ambito di questa discussione) di un particolare tipo di Form (ad esempio Form1), allora sarà necessario dichiarare la variabile FormCheMiHaAperto di tipo opportuno. Nell'esempio di cui sopra, la dichiarazione diventa:
Public FormCheMiHaAperto As Form1
Dichiarando la variabile come Form generico, soltanto i membri della classe base (cioè Form) saranno accessibili in maniera diretta.
Conclusione:
Quello che potrebbe sembrare un argomento banale, in realtà per essere ben compreso richiede un minimo di approfondimento sulle basi del linguaggio. Infatti, l'introduzione delle istanze di default è andata incontro a chi vuole scrivere subito qualcosa, senza preoccuparsi di aprire un buon manuale.
Personalmente, continuo a non usarle e ritengo che comunque si debba essere in grado di sapere come si gestisce la visibilità degli oggetti in una applicazione.
Questa 'pillola' sull'accesso ai Form (ma come dicevo il concetto di base non si limita ai Form) vuole essere un aiuto a chi si trova di fronte al problema specifico, ma anche uno stimolo a cercare di capire la logica con cui gira il meccanismo della rilocazione degli oggetti e dei riferimenti che ad essi vengono assegnati.