Due trucchi per Userform
Si tratta di due sciocchezzuole, però del tipo “toh, chi ci avrebbe pensato?”...
Primo trucco: pulsanti “in cascata”
Questa ideuzza mi è venuta esaminando una soluzione implementata mediante le modernissime finestre WPF, ossia basate sulla spettacolare e multimediale tecnologia Windows Presentation Foundation. In quel caso si aveva una serie di pulsanti di cui di volta in volta veniva fatto emergere in primo piano quello richiesto dal contesto.
Ovviamente niente del genere è possibile con le normali Form, men che meno con le semplici UserForm che ci passa il convento VBA. Giusto? Non del tutto, almeno nell’esempietto che vado a illustrare. L’ipotesi è di aver a che fare con due (o più) pulsanti uno soltanto dei quali va sottoposto all’utente, di volta in volta. Il trucco è presto detto: rendere visibili, di volta in volta, soltanto quello che occorre, evitando così tediosi switch booleani.
Si tratta della proprietà Visible da porre =False o =True secondo necessità. L’alternativa, va ricordato, è l’attivazione o meno della proprietà Enabled, comunque nell’esempio che sto per proporre ho inoltre previsto di sovrapporre i pulsanti in guisa tale che uno soltanto (tra quelli che interessano) sia in vista, ergo concretamente a disposizione dell’utente.
Ed ecco le mosse da compiere:
- Creare una Userform, dotandola di due pulsanti CommandButton1 e CommandButton2 di dimensioni identiche, aventi etichetta (Caption) “Primo” e, rispettivamente, “Secondo”;
- Trascinarli facendo in modo che il primo si sovrapponga al secondo;
- Aggiungere un controllo Label1 etichettato “FINE LAVORI”;
- Inserire il codice macro seguente.
Private Sub CommandButton1_Click()
If MsgBox("Vuoi eseguire il PRIMO lavoro?", vbQuestion + vbYesNo, "") = _
vbNo Then Exit Sub
PrimoLavoro
CommandButton1.Visible = False
CommandButton2.Visible = True
End Sub
Private Sub CommandButton2_Click()
If MsgBox("Vuoi eseguire il SECONDO lavoro?", vbQuestion + vbYesNo, "") = _
vbNo Then Exit Sub
SecondoLavoro
CommandButton2.Visible = False
If MsgBox("Vuoi eseguire di nuovo il PRIMO lavoro?", vbQuestion + vbYesNo, "") = _
vbYes Then
CommandButton1.Visible = True
Exit Sub
End If
Label1.Visible = True
End Sub
Le generiche routine PrimoLavoro e SecondoLavoro potrebbero essere del tipo seguente (mentre saranno ovviamente adattate ai casi singoli):
Sub PrimoLavoro()
MsgBox "Ora eseguo il primo lavoro", vbInformation, ""
End Sub
Sub SecondoLavoro()
MsgBox "Ora eseguo il secondo lavoro", vbInformation, ""
End Sub
Le varie routine parlano da sole e sono di agevolissima sperimentabilità pertanto mi limito a dire che, c.v.d., l’utente dispone inizialmente del solo pulsante “Primo”, cliccando il quale se a.d.r. (a domanda risponde, come nei verbali tribunalizi) Yes viene lanciata PrimoLavoro, al che il primo pulsante è occultato e, alla comparsa del secondo... A tutti sarà insomma palese che se si verifica il duplice assenso dell’utilizzatore a PrimoLavoro segue SecondoLavoro e la (quasi-farsa?) si conclude con la visualizzazione della scritta (Label1.Visible=True) “FINE LAVORI”.
Chicca finale. La nostra Userform potrebbe essere lanciata – in modo atipico e comunque poco noto – tramite un hyperlink come questo posto, diciamo, sul primo foglio di lavoro:
Il codice che si scatena cliccandovi sarà il seguente, relativo all’evento FollowHyperlink da inserire nel modulo Foglio1 dell’Editor VBA:
Private Sub Worksheet_FollowHyperlink(ByVal Target As Hyperlink)
Load UserForm1
UserForm1.Show
End Sub
Secondo trucco: controlli filtrati per nome
Si abbia una UserForm dall’aspetto, grossomodo, seguente:
|
|
|
|
|
|
|
DATA |
|
|
|
|
|
|
|
|
|
CLIENTE |
|
|
|
|
|
|
|
|
|
CATEGORIA |
II |
|
|
|
|
|
|
|
|
TIPO LAVORO |
II |
|
|
|
|
|
|
|
|
IMPORTO |
|
|
|
|
|
|
|
|
|
|
OK |
|
|
|
|
|
|
In fondo ad essa, com’è ovvio, si trova un CommandButton1 etichettato “OK”, inoltre dei vari controlli sulla destra quelli contraddistinti con “II” sono delle ComboBox e le due II imitano alla grossolana le relative frecciotte. Ciascuna di tali caselle a discesa è caratterizzata dalla proprietà RowSource, pari rispettivamente a “TabellaCat” e “TabellaTipoLav”. Si tratta di due intervalli del foglio di lavoro così pre-denominati contenenti intuibili codici di categoria e di tipo di lavoro. In tal modo nelle due combo box si può attingere a tali elenco scegliendo quel che si desidera.
Dimenticavo di dire che la nostra Userform è una maschera di input. Per semplicità si pensi che il pulsante OK ne inserisca i dati in una riga aggiuntiva di una certa Tabella1 del foglio, ma solo a patto che – come minimo – tutte le caselle di testo e i combo box non siano vuote. L’inghippo che nasceva derivava, tra l’altro, dal fatto che nelle UserForm non esiste un modo standard per selezionare controlli funzionalmente omogenei. Nel nostro caso la omogeneità “funzionale” accomuna caselle e combo box, escludendo invece le label e il commandbutton.
In altre occasioni me la cavavo con cose del genere:
For Each Ctrl In Me.Controls
' Esplora solo le caselle di testo
If Left(Ctrl.Name,7) = "TextBox" Then
MsgBox Ctrl.Name
End If
Next
Nota bene. Non esiste una qualche proprietà che indichi il tipo di controllo...
Nel caso in questione, inoltre, volevo segnalare all’utente il significato del dato, che di fatto viene indicato dalle varie etichette sulla sinistra (DATA, CLIENTE, ecc.). La soluzione è scaturita dalla nozione seguente, che facilmente si ignora o si dimentica:
i controlli di una UserForm si possono individuare non solo con l’indice (numero) ma anche mendiante il loro rispettivo nome.
Insomma se la casella di testo del cliente è il quarto controllo ed ha nome TextBox2 essa può essere indirizzata con Controls(4) o, indifferentemente, con Controls(“TextBox2”).
La soluzione. Consiste nell’adottare due vettori (Array) correlati e opportunamente riempiti, relativi l’uno ai (soli) controlli Textbox e Combobox, l’altro ai corrispettivi nomi di dato (espressi dalle etichette, peraltro indipendentemente da queste).
Senza ulteriori indugi ecco la macro dell’evento scatenato dal pulsante OK.
Private Sub CommandButton1_Click()
' Definisci NuovoRec la riga sotto l’ultima di Tabella1
Dim NuovoRec As Range, CellaRec As Range
With Range("Tabella1").End(xlDown).Offset(1)
Set NuovoRec = Range(.Cells(1), .Cells(1, 7))
End With
' Vettore nomi controlli di input
NomiControlli = _
Array("TextBox1", "TextBox2", "ComboBox1", "ComboBox2", "TextBox3")
' Vettore nomi dei corrispondenti dati di Tabella1
NomiVariabili = _
Array("DATA", "CLIENTE", "CATEGORIA", "TIPO LAVORO", "IMPORTO")
Dim NomeCtrl As String, NomeVariab As String
Dim i As Integer
' Verifica che tutti dati immessi non siano nulli
For i = 0 To UBound(NomiControlli)
NomeCtrl = NomiControlli(i)
NomeVariab = NomiVariabili(i)
If Controls(NomeCtrl).Text = "" Then
MsgBox "Il campo " & NomeVariab & " DEVE essere riempito!"
Exit Sub
End If
Next
' Trasferimento dei dati della maschera nel NuovoRec
For i = 0 To UBound(NomiControlli)
NomeCtrl = NomiControlli(i)
NuovoRec(1, i + 1).Value = Controls(NomeCtrl).Text
Next
End Sub
Commenti essenziali, per gli esperti cui mi rivolgo. Sul foglio di lavoro – quello in cui viene lanciata la nostra Userform, con un pulsante ivi incorporato o con l’hyperlink del primo trucco – si abbia una Tabella1 così denominata e dall’aspetto seguente:
|
DATA |
CLIENTE |
CATEGORIA |
TIPO |
IMPORTO |
|
12/05/2010 |
Pallino Pinco |
ABC01 |
Questo |
12.500 |
|
15/05/2010 |
Nanni Telegrafista |
ACD05 |
Quell’altro |
9.500 |
Eccetera.
Si tratta di un oggetto ListObjest ossia, per intenderci, del tipo che fa sì che inserendo nuovi dati nella riga immediatamente sottostante essa viene automaticamente “arruolata”, ergo inclusa nel nome “Tabella1” (la parte sotto le intestazioni, per la cronaca).
Qui giunti per il buon intenditore cui mi rivolgo non spendo altre chiacchiere salvo far notare che l’ultimo ciclo For i = 0 To Ubound(NomiControlli)... Next sostituisce, sinteticamente, le singole istruzioni in un primo tempo adottate:
NuovoRec(1, 1) = TextBox1.Text
NuovoRec(1, 2) = TextBox2.Text
NuovoRec(1, 3) = ComboBox1.Text
Eccetera
?>