Articolo wiki
  • Questo articolo wiki è bloccato
Applicabile a 9575 visualizzazioni

Excel - Controllo Frame e UserForm. Alcuni esempi - VBA (Update)

Livello tecnico : Base

Riepilogo

Livello tecnico: Conoscenza di base di Visual Basic for Applications Si applica a: Excel (2003/2007/2010/2013)

Articolo aggiornato il 07/11/2014 con l'aggiunta di un nuovo esempio per nascondere un Frame con i suoi controlli o per disabilitare tutti i controlli di un Frame. Un secondo esempio mostra come sia possibile annidare i Frame e visualizzarli in base a scelte dell'utente.

Dettagli

NOTA. Gli esempi ed il codice sono forniti *così come sono* e l’autore declina ogni responsabilità per un loro uso scorretto. Utilizzate gli esempi forniti o file di prova per i vostri test. La correzione delle eccezioni (errori) e il controllo del tipo di dati inserito è solo parziale. I file vogliono essere un semplice esempio che illustra come sia possibile automatizzare Excel e sono forniti solo a scopo dimostrativo.

Situazione.

Creare una UserForm è una cosa in se semplice, ma molto spesso vedo progetti con UserForm che non sono molto *amichevoli* e che fanno perdere tempo agli utenti che le devono utilizzare. Inoltre, controllando il codice della UserForm, vedo inutili ripetizioni e cattiva progettazione.

In questo articolo Wiki, vediamo come evitare alcuni di questi errori/problemi.

Il file con gli esempi è scaricabile da qui: http://www.maurogsc.eu/wiki/userformframe.zip

Una *brutta* UserForm.

Cominciamo creando una semplice UserForm con 6 TextBox e 2 CommandButton. CommandButton1 andrà a scrivere Ciao nelle TextBox 1, 2, 3 e CommandButton2 ne cancellerà il contenuto. TextBox 4, 5, 6 non vogliamo siano interessate.

Figura 1: La UserForm1 con i controlli.

Il codice dei due CommandButton è questo (UserForm1 nell'esempio scaricabile):

Private Sub CommandButton1_Click()
    With Me
        .TextBox1.Text = "Ciao"
        .TextBox2.Text = "Ciao"
        .TextBox3.Text = "Ciao"
    End With
End Sub

Private Sub CommandButton2_Click()
    With Me
        .TextBox1.Text = ""
        .TextBox2.Text = ""
        .TextBox3.Text = ""
    End With
End Sub

Niente di particolare, se non il fatto che se le TextBox fossero in numero consistente, dovremmo scrivere tante righe di codice per quante sono le TextBox interessate. Potremmo sostituire il nostro codice con questo (UserForm2 nell'esempio scaricabile):

Private Sub CommandButton1_Click()
    Dim lng As Long
    With Me
        For lng = 1 To 3
            .Controls("TextBox" & lng).Text = "Ciao"
        Next
    End With
End Sub

Private Sub CommandButton2_Click()
    Dim lng As Long
    With Me
        For lng = 1 To 3
            .Controls("TextBox" & lng).Text = ""
        Next
    End With
End Sub

In questo caso, se dovessimo operare su di un numero diverso di TextBox, possiamo modificare il valore di iterazione. Ma, c'è un ma. Il numero dopo TextBox deve essere progressivo. Se inseriamo la TextBox7 nella parte sx della nostra UserForm, non possiamo utilizzare il ciclo For. Siamo costretti a *indicizzare* in qualche modo le nostre TextBox. Inoltre non abbiamo nessuna separazione fra le varie parti della UserForm, tutti i controlli si trovano all'interno della UserForm.

Il controllo Frame.

Il controllo Frame è poco o affatto utilizzato o utilizzato solo parzialmente. Vediamo la UserForm modificata con l'aggiunta di tre Frame(UserForm3 nell'esempio scaricabile):

Figura 2: La UserForm3 con i controlli all'interno dei controlli Frame.

Si può notare come i controlli Frame consentano di raggruppare al loro interno altri controlli, dividendoli in modo logico. Oltre a migliorare l'estetica della UserForm, questo ci consente di gestire da codice i controlli all'interno dei singoli Frame come *collezioni di controlli*. Ad esempio, nel Frame1 ho inserito nell'ordine dall'alto al basso: TextBox1, TexBox6 e TextBox5. Non è possibile utilizzare un ciclo For come visto in precedenza. Ma Il controllo Frame1 contiene un insieme di controlli TextBox, quindi possiamo scrivere questo codice per ciclare l'insieme:

Private Sub CommandButton1_Click()
    Dim ctr As MSForms.Control
    With Me.Frame1
        For Each ctr In .Controls
            ctr.Text = "Ciao"
        Next
    End With

    Set ctr = Nothing
End Sub

Private Sub CommandButton2_Click()
    Dim ctr As MSForms.Control
    With Me.Frame1
        For Each ctr In .Controls
            ctr.Text = ""
        Next
    End With

    Set ctr = Nothing
End Sub

Come detto, cicliamo l'insieme di controlli presenti nel Frame1 e quindi non è importante conoscerne il nome ed il numero. I controlli all'esterno del Frame1 non sono interessati alle modifiche.

Complichiamo l'esempio.

Il passo successivo è creare una UserForm (frmDati nell'esempio scaricabile) dove all'avvio della stessa carichiamo i dati presenti nella tabella di foglio Dati in una Listbox. Selezionando poi i vari valori nella Listbox, andremo ad inserirli nelle TextBox del Frame. In questo caso ho modificato i nomi dei vari controlli assegnando nomi significativi e ho commentato il codice:

Option Explicit

'utilizzo l'evento Initialize della UserForm
'per caricare i dati nella ListBox
Private Sub UserForm_Initialize()
   
    'dichiaro le variabili
    Dim sh As Worksheet
    Dim rng As Range
   
    Set sh = ThisWorkbook.Worksheets("Dati")
   
    With sh
        'rilevo il Range della tabella
        Set rng = .Range("A1").CurrentRegion
        'faccio il Resize del Range eliminando la prima riga
        'che contiene l'intestazione delle colonne
        Set rng = rng.Offset(1, 0).Resize(rng.Rows.Count - 1)
        'rilevo il numero di colonne della tabella
        'e imposto il numero di colonne della ListBox
        Me.lstDati.ColumnCount = rng.Columns.Count
        'carico i valori della tabella nella ListBox
        Me.lstDati.List = rng.Value
    End With
   
    'Set a Nothing delle variabili oggetto
    Set rng = Nothing
    Set sh = Nothing
   
End Sub

'chiusura della UserForm
Private Sub cmdChiudi_Click()
    Unload Me
End Sub

'richiamo la Sub mPulisci per eliminare il contenuto
'delle TextBox di fraRisultato
Private Sub cmdPulisci_Click()
    Call mPulisci
End Sub

'rimozione dalla ListBox della riga selezionata
Private Sub cmdRinuovi_Click()

'intercetto l'eccezione se non
'ci sono più righe da eliminare
On Error GoTo RigaErrore
    'rimozione riga
    Me.lstDati.RemoveItem (Me.lstDati.ListIndex)
    Exit Sub
 
'gestione errore
RigaErrore:
    MsgBox "Nesssuna riga da eliminare", _
        vbOKOnly + vbInformation, "Attenzione"
    Call mPulisci
   
End Sub

'passo i valori della riga selezionata nella ListBox
'alle TextBox del Frame fraRisultato
Private Sub lstDati_Click()

    'dichiaro le variabili
    Dim ctr As MSForms.Control
    Dim lCol As Long
   
    'valorizzo a zero la variabile lCol; ricordo che
    'la prima colonna di una ListBox è la colonna zero
    lCol = 0
    'richiamo la Sub mPulisci per eliminare
    'quanto eventualmente presente nelle TextBox
    Call mPulisci
   
    With Me.fraRisultato
        'ciclo i controlli del Frame fraRisultato
        For Each ctr In .Controls
            'se il controllo è di tipo TextBox
            If TypeOf ctr Is MSForms.TextBox Then
                'vado ad inserire i dati della riga/colonna
                ctr.Text = Me.lstDati.List(Me.lstDati.ListIndex, lCol)
                'passo alla colonna successiva
                lCol = lCol + 1
            End If
        Next
    End With
   
    'Set a Nothing della variabile oggetto
    Set ctr = Nothing
   
End Sub

'routine che elimina il contenuto
'delle TextBox del Frame fraRisultato
Public Sub mPulisci()

    'dichiaro una variabile di tipo MSForms.Control
    Dim ctr As MSForms.Control
    With Me.fraRisultato
        'ciclo i controlli del Frame fraRisultato
        For Each ctr In .Controls
            'se il controllo è di tipo TextBox
            If TypeOf ctr Is MSForms.TextBox Then
                'elimino il contenuto
                ctr.Text = ""
            End If
        Next
    End With
    'Set a Nothing delle variabili oggetto
    Set ctr = Nothing

End Sub

E questo è quanto vedo lanciando la UserForm frmDati e selezionando una delle righe della Listbox:

Figura 3: La UserForm frmDati con una riga selezionata ed i dati riportati nelle TextBox.

Faccio notare come sia possibile determinare il tipo di controllo dell'item ciclato, qui sotto nel ciclo If verranno considerate solo le TextBox:

If TypeOf ctr Is MSForms.TextBox Then

Se il numero dei campi della tabella fosse differente, non dovrei fare altro che aggiungere le relative TextBox nel Frame fraRisultato, senza modificare una sola riga di codice, se non il riferimento al foglio con la tabella(frmDati2 nell'esempio scaricabile):

Figura 4: La UserForm frmDati2 con una riga selezionata ed i dati riportati nelle TextBox. Nessuna modifica al codice rispetto a frmDati se non il riferimento al foglio con la nuova tabella.

Nascondere un Frame o disabilitare tutti i controlli contenuti al suo interno.

A volte si ha la necessità di nascondere o disabilitare una serie di controlli nella UserForm. Nell'esempio scaricabile da qui:

premendo CTRL+a viene visualizzata la UserForm1 che contiene due Frame:

  • Premendo il pulsante Nascondi, l'intero Frame1 viene nascosto.
  • Premendo il pulsante Mostra, viene nuovamente visualizzato il Frame1 ed i controlli in esso contenuti.
  • Premendo il pulsante Disabilita, tutti i controlli all'interno del Frame1 vengono disabilitati
  • Premendo il pulsante Abilita, tutti i controlli all'interno del File1 vengono abilitati

Figura 5: La UserForm1 dell'esempio come viene caricata all'avvio.

Figura 6: La UserForm1 dopo aver premuto il pulsante Nascondi.

Frame annidati.

Qui:

potete scaricare un esempio dove in Frame1 sono annidati Frame2 e Frame3, nascosti all'avvio della UserForm1. Selezionando uno dei valori della ComboBox viene mostrato il Frame corrispondente. Frame2 e Frame3 sono sovrapposti in fase di runtime, cioè al caricamento della UserForm, questo perché a volte diventa *una lotta infinita* sovrapporre i Frame senza che l'uno venga a essere inserito nell'altro (vedi Figura 9 e Figura 10). Provare per credere... ;-)

Figura 7: La UserForm1 all'avvio.

Figura 8: La UserForm1 dopo aver selezionato Frame3 nella ComboBox.

Figura 9: Sovrapporre due Frame in fase di programmazione può essere una sfida (quasi) impossibile. Meglio farlo via codice all'avvio della UserForm.

Figura 10: La parte di codice che sovrappone runtime i due Frame.

Conclusioni.

Il controllo Frame consente di avere UserForm più semplici da utilizzare in quanto divide i controlli in blocchi logici per chi deve utilizzare i nostri file. Inoltre, il Frame è lato codice un contenitore di altri controlli e possiamo, come visto negli esempi, riferirci ai controlli del Frame come ad un insieme separato dal resto della UserForm, semplificando e riducendo il nostro codice. E' inoltre possibile nascondere un intero Frame con i controlli che si trovano al suo interno o disabilitarlo, disabilitando i controlli contenuti. Ancora, è possibile annidare i Frame e visualizzarli solo se l'utente compie determinate scelte.

Riferimenti.

I file di esempio di questo articolo Wiki sono scaricabile da qui:

Altri articoli Wiki che riguardano il VBA di Excel e le UserForm:

Un altro esempio dell'utilizzo dei controlli Frame e delle collezioni di controlli è scaricabile da qui:

L'esempio è stato creato in risposta a questa domanda fatta nella Community sul forum di Excel:

Commenti.

Sono graditi i tuoi commenti a questo articolo Wiki e, se lo ritieni interessante, per favore spunta la voce : E' stato utile all'inizio della pagina.

Grazie.

NOTA. Excel non dispone nativamente di procedure assolutamente sicure per impedire la visualizzazione e la modifica del codice vb e/o per la protezione dei fogli. E’ una cosa da tenere sempre presente quando si distribuiscono file con contenuti ritenuti delicati. Ricordate anche che è possibile lanciare il file di Excel senza che vengano eseguite le macro.


Risorse.


 Avvio Pulito di Windows
(courtesy of Microsoft MVP Franco Leuzzi)


Computer infettato da malware (courtesy of Microsoft MVP Vincenzo Di Russo)




Commenti (0)

avanzamento