33 0 7MB
MANUALE VBA
X EXCEL
VBA : informazioni generali ..........................................................................................................................2 Associazione Macro ..............................................................................................................................3 Casella Strumenti ...................................................................................................................................5 Colori e ColorIndex (tabella)................................................................................................................9 Cosa è il VBA.........................................................................................................................................10 Cosa è una macro...............................................................................................................................11 Editor di Visual Basic.............................................................................................................................12 Identifica celle e Intervalli...................................................................................................................15 Lunghezze procedure .........................................................................................................................16 Limitazioni relative ai progetti. ............................................................................................16 Limitazioni relative ai controlli..............................................................................................16 Numero massimo di controlli ...............................................................................................16 Limitazioni relative a controlli specifici ...............................................................................16 Limitazioni relative al codice...............................................................................................17 Routine, tipi a variabili ..........................................................................................................17 Tabella di dichiarazioni DLL .................................................................................................17 Tabella di importazione .......................................................................................................17 Limitazioni relative ai dati. ...................................................................................................17 Routine, tipi a variabili ..........................................................................................................18 Tipi definiti dall'utente...........................................................................................................18 Spazio dello stack .................................................................................................................18 Posizionare le Istruzioni.........................................................................................................................19 Registrazione Macro ............................................................................................................................22 VBA : Esempi ed esercizi in ordine alfabetico ..........................................................................................23 Aprire Applicativi (programmi) da Excel..........................................................................................24 Aprire file (o cartelle di Excel) .XLS ....................................................................................................27 Aprire tutti i files .xls contenuti in una cartella. ..................................................................27 Controllare se un files .xls è già aperto. ............................................................................27 AGGIORNAMENTO 09/01/04 ..............................................................................................28 AGGIORNAMENTO 09/10/03 ..............................................................................................29 Calcolare Valori nelle celle ................................................................................................................31 Calcolo date in VBA ............................................................................................................................33 Calcolo con le date in VBA 2.............................................................................................................40 Calcolo sulle date in VBA 3 ................................................................................................................45 Calendario perpetuo ..........................................................................................................................48 Cambiare i Fonts sul foglio di lavoro. ................................................................................................50 Caricare una ListBox ............................................................................................................................52 Cerca e seleziona ................................................................................................................................54 Un esercitazione interessante : Ricerca tra due date con totale dei valori correlati. ......................................................................................................................................60 Ricerca di tre valori..............................................................................................................................65 Trovare un dato (vettore) in una tabella, con restituzione di un dato correlato. ...............................................................................................................................................67 Cercare File(s) sull'Hard-Disk. ..............................................................................................................69 Controllo comunicazioni MSCOMM32.OCX per comunicare col Modem e/o chiamare numeri telefonici. ........................................................................................72
http://ennius.interfree.it/
Pagina 1
MANUALE VBA
X EXCEL
VBA : informazioni generali
http://ennius.interfree.it/
Pagina 2
MANUALE VBA
X EXCEL
Associazione Macro Una volta creata una macro inserita in un Modulo, dobbiamo poterla attivare alla bisogna. Potremo usare uno dei seguenti metodi: Dal menù Strumenti/Macro cliccare su Macro, si aprirà una finestra dove vengono elencate le macro presenti nella cartella di lavoro, indi selezionata la macro desiderata, premere il pulsante "Esegui", oppure premere il pulsante"Opzioni", e nella finestra che si apre, assegnare una lettera come "tasto di scelta rapida" : CTRL + lettera da voi scelta e dare OK. In questo modo con la combinazione CTRL + lettera attiverete la macro. Dal menù Visualizza/Barre degli strumenti e mettere un segno di spunta alla voce "Visual Basic", apparirà sul foglio di lavoro una piccola finestra come quella indicata dalla freccia rossa nella foto sottostante. In questa finestra ci sono alcuni comandi: il primo da sinistra (un triangolo), se cliccato farà apparire la finestra delle macro, basterà seguire la modalità del punto precedente. (in questa finestrina appaiono altri comandi utili per le macro: il registratore, l'editor, ecc.
L'inconveniente legato a questi metodi è palese: bisogna ripetere l'operazione tutte le volte che vogliamo attivare una macro nel primo caso, mentre con l'assegnazione di "tasti di scelta rapida" bisognerà ricordarsi, in caso di più macro presenti, quale è la lettera giusta per una determinata macro. La soluzione che consiglio è quella di crearsi un pulsante (ottenibile comunque dall'inserimento sul foglio degli "strumenti di controllo" (vedi foto sopra), usando invece la modalità "disegno". Se Clicchiamo sulla barra degli strumenti, sull'icona "Disegno" (vedi foto sottostante) indicata con la freccia del mouse (parte alta a destra), apparirà sul piede del foglio (freccia rossa) gli oggetti utilizzabili con questa modalità: le Word Art.
http://ennius.interfree.it/
Pagina 3
MANUALE VBA
X EXCEL
Selezionando la forma "rettangolo" (vedi foto sotto), o la "casella di testo" (2 icone a destra)
e spostando il cursore del mouse in un punto qualsiasi del foglio, e tenendo premuto il pulsante del mouse, disegnare un rettangolo di cui potremo variare le dimensioni a piacere. Cliccando sul bordo del rettangolo col pulsante destro del mouse, apparirà un menù contestuale, dal quale, col sinistro del mouse, sceglieremo "ASSEGNA MACRO", si aprirà una finestra con le macro presenti e selezioneremo la prescelta dando OK. In questo modo avremo creato un comodo avvio per la nostra macro, che potremo attivare quando lo vorremo, infatti quando andremo col mouse sul pulsante associato, il cursore assumerà la forma di una mano, cliccheremo e la macro scatterà.
Per completare l'opera, tornando sul bordo del pulsante, in basso a destra, col destro del mouse, potremo scegliere "Aggiungi testo" e dare un nome per identificare a quale macro è associata, e da "Formato forme" per il "rettangolo" o da "formato casella di testo" per quest'ultima, potremo scegliere il colore di fondo, per evidenziare il pulsante. Un' ulteriore maniera è quella di assegnare una macro ad un "CommandButton", selezionabile dalla casella degli strumenti, ed in "modalità progetto", assegnare all'evento click del commandbutton la suddetta macro. Ma questo lo vedremo nella sezione "Strumenti di controllo".
http://ennius.interfree.it/
Pagina 4
MANUALE VBA
X EXCEL
Casella Strumenti Una caratteristica di molti linguaggi di programmazione, e il VBA è uno di questi, è la "programmazione ad oggetti". Per oggetti si intendono quelli strumenti che facilitano o migliorano la gestione del foglio di lavoro e dei dati in esso contenuti, attraverso l'inserimento dell'oggetto stesso sul foglio di lavoro: sono oggetti : commandbutton, casella combinata, casella di testo, option button, una form,ecc. Ogni oggetto possiede delle proprietà, dei metodi, e degli eventi . Le proprietà sono le caratteristiche che l'oggetto possiede e che possono essere modificate nelle impostazioni. Ogni oggetto ha le sue proprie proprietà, non necessariamente comuni ad altri oggetti. Per usare gli oggetti,una volta inseriti in un foglio, dovremo lavorare in "modalità progettazione" (si attiva automaticamente quando inseriamo l'oggetto, o si richiama cliccando sull'icona "squadra" nella casella degli strumenti); in questa modalità, con un doppio click sull'oggetto, entreremo nell'editor di visual basic che ci mostrerà nella finestra inferiore a sinistra, le proprietà dell'oggetto stesso. Sulla pagina destra troveremo invece la zona dove inserire eventuali istruzioni da far eseguire scegliendo un evento dell'oggetto stesso. Per esempio, usando un commandbotton, potremo sfruttare l'evento click, selezionandolo dalla finestrina in alto a destra, e ciò che vedremo sarà così:
Private Sub CommandButton1_Click() qui inseriremo l'istruzione (macro) End Sub continuando: l'oggetto "CommandButton" o "pulsante di comando", ha la proprietà "Caption" impostata a "CommanButton1" (la proprietà Caption è la scritta che vediamo sul pulsante) Possiamo modificare l'impostazione, scrivendo nel relativo campo delle proprietà, le parole che ci ricordano cosa avviene premendo il pulsante, per esempio "Aggiungi Dati". Ora, quando vedremo il pulsante, ci troveremo scritto "Aggiungi Dati". Il metodo è l'azione che l'oggetto "può" compiere e l' evento è l'azione che attiva il metodo. Vi consiglio di consultare la guida in linea, attivabile anche con F1, per familiarizzarsi con questi concetti. Vediamo, passo passo, come lavorare con questi strumenti: dal Menù Visualizza/Barre degli strumenti, selezionare la voce "Strumenti di Controllo" (o "Casella degli Strumenti" per le versioni più vecchie di Excel). vedi immagine sotto.
Apparirà una piccola finestra come quella nelle foto sotto: è la finestra degli strumenti: passando il mouse sulle icone, appare la descrizione dell'oggetto. Nella foto sotto, a destra, è evidenziata l'icona http://ennius.interfree.it/
Pagina 5
MANUALE VBA
X EXCEL
della "Modalità Progettazione". questa modalità dovrà essere selezionata ogni qualvolta vorremo intervenire sulle proprietà dell'oggetto che avremo inserito sul foglio di lavoro. (la "Modalità Progettazione si attiva in automatico allorchè, selezionando un oggetto, lo "incolleremo" sul foglio di lavoro).
Vediamo ora un esempio di come inserire e dialogare con un oggetto: la casella combinata. Nella "Finestra degli oggetti", clicchiamo sull'icona "Casella Combinata", spostiamoci col mouse sul foglio di lavoro e in corrispondenza della zona che avremo scelto per ospitare l'oggetto, clicchiamo col sinistro del mouse e trasciniamo: apparirà l'oggetto che potremo dimensionare a piacere agendo sui punti di selezione (i circoletti). In questa fase saremo già in "Modalità progettazione". Foto sotto: la casella combinata
Ora dovremo passare alla visualizzazione delle proprietà dell'oggetto per inserire le istruzioni che faranno al caso nostro: il reperimento di una colonna di dati e la casella di destinazione dove vorremo che il dato prescelto nella casella stessa, appaia. Doppio click sull'oggetto casella combinata, e si aprirà l'editor di visual basic. Vedi foto sotto a sinistra. Nella zona inferiore sinistra di questa foto vediamo la finestra delle proprietà relative al "ComboBox1", nome inglese della "casella combinata". Il N° 1 viene aggiunto dal codice per identificare che questa è la casella combinata prima inserita, e, poichè ne potremo inserire altre, ad ognuna verrà assegnato un numero di identificazione progressivo.Nella foto sotto a destra vediamo invece le proprietà che invece ci interessano da vicino: "LinkedCell" e "ListFillRange". La prima identifica la cella che vorremo indicare per riportare i dati che selezioneremo sul foglio di lavoro nella casella combinata, la seconda indica la zona dove la casella combinata andrà a trovare i dati che la stessa ci mostrerà sul foglio di lavoro. Nell'esempio che trattiamo, la lista dei nomi da cercare si troverà nella colonna A, dalla riga 1 alla 8. Per il momento trascuriamo di illustrare le altre proprietà che saranno consultabili attraverso la guida in linea.
http://ennius.interfree.it/
Pagina 6
MANUALE VBA
X EXCEL
Ritorniamo sul foglio di lavoro, clicchiamo sull'icona della "squadra" che identifica la "Modalità progettazione" per uscire da questa modalità, e ciò che vedremo sarà quello illustrato nella foto sotto:
Nella casella combinata ora appare un elenco di nomi (quello che avevamo collegato con "ListFillRange") , con un click su un nome, vedremo la cella C1 (quella collegata con "LinkedCell") riempirsi del nome selezionato. Soprassiedo per il momento a ciò che avremmo potuto fare sfruttando i metodi e gli eventi . Un ultimo accorgimento: poichè la finestra degli strumenti resterebbe "a spasso" per il foglio di lavoro, si può chiuderla usando la famosa "X", ma meglio se si inserisce, così l'avremo sempre a portata di mano, in alto, sotto i menù, cercando uno spazio tra le finestra già inserite. E' sufficiente trascinare la finestra dgli strumenti con il mouse, e rilasciare quando gli avremo trovato la giusta collocazione. Vedi foto sotto:
http://ennius.interfree.it/
Pagina 7
MANUALE VBA
http://ennius.interfree.it/
X EXCEL
Pagina 8
MANUALE VBA
X EXCEL
Colori e ColorIndex (tabella) Spesso ci necessita evidenziare con un colore diverso una o più celle del nostro foglio di lavoro o il carattere (Font) quando si verifica una determinata condizione, per esempio quando in una cella appare o inseriamo un determinato valore, per esempio 100, oppure quando appare o scriviamo un determinato giorno, per esempio "domenica", ecc. ecc. Dovremo, usando il codice Vba, utilizzare la proprietà CororIndex applicata all'oggetto Font o all'oggetto Interior (per la cella). Per esempio: Questo esempio imposta a rosso il colore dei caratteri nella cella A1 del Foglio1. Worksheets("Foglio1").Range("A1").Font.ColorIndex = 3 Questo esempio utilizza la proprietà Interior per restituire l'oggetto Interior e imposta su rosso il colore della parte interna della cella A1: Worksheets("Foglio1").Range("A1").Interior.ColorIndex = 3 Ma al di là del tipo di istruzione utilizzata, (si può usare la Funzione RGB che ci permette di disporre di una gamma molto più ampia di colori, e in questo caso l'istruzione va compilata come nel seguente esempio: Worksheets("Foglio1").Range("A1").Font.Color = RGB(255, 0, 0) avere il font rosso)
per
un problema nasce quando vogliamo sapere quale numero corrisponde ad un determinato colore; ecco quà una tabella con i Codici Colore (ColorIndex) realizzata dal sottoscritto, con i colori ordinati come nella tabella colori di excel, ed i nomi colore:
http://ennius.interfree.it/
Pagina 9
MANUALE VBA
X EXCEL
Cosa è il VBA Per "CODICE", in Excel, intendiamo un linguaggio con il quale si scrivono istruzioni che Excel è in grado di capire e di eseguire, questo linguaggio si chiama : VBA (Visual Basic for Application). Esistono libri su questo linguaggio, che pur essendo simile concettualmente ad altri linguaggi, usa tuttavia regole sue che necessariamente dovranno essere gestite, ecco perchè chi vuole reperire informazioni sul VBA dovrà munirsi dei libri specifici. (per esempio, della Jackson Libri, "Excel 2002 VBA", scritto dal "Maestro" Gianni Giaccaglini, oppure della Apogeo, "Excel 2002 Macro" dell'altrettanto esimio Paolo Guccini). Excel, per nostra grande fortuna, ha la capacità di compilare automaticamente del codice, quando noi glielo chiediamo. Come?. Semplicemente dicendogli di registrare una "Macro". Dal menu: Strumenti\macro\ selezionare: Registra nuova macro, apparirà una finestra che ci chiede con quale nome vogliamo chiamare la macro e, datogli il nome, premiamo su OK, apparirà ora una piccola finestrina con un pulsante che servirà ad interrompere la registrazione. Il funzionamento è semplice, proprio come un normale registratore, solo che Excel registrerà tutto ciò che faremo sul foglio o sui fogli di lavoro, (da selezione cella a immissione di una formula, da spostamenti a celle o colonne diverse, a immissione di nuovi fogli, ecc.) e CONVERTIRA' in CODICE tutto ciò che abbiamo fatto, registrandolo in una zona opportuna, che noi non vediamo, ma che è presente: premendo i tasti ALT + F11 oppure dal menù "Strumenti/Macro/Visual Basic Editor" si aprirà la pagina dove si trova il codice. ALT + F6 per richiudere la pagina. In questo modo potete cominciare a capire ciò che avete fatto, collegandolo con il codice che è stato scritto da Excel. (Excel pone la registrazione di una macro in "Visual basic editor", all'interno di un "Modulo", che troverete in alto sulla sinistra, nella finestrina degli "Oggetti". Doppio click su "Moduli" e si apriranno "modulo1", "modulo2", ecc. Click su questi, e nella pagina sulla destra compare il codice che Excel ha compilato.) A lato trovate dei suggerimenti per argomenti correlati.
http://ennius.interfree.it/
Pagina 10
MANUALE VBA
X EXCEL
Cosa è una macro La MACRO è un' istruzione scritta in linguaggio Visual Basic che Excel usa per compiere determinate azioni descritte nell'istruzione stessa. L'insieme delle istruzioni si definisce CODICE. Il Codice è compilabile, cioè possiamo scriverlo, aggiungerlo, modificarlo come si farebbe con qualunque testo, solo che è necessario usare una forma di scrittura che Excel sia in grado di comprendere : il suo linguaggio, il Visual basic for Application. Excel stesso è in grado di compilare una Macro, cioè del codice : basta usare il "Registratore di Macro". (vedi sezione "Cosa è il VBA"). Quello che segue è un piccolo esempio di codice( o macro. Da notare: ogni macro ha bisogno di un nome che la identifichi. NON si può usare lo stesso nome 2 volte): Macro di esempio (o codice) Sub Prova() Range("A3").ClearContents Range("A3").Formula = "=10*RAND()" End Sub
Significato delle istruzioni Nome della macro(Prova) (e inizio istruzioni) Pulisci la cella A3 dal contenuto Nella cella A3 genera un numero casuale Fine istruzioni (esce dalla macro)
La stessa istruzione poteva essere svolta da una funzione : CASUALE() da scriversi nella cella A3. Es.: =CASUALE()*10 (avrebbe generato un numero casuale compreso tra 0 e 10), però: Considerazioni Excel possiede quindi DUE possibilità di eseguire istruzioni: la prima, e più immediata, è l'immissione di comandi attraverso le FORMULE o le FUNZIONI, che devono essere inserite sul foglio di lavoro, scritte nelle celle interessate, e che quindi possiamo definire "RESIDENTI", agiscono cioè sempre e comunque (errori a parte). NON possono essere attivate su comando, e molto spesso, ci costringono ad usare altre celle per scambi di istruzioni e non basta, pena il famoso messaggio "Impossibile calcolare la formula, riferimento circolare", o si verificano solo ad apertura Foglio. la seconda, è la creazione di codice che esegua, su COMANDO, un'istruzione di qualunque genere, compreso ovviamente formule o funzioni, ma attivabili nel momento in cui eseguiamo il comando. In questo caso avremo un'istruzione NON RESIDENTE, che ci consente una migliore gestibilità del nostro lavoro, e la ripetitività agendo di nuovo sul comando. Resta inoltre la versatilità di un linguaggio (codice) capace di impostare istruzioni che con le formule o le funzioni non è possibile eseguire (per esempio i cicli For ...Next, o altre amenità del genere). Il consiglio che vivamente suggerisco, è quello, per colui che voglia avventurarsi nel mondo del "CODICE", di munirsi di libri specifici sull'argomento, oltre a quello di dare sempre un "occhiata" alla guida in linea, raggiungibile dalla pagina dell'editor di visual basic, premendo il tasto F1. (la guida è diversa se si preme F1 in visualizzazione Foglio di lavoro)
http://ennius.interfree.it/
Pagina 11
MANUALE VBA
X EXCEL
Editor di Visual Basic Raggiungere l'Editor di Visual Basic o Visual basic Editor (secondo le versioni di Excel) è semplice, dal Menù Strumenti/macro/visual basic editor (vedi immagine sotto), oppure premendo i tasti ALT + F11.
Con l'editor aperto dovreste avere una pagina come l'immagine sottostante
In questa pagina, oltre al menù, troviamo tre finestre : la prima in alto a sinistra è la finestra degli OGGETTI che compongono la cartella di lavoro (ThisWorkbook) e sono i Fogli che compongono la cartella di default (3). Nella stessa finestra compariranno i MODULI, se inseriti. Nella finestra a sinistra, immediatamente sotto, c'è la finestra delle Proprietà degli oggetti presenti nella finestra soprastante. Nella terza finestra, quella grande sulla destra, è la zona dove si può scrivere il codice. Nella parte superiore di questa finestra, ci sono due "menù a tendina". Ogni foglio ha una propria pagina dove poter scrivere il codice che riguarda istruzioni da eseguire all'interno della pagina stessa, il primo sottomenù sulla sinistra ci offre due possibilità di scelta (vedi immagine sottostante)
http://ennius.interfree.it/
Pagina 12
MANUALE VBA
X EXCEL
Generale : verranno inserite istruzioni che avranno validità su tutto il foglio, come dimensionamento di variabili, di funzioni personalizzate, ecc. Worksheet : verranno inserite istruzioni "locali" che non si influenzeranno le une con le altre, cioè se dichiareremo una variabile, la stessa sarà attiva solo all'interno della propria routine (Sub Pippo() ....End Sub). Nel sottomenù sulla destra, appare la scelta della selezione degli "EVENTI", cioè delle cause che attiveranno l'esecuzione di una o delle macro (vedi immagine sottostante).
Gli eventi sono scritti in inglese, come d'altra parte tutto il codice, ma sono facilmente comprensibili nel significato: se scegliamo per esempio, l'evento "Change", diremo ad Excel che tutte le volte che nel foglio avviene un cambiamento, si attiverà la macro e svolgerà il compito assegnatole. In questo modo, sempre per esempio, automatizzeremo l'esecuzione delle istruzioni: basterà cambiare il valore in una cella, per avere un evento "Change" che attiverà l'esecuzione della macro. Ovviamente ci sono controindicazioni, per questo è opportuno valutare l'evento da scegliere per evitare effetti indesiderati. Consideriamo per esempio, che il semplice inserimento di un numero in una nuova cella, causerebbe l'esecuzione della macro, anche se in quel momento a noi la macro non servisse. Aiutatevi con la guida in linea per avere le spiegazioni relative ad ogni evento. Un possibile accorgimento per usare istruzioni eseguibili su Eventi del foglio è quello di inserire istruzioni legate alla formattazione delle celle, o dei caratteri (font) o di un colore di fondo, o di selezione di un area di stampa, ecc. insomma per istruzioni di carattere generale o parziale, che non tocchino formule o celle con valori che vorrete modificare su vostro comando. L'intestazione della macro, comunque, inizierà così : Private Sub Worksheet_Change(ByVal Target As Range) (Nome e inizio della Macro o Routine ) ‘in questo spazio va inserito il codice End Sub
(Fine della macro e uscita)
Quando non vogliamo usare un EVENTO abbinato ad un foglio per attivare una macro, perchè vogliamo decidere noi quando attivarlo, potremo usare un MODULO e scrivere il codice nel modulo, dando un nome alla routine (macro) per poi associarla ad un pulsante usando il nome assegnato in precedenza (vedi: Associazione Macro). L'inserimento di un Modulo è semplicissima: dal menù scegliamo Inserisci/Modulo (vedi immagine sottostante).
http://ennius.interfree.it/
Pagina 13
MANUALE VBA
X EXCEL
Nella finestra degli oggetti, apparirà il simbolo di una cartella con il nome "Moduli". DoppioClick sulla cartella e si aprirà mostrando il contenuto: "Modulo1", "Modulo2", e così via. Va precisato che in un modulo, si possono inserire più macro, non occorre quindi creare un modulo per ogni macro; queste infatti saranno riconosciute dal nome assegnato, non dalla residenza in un modulo piuttosto che un altro. Attenzione a non affollare comunque troppo il modulo (quando il modulo viene letto per cercare la macro richiesta, vengono lette comunque tutte le macro presenti nel modulo stesso). La finestra che si aprirà sulla destra conterrà, nei sotto menù in alto, solo "GENERALE" e "DICHIARAZIONI", zona che servirà ad assegnare variabili utilizzabili in tutte le macro presenti nel Modulo. L'inserimento della macro è semplice, basta scrivere il nome che vogliamo, preceduto da Sub e seguito da () parentesi aperta e chiusa, e alla fine del codice, scrivere "End Sub". Esempio. Sub Ciccio() ‘in questa zona scrivere le istruzioni End Sub Un altro modo di inserire il codice lo vedremo nella sezione "Casella degli strumenti" ora detta "Strumenti di controllo", anche se è simile al precedente.
http://ennius.interfree.it/
Pagina 14
MANUALE VBA
X EXCEL
Identifica celle e Intervalli
http://ennius.interfree.it/
Pagina 15
MANUALE VBA
X EXCEL
Lunghezze procedure Lunghezze e max dimensioni procedure in vb. In risposta ad una domanda, Michele (Mike page) ha trovato una documentazione che, per i suoi contenuti, potrà interessare diversi "pellegrini", sull'argomento in oggetto, e come tale la riportiamo quasi integralmente.
Limitazioni relative ai progetti. Un singolo progetto può contenere fino a 32.000 identificatori, tra cui form, controlli, moduli, variabili, costanti, routine, funzioni a oggetti. I nomi di variabili possono essere composti da un massimo di 255 caratteri, i nomi di form, controlli, moduli a classi da un massimo di 40 caratteri. Non esiste alcun limite al numero di oggetti per un progetto.
Limitazioni relative ai controlli Ciascun controllo di tipo non grafico, ovvero tutti i controlli tranne linee, forme, immagini ed etichette, utilizza una finestra. Ciascuna finestra utilizza a sua volta risorse di sistema. II numero massimo di finestre che è possibile utilizzare contemporaneamente risulta pertanto limitato a dipende dalle risorse di sistema disponibili a dal tipo di controlli utilizzati. Per limitare il consumo di risorse di sistema, è consigliabile creare o visualizzare gli elementi grafici utilizzando i controlli forma, linea, etichetta a immagine anzichè i controlli casella immagine.
Numero massimo di controlli II numero massimo di controlli consentito in un singolo form dipende dal tipo di controlli utilizzati a dalle risorse di sistema disponibili. Esiste tuttavia un limite fisso di 254 nomi di controlli per form. Una matrice di controlli viene considerata un solo controllo, in quanto a tutti i controlli in essa contenuti a assegnato to stesso nome. In tutte le versioni il limite degli indici di una matrice di controlli a compreso try 0 e 32.767. Nel caso di controlli nidificati, ad esempio quando vengono inseriti alcuni controlli cornice all'interno di altre cornici, non a possibile nidifrcare piu di sei livelli di controlli.
Limitazioni relative a controlli specifici Nella tabella seguente sono elencate le limitazioni relative alle proprietà applicate ad alcuni controlli specifici di Visual Basic
Proprietà List e ListCount
Text
Caption
http://ennius.interfree.it/
Si applica a Controlli casella di riepilogo a casella combinata
Limitazione II numero massimo di voci a pari a 32 KB. 11 limite delle dimensioni di ciascuna voce a pari a 1 KB (1.024 byte). Controllo casella di testo Le dimensioni massime sono pari a 64 KB. Controllo etichetta Le dimensioni massime sono pari a 1.024 byte Controllo pulsante di Le dimensioni massime comando casella di sono pari a 255 caratteri. che controllo cornice e Le didascalie Pagina 16
MANUALE VBA
X EXCEL pulsante di opzione
Controllo menu Tag
Tutti i controlli
Name
Tutti i controlli
superano questo limite risulteranno troncate. Le dimensioni massime per le didascalie di controlli aggiuntivi sono pari a 32 KB Le dimensioni massime sono pari a 255 caratteri Le dimensioni massime dipendono dalla memoria disponibile Le dimensioni massime sono pari a 40 caratteri
Nota : In Visual Basic, i nomi delle propriety dei controlli non devono essere composti da piu di 30 caratteri.
Limitazioni relative al codice. II numero massimo di righe di codice che è possibile caricare in un form, una classe o un modulo standard a pari a 65.534 righe. Una singola riga di codice può equivalere a un massimo di 1.023 byte. II testo di una riga pub essere preceduto da un massimo di 256 spazi, mentre in una singola riga logica si possono includere fino a nove caratteri di continuazione riga (_).
Routine, tipi a variabili Non esiste alcun limite al numero di routine, tipi a variabili che è possibile inserire in un modulo. Ciascuna routine può includere fino a 64 KB di codice. Se si supera questo limite, verrà generato un errore di compilazione che è possibile risolvere suddividendo le routine di grandi dimensioni in routine piu piccole o spostando le dichiarazioni a livello di modulo in altri moduli. I nomi degli identificatori utilizzati nel codice, ovvero variabili, routine, costanti a cosi via, vengono registrati in tabelle. Le dimensioni massime di ciascuna tabella sono pari a 64 KB.
Tabella di dichiarazioni DLL Ciascun form e ciascun modulo utilizza una tabella contenente una struttura che descrive una voce della DLL. Ciascuna struttura utilizza circa 40 byte a può avere dimensioni massime pari a 64 KB, corrispondenti a circa 1.500 dichiarazioni per modulo. Tabella dei nomi del progetto L'intera applicazione utilizza una singola tabella contenente tutti i nomi inclusi nel progetto, ovvero: Nomi di costanti Nomi di variabili Nomi di tipi definiti dall'utente Nomi di modulo Nomi delle dichiarazioni delle routine DLL Non esiste alcun limite alle dimensioni totali della tabella dei nomi del progetto. Esiste invece un limite di 32 KB per voci univoche della tabella in cui la distinzione tra maiuscole e minuscole è rilevante. Se si raggiunge questo limite, sarà possibile riutilizzare gli identificatori privati in altri moduli.
Tabella di importazione Con ciascun riferimento agli identificatori inclusi in altri moduli viene creata una voce nella tabella di importazione. Le dimensioni di ciascuna voce devono essere comprese tra un minimo di 24 byte a un massimo di 64 KB, equivalenti a circa 2.000 riferimenti per modulo. Tabella delle voci di moduli Questa tabella accetta fino a 125 byte per modulo, con un limite massimo di 64 KB, equivalenti a 400 moduli per progetto.
Limitazioni relative ai dati. Le limitazioni descritte di seguito sono relative alle variabili del linguaggio Visual Basic. Dati di moduli di form, standard a di classe http://ennius.interfree.it/
Pagina 17
MANUALE VBA
X EXCEL
Ciascun modulo di form, standard a di classe ha un proprio segmento di dati che può avere dimensioni massime pari a 64 KB. 11 segmento contiene i seguenti dati: Variabili locali dichiarate con Static. Variabili a livello di modulo diverse da matrici a da stringhe di lunghezza variabile. 4 byte per ciascuna matrice a livello di modulo e stringhe a lunghezza variabile.
Routine, tipi a variabili Quando in una routine o in un modulo viene superato il limite massimo di 64 KB di codice, viene generato un errore di compilazione. Per risolvere questo errore, è possibile suddividere le routine di grandi dimensioni in routine piu piccole o spostare le dichiarazioni a livello di modulo in altri moduli.
Tipi definiti dall'utente Le variabili dei tipi definiti dall'utente non devono essere superiori ai 64 KB. La somma delle stringhe di lunghezza variabile in un tipo definito dall'utente, tuttavia, può superare questo limite. Ciascuna stringa occupa infatti solo 4 byte in un tipo definito dall'utente, in quanto 1'effettivo contenuto viene registrato separatamente. I tipi definiti dall'utente possono essere definiti in rapporto ai tipi definiti da altri utenti. La somma totale dei tipi non deve essere tuttavia superiore a 64 KB.
Spazio dello stack Gli argomenti e le variabili locali nelle routine occupano spazio dello stack in fase di esecuzione. Le variabili statiche e a livello di modulo invece non occupano alcuno spazio dello stack in quanto sono assegnate al segmento di dati di form o moduli, Lo spazio dello stack viene utilizzato anche durante 1'esecuzione di routine DLL. Parte dello spazio dello stack viene inoltre utilizzato da Visual Basic stesso, ad esempio per la registrazione di valori intermedi durante la valutazione di espressioni.
http://ennius.interfree.it/
Pagina 18
MANUALE VBA
X EXCEL
Posizionare le Istruzioni Considerazioni su DOVE inserire il codice (Le Istruzioni in codice VBA). Abbiamo visto che cos'è una Macro : una o più istruzioni compilate in linguaggio VBA e come associare una macro per attivare le istruzioni in essa contenute. Vorrei però riepilogare meglio i concetti di QUANDO e DOVE posizionare le istruzioni, cercando di spiegare PERCHE'. E' necessario ribadire che il VBA è un linguaggio di programmazione a "OGGETTI" - In una cartella di lavoro di Excel, sono "oggetti" i fogli di lavoro (Foglio1, Foglio2, ecc), la stessa cartella (WorkBook), e, trascurando per ora di parlare dell'oggetto "Range", sono "oggetti" anche tutti gli strumenti che possiamo inserire in un foglio di lavoro, quali ad esempio, un Commandbutton, un Combobox, un Pulsante di Opzione, una UserForm, un textbox, ecc.. Tralasciando di parlare in questo paragrafo delle "Proprietà" degli "oggetti", fisseremo la nostra attenzione sugli "EVENTI" che OGNI "Oggetto" possiede. Per "EVENTO" si intende l'"AZIONE" che si deve compiere per ATTIVARE le istruzioni abbinate all'"Oggetto" di cui l'evento fa parte. Precisiamo che esistono due modi per attivare le istruzioni (che da ora chiameremo "codice"): attivazione in automatico attivazione su comando Per l'attivazione di codice in Automatico, useremo gli oggetti WorkBook, ed i Fogli, sfruttando gli eventi ad essi collegati: se per esempio, vogliamo che un'istruzione si attivi ad ogni apertura della cartella di lavoro, sceglieremo l'evento "Open" dell'oggetto "WorkBook", scrivendo il codice tra inizio e fine della routine che Excel stesso, se selezioneremo l'oggetto WorkBook nell'editor di visual basic, provvede ad inizializzare: Private Sub Workbook_Open() ‘in questa zona va scritta l'istruzione End Sub Oppure, secondo esempio, se vogliamo che l'istruzione si attivi ad ogni variazione in un foglio di lavoro, possiamo scegliere l'evento "Change" dell'oggetto "Foglio1" ripetendo quando detto nell'esempio precedente: Private Sub Worksheet_Change(ByVal Target As Range) MsgBox ("Ciao !") End Sub Appare evidente che, selezionando l"Evento" più appropriato, tra quelli disponibili per l"oggetto" scelto, potremo pilotare l'esecuzione di codice come più ci necessita, compreso rendere l'automatismo dell'evento ancora più flessibile, inserendo un'istruzione opportuna, per esempio: Private Sub Worksheet_SelectionChange(ByVal Target As Range) If Range("A1") = ""
Then exit Sub
MsgBox ("Ciao !") End Sub (la differenza tra i due eventi sopra, che sembrano simili, è questa: l'evento "Change" si verifica quando, selezionando una cella, inseriamo un valore o modifichiamo quello già presente, premendo invio, altrimenti non si ha l'evento "Change". L'evento "SelectionChange" si verifica anche quando selezioniamo o clicchiamo su una cella diversa da quella attualmente selezionata, e quindi praticamente sempre, compreso un inserimento valore o modifica valore, perchè dovremo http://ennius.interfree.it/
Pagina 19
MANUALE VBA
X EXCEL
deselezionare la cella in questione per attivare la modifica, selezionandone un altra, compreso l'uso del tasto "invio" che sposta il focus sulla cella sottostante e quindi genera un cambio di selezione.) L'esempio sopra, nonostante sia abbinato all'evento "SelectionChange" (cambio di selezione di cella) e quindi praticamente ad ogni modifica che facciamo su un Foglio, "usa" un "interruttore di consenso" rappresentato da una cella (A1 per esempio): l'istruzione dice: "Se la cella A1 è vuota, esci da questa routine" : cioè non viene più eseguito tutto ciò che si trova sotto. Infatti l'esecuzione di qualunque istruzione contenuta tra inizio e fine routine, avviene sempre eseguendo le istruzioni riga dopo riga, partendo da inizio istruzioni e fino alla fine. Potremo quindi scrivere in A1 "Si" o qualunque altra cosa e l'istruzione verrà eseguita fino in fondo, o lasciare la stessa cella vuota per evitare di attivare la stessa istruzione. Il vantaggio di esecuzione di codice in automatico appare quindi evidente: non dovremo premere nessun pulsante, o richiamare nessuna macro, perchè sarà sufficiente il verificarsi di un "evento" . Ci sono istruzioni che però vorremo attivare direttamente noi, e non lasciarle gestire da "eventi" collegati ad un foglio di lavoro, sono quelle definite "Attivazione su comando". Questo tipo di istruzioni si possono inserire in due modi: Inserimento in un Modulo Inserimento in un "Evento" associato ad un oggetto preso dalla Casella degli Strumenti L'inserimento in un modulo è forse il metodo più conosciuto e lo stesso usato da Excel quando si fa uso del "Registratore di Macro". Manualmente si ottiene recandosi nell'editor di visual basic e, da "Inserisci", scegliamo "Modulo"; appare nella finestra degli "Oggetti" una nuova cartella "Moduli" e cliccandoci si apre mostrandoci "Modulo 1". Nella parte destra si dovrà inserire il codice, avendo cura di dare un nome alla routine, per esempio "Sub conteggi()". Excel predisporrà "End Sub". All'interno tra inizio e fine scriveremo il nostro codice. Esempio, facciamo eseguire due semplici operazioni, la somma di due celle (A1+A2) e la divisione della somma per 2, il risultato lo vogliamo in C1: Sub conteggi() Range("C1") =
(Range("A1") + Range("A2")) / 2
End Sub Il vantaggio di questa soluzione e che disporremo di un codice che potremo attivare a nostro piacimento. Come? Associando il codice (macro) ad un pulsante ottenuto usando un "Formato forme" degli strumenti "Disegno", magari un "rettangolo". Al "rettangolo" possiamo cambiare la dimensione, aggiungere del testo, cambiare il colore di sfondo e del font, posizionarlo sul foglio dove più ci piace, ecc. ecc. Una volta associato il "pulsante" ad una macro, tutte le volte che ci cliccheremo sopra, attiveremo il codice contenuto nella stessa macro. Un altro modo è quello di lavorare con la finestra "Moduli" ottenibile da "Visualizza/Barre degli Strumenti/Moduli". Appare una finestra con una serie di icone che rappresentano degli oggetti simili a quelli che sono nella finestra "Strumenti di Controllo": ATTENZIONE: non sono "OGGETTI", infatti non possiedono "Proprietà" nè "Eventi". Assomigliano molto di più alle "Forme" ottenibili da "Disegno" anche se il loro aspetto è decisamente più consono. L'associazione al codice è ancora più diretta: cliccando sull'icona "pulsante" e trascinando sul foglio, apparirà un bel pulsante e contemporaneamente una finestra per associazione alla macro, basta selezionare quella giusta. Trascuro di parlare dell'attivazione macro attraverso il percorso che passa dal menù "Strumenti/ Macro/Macro/Nome Macro/ Esegui" , secondo me troppo lungo e noioso. La differenza sostanziale che notiamo in queste soluzioni è che non sfrutteremo più nessun "EVENTO". Come contropartita, oltre ad inserire pulsanti sul nostro foglio di lavoro, (spesso lo spazio a disposizione non ci lascia troppe chances), avremo un' esecuzione codice che attiveremo solo se interveniamo noi. Esistono tuttavia casi nei quali vorremmo che l'attivazione del codice si verificasse in automatico, ma SOLO se interveniamo noi modificando uno o più dati del foglio di lavoro. Un pò come avviene con le formule inserite in un foglio di lavoro: basta cambiare un dato in una cella richiamata in una formula, e il risultato si aggiorna automaticamente senza bisogno di codice. (ricordo però che il codice NON può risiedere sul foglio di lavoro e quindi và attivato, in una maniera o nell'altra). Questi casi si affrontano utilizzando gli "Eventi" degli "OGGETTI" "STRUMENTI" presi dalla "Finestra degli Strumenti" o "Strumenti di Lavoro" , che sono la stessa cosa , ma vengono chiamati diversamente a secondo la versione di Excel che abbiamo. Se inseriamo in un foglio di lavoro, per esempio, un "combobox" detta in italiano "casella combinata", potremo sfruttare gli "eventi" che possiede (Change, Click, Dblclick, LostFocus, Keypress, ecc. ecc), scegliendo l'evento che meglio si adatta per attivare il codice, che ovviamente andrà http://ennius.interfree.it/
Pagina 20
MANUALE VBA
X EXCEL
inserito tra inizio e fine routine che si genera appena scelto l'evento da noi selezionato. Continuando con l'esempio, ipotiziamo di avere collegato la proprietà "ListFillRange" dell'oggetto combobox1 alla colonna A del Foglio1, dove dalla 5 alla 20a riga abbiamo inserito un elenco di nomi (ListFillRange A5:A20), e di avere scelto l' evento "Click", vedremo che la routine inizierà con Private Sub ComboBox1_Click() ‘Qui inseriremo l'istruzione End Sub Tornati sul foglio di lavoro, e usciti da modalità progettazione con un click sull'icona "Squadra" della finestra degli strumenti, vedremo che la nostra "casella combinata" porta tutti i nomi presenti nel range di celle che gli avevamo assegnato nella proprietà ListFillRange. Basterà selezionare un nome, tra quelli inseriti, (per selezionare usiamo un Click del mouse), per vedere attivato il nostro codice. L'evento "Click" si verifica tutte le volte che cliccheremo nella "zona bianca" della casella, NON se clicchiamo sul triangolino nero che attiva il "menù a discesa (zona bianca o, se preferite, la lista dei nomi)". Se questo evento può generare errori per "cliccaggi" imprecisi, potrete usare l'evento "Change" che si verifica tutte le volte che si "cambia", selezionandolo dal menù a discesa, un nominativo. Ritengo che l'attivazione di codice attraverso un "evento" collegato ad un "oggetto" sia di gran lunga da preferire, quando possibile, ad altri sistemi, compreso l'uso di un semplice "commandutton", in cui, nell'evento Private Sub CommandButton1_Click() inseriremo il nostro codice anzichè usare i moduli. (in questo caso non occorrerà dare un nome alla macro perchè il nome esiste già)
http://ennius.interfree.it/
Pagina 21
MANUALE VBA
X EXCEL
Registrazione Macro Registratore di macro. Il modo più veloce per compilare codice è quello di affidarsi al "Registratore di Macro". Come spiegato in questa sezione su "Cosa è il VBA" . Qualunque azione compiamo nella nostra cartella di lavoro, verrà scrupolosamente REGISTRATO, SCRITTO E CONVERTITO in codice VBA, visibile poi usando il Visual Basic Editor. L'utilità di questo strumento, il registratore, è importantissima per chi, spinto dal desiderio di approfondire le proprie conoscenze sul VBA, voglia cominciare a comprendere COME si compila del codice. E' sufficiente avviare il registratore di macro (vedi immagine a sinistra), apparirà prima una finestra dove ci verrà chiesto con quale nome vogliamo salvare la nostra macro, scelto un nome e confermato con OK, apparirà una piccola finestra con un quadratino blu: è il pulsante di fine registrazione (vedi immagine a destra - la finestrina è spostabile, tramite mouse, all'interno del foglio),
Cominciamo ad eseguire sul foglio di lavoro le operazioni che normalmente faremmo, per esempio: selezione della cella in cui vogliamo inserire una formula. (esempio la cella A10) Inserimento nella cella A10 della formula (con relativo segno = (uguale) all'inizio) esempio =SOMMA(A1:A9) cioè in A10 voglio il totale dei valori contenuti dalla cella A1 alla cella A9 comprese. Clicchiamo sul pulsante di fine registrazione, che si chiuderà automaticamente: bene la macro è compilata e registrata. Ora dal Menù Strumenti/Macro selezioniamo Visual Basic Editor e vedremo che se non c'era, nella casella degli oggetti e comparsa una nuova cartella Moduli, apriamo la cartella e troveremo "Modulo 1", doppio click su quest'ultimo e sulla destra apparirà la nostra macro: la riconosceremo perchè comincerà con la data di creazione della macro e il nome della macro Sub Nome da noi scelto() Range("A10").Select ActiveCell.FormulaR1C1 = "=SUM(R[-9]C:R[-1]C)" Range("A11").Select End Sub Tutto ciò che è compreso tra Sub - End Sub E' IL CODICE scritto nel famigerato VBA. Coloro che volessero cominciare a capirne il significato, potranno usare la guida in linea, dove c'è tutto, ovviamente diviso per voce e per argomenti, oppure acquistarsi un libro sull'argomento "Excel e il codice VBA" Va da se, che continuando a registrare macro su nuove azioni, cominceremo a crearci un esperienza delle varie compilazioni relative alle nuove azioni. Chi ben comincia....... o, se preferite, aiutati che Iddio.......
http://ennius.interfree.it/
Pagina 22
MANUALE VBA
X EXCEL
VBA : Esempi ed esercizi in ordine alfabetico
http://ennius.interfree.it/
Pagina 23
MANUALE VBA
X EXCEL
Aprire Applicativi (programmi) da Excel Quando vogliamo aprire da Excel, un file realizzato con un altro programma, (per esempio un file di Word, un database di Access, una presentazione di PowerPoint, ecc.) dobbiamo per prima cosa aprire l'Applicativo che ha generato il file, e nell'Applicativo aperto, far aprire poi il file. L'istruzione da compilare (una macro) dovrà necessariamente prevedere l'indirizzo completo (PathName) dove risiedono sia l'eseguibile (.exe) dell'Applicativo, sia in file da aprire. Per richiamare un file faremo ricorso alla Shell di sistema inserendo i percorsi suddetti. Per fare un esempio, ipotizziamo di aprire il file Pippo.doc che si troverà nella cartella Documenti, l'istruzione quindi sarà (gli manca "qualcosa", ma è solo per vedere come costruire la stringa): Shell ("C:\Programmi\Microsoft Office\Office\WINWORD.EXE C:\Documenti\Pippo.doc", 1) Come vedete, abbiamo inserito il percorso, prima dell'eseguibile, poi, preceduto da uno spazio, l'indirizzo dove risiede il file, poi una virgola, indi il numero 1 che serve a far aprire il file in primo piano. Ricordo che per indicare un "percorso" si deve primo, indicare l'unità (cioè Hard-disk. Floppy, o CDRom) seguito dalla cartella ed ev. dalle sottocartelle dove risiede il file da aprire. (l'unità si indica sempre con Una Lettera, (C o D ecc.) seguita da due punti ( : ) e la barra rovesciata ( \ ). Associando ad un pulsante, la macro, con un click otterremo l'apertura di Word che aprirà il file Pippo.doc, e la finestra di Word apparirà sopra quella di Excel che non viene chiuso. (L'istruzione sopra descritta, così comè, non funziona; per renderla funzionale bisogna togliere la virgola e in numero 1, così modificata, aprirebbe Word ma non ci metterebbe il focus, cioè Word sarebbe aperto, ma SOTTO la finestra di Excel). Per avere Word in vista, l'istruzione và modificata con l'aggiunta di una variabile, così: Dim myval myval = Shell ("C:\Programmi\Microsoft Office\Office\WINWORD.EXE C:\Documenti\Pippo.doc", 1) Questa istruzione funziona, ma presenta due inconvenienti : il primo è che l'istruzione può diventare "chilometrica", il secondo è che non tutti si rinvengono nel trovare il percorso dell'eseguibile. Per fortuna esiste un alternativa, l'utilizzo del metodo "Start" abbinato alla Shell, che ci consente di risparmiare spazio, ma soprattutto lascia alla Shell il compito di trovare il giusto percorso dell'eseguibile. La cosa funziona solo con tutti gli eseguibili di quei programmi che, in fase di installazione, si "registrano" nel Registro di Windows. Vediamo quindi questa istruzione, la prima per aprire solo il programma (Word per esempio), la seconda per aprire Word e il file che ci interessa: Shell ("Start Winword.exe") oppure Shell ("Start Winword.exe C:\Documenti\Pippo.doc") Una precisazione l'utilizzo del metodo Start, funziona con tutte le versioni di Windows98, 98SE, ME, ma non funziona con Windows XP (non ho ancora capito perchè, anzi se qualcuno riesce a fornirmi il motivo, avrà la mia gratitudine, e sarà citato come "salvatore della patria"). Con XP è necessario usare un altro tipo di istruzione (per aprire un' altra Applicazione Microsoft da Excel), che però consente di aprire solo l'eseguibile, ed è questa: Application.ActivateMicrosoftApp xlMicrosoftWord (E SOSTITUISCE QUESTA: Shell ("Start Winword.exe")) Gli Applicativi richiamabili, sono: xlMicrosoftWord xlMicrosoftPowerPoint xlMicrosoftMail http://ennius.interfree.it/
Pagina 24
MANUALE VBA
X EXCEL
xlMicrosoftAccess xlMicrosoftFoxPro xlMicrosoftProject xlMicrosoftSchedulePlus In data 01/04/03 Giuliano Gialli [email protected] ci fornisce una soluzione al problema Start con WindowsXP e Windows2000, sembra l'uovo di Colombo, ma bisognava pensarci, e Giuliano lo ha fatto. Questa la sua proposta, che funziona : bastava togliere Start e fare riferimento solo alla Shell, così: Shell ("Winword.exe"), 1 oppure se si deve aprire un file predefinito, far seguire al nome dell'applicativo il percorso del file da aprire con l'applicativo stesso: Shell ("Winword.exe C:\Documenti\Pippo.doc"), 1 Un sentito grazie a Giuliano. Le varianti sotto riportate potranno essere quindi modificate con l'utilizzo della soluzione appena esposta: Queste sono 2 varianti per aprire Word ed il file, è necessario il path completo anche di WordXP che su WindowsXP è diverso dalle altre versioni: Dim myVal myVal = Shell("C:\Programmi\Microsoft Office\Office10\WinWord.exe C:\Documents And Settings\Nome Utente\Documenti\Pippo.doc", 1) ‘Variante Giuliano: Shell("WinWord.exe C:\Documents And Settings\Nome Utente\Documenti\Pippo.doc"), 1 Seconda variante in due mosse: la prima consiste in una variabile fissa (X) che porta il path completo, simile alla precedente: Dim X X = "C:\Programmi\Microsoft Office\Office10\WinWord.EXE C:\Documents And Settings\Nome Utente\Documenti\Pippo.doc") myVal = Shell(X, 1) ‘Variante1 Giuliano: Dim X X = "C:\Documents And Settings\Nome Utente\Documenti\Pippo.doc" Shell ("WinWord.exe " & X & ""), 1 La seconda, più interessante perchè consente di poter variare sia il nome dell'eseguibile, sia il nome del file da aprire, legge i percorsi posti in due celle del foglio di lavoro. Mettiamo in A1 il path di Word e in A2 il path del file: cambiando inA2 il percorso possiamo mirare a qualunque file .doc , in qualsiasi cartella si trovi
http://ennius.interfree.it/
Pagina 25
MANUALE VBA
X EXCEL
Dim X X = Range("A1").Value & " " & Range("A2").Value myVal = Shell(X, 1) Variante2 Giuliano: Dim X X = (Range("A1").Value) & " " & (Range("A2").Value) Shell (X), 1 ‘il numero 1 che vediamo nell'istruzione serve per mettere il focus ‘su Word
http://ennius.interfree.it/
Pagina 26
MANUALE VBA
X EXCEL
Aprire file (o cartelle di Excel) .XLS Aprire tutti i files .xls contenuti in una cartella. Controllare se un files .xls è già aperto. Sul sito esistono già suggerimenti per l'apertura di file .xls, anche nella sezione "le vs domande", ma visto che continuano ad arrivare richieste specifiche, dedichiamo una pagina all'argomento. L'istruzione che serve all'uopo, è molto semplice: Workbooks.Open Filename:="percorso e nome file", ReadOnly:=False dove in "percorso e nome file" dovrete indicare in sequenza: l'unità, la cartella (directory), eventuali sottocartelle, nome del file completo di estensione, ed inserito come una stringa, tra doppi apici, ad esempio: "C:\Documenti\Ufficio\Clienti.xls" Come si nota, il percorso che mira al file è inserito direttamente nell'istruzione e consente di aprire sempre e soltanto quel file. Come fare allora quando vogliamo cambiare il file da aprire ? Imposteremo una variabile che di volta in volta rappresenti il nome di un file, e richiameremo la variabile nell'istruzione vista sopra. Per assegnare il nome del file da aprire alla variabile, potremo usare vari sistemi, vediamone alcuni, i più semplici: usare una cella del foglio di lavoro dove scrivere il SOLO nome (senza estensione, che sarà già presente nell'istruzione, dato che vorremo aprire solo file .xls). In questo modo, cambiando il nome, si aprirà quel file. usare una InputBox nella quale digitare il nome del file da aprire. creare una tabella con i nomi dei file che ci interessa aprire, assegnare al ListFillRange di una ComboBox i riferimenti alle celle che delimitano la tabella, ed assegnarle una cella di destinazione (LinkedCell) che restituisce di volta in volta il nome selezionato nella ComboBox (sto riferendomi alla ComboBox ActiveX presa NON da Moduli ma da Strumenti di controllo o Casella degli strumenti). La LinkedCell sarà assegnata alla variabile. Vediamo gli esempi: Sub apritisesamo() Dim X As String X = "C:\Documenti\" & Range("A1").Value & ".xls" Workbooks.Open Filename:=X, ReadOnly:=False End Sub Commenti: dichiariamo la variabile X come stringa (testo), poi assegniamo alla X (tra doppi apici, rossi) il percorso del file, dove il solo nome del file viene "preso" dal valore contenuto in una cella (nell'esempio la A1) usando il concatenamento di stringa, cioè doppio apice(" verde), spazio, e commerciale(&), spazio, riferimento alla cella come valore, spazio, e commerciale (&), spazio, doppio apice(" verde). A questo punto, dopo .Open FileName:= si scrive il nome della variabile X senza doppi apici, in quanto X è già comprensiva di doppi apici. Questo esempio è valido sia per il primo che per il terzo punto sopra citati. Ora vediamo con l'InputBox : Sub apritifile() Dim X As String nome = InputBox("Scrivi il nome del file da aprire") If nome = "" Then Exit Sub http://ennius.interfree.it/
Pagina 27
MANUALE VBA
X EXCEL
X = "C:\Excelweb\" & nome & ".xls" Workbooks.Open Filename:=X, ReadOnly:=False End Sub Commenti: in questo caso usiamo due variabili, la X di cui dichiariamo il Tipo, e "nome", che senza dimensionamento viene assunta come variabile di tipo Variant, e che serve a reperire il nome del file che scriveremo nella inputbox. Le altre cose che cambiano sono: l'aggiunta di una riga con la condizione che se non scriviamo niente nella inputbox, si esca senza generare errori, e la concatenazione della variabile "nome" al posto del riferimento ad una cella. Queste istruzioni servono nel caso che i vari file a cui vogliamo accedere, siano tutti all'interno di una stessa cartella. Se dovessimo aprire file che possono essere su cartelle diverse, dovremo creare una variabile anche per il nome della cartella, con lo stesso sistema in modo da poter variare sia il nome cartella sia il nome file, e quindi, con l'uso di due celle, per reperire nome cartella (A1) e nome file (B1), diventa: Sub apritisesamo2() Dim X As String X = "C:\" & Range("A1").Value & "\" & Range("B1").Value & ".xls" Workbooks.Open Filename:=X, ReadOnly:=False End Sub o con le inputbox: Sub apritifile2() Dim X As String cartella = InputBox("Scrivi il nome della cartella") If cartella = "" Then Exit Sub nome = InputBox("Scrivi il nome del file da aprire")
If nome = "" Then Exit Sub X = "C:\" & cartella & "\" & nome & ".xls" Workbooks.Open Filename:=X, ReadOnly:=False End Sub
AGGIORNAMENTO 09/01/04 Ora vediamo invece come eseguire un controllo se un file .xls è già aperto : se aperto, lo attiviamo, in caso contrario, lo apriremo. Usiamo quindi un ciclo For Each che controlli i nomi di tutte le cartelle Excel (Workbook) in quel momento aperte. Per la ricerca useremo il solo Nome della cartella completo di estensione, ma senza percorso. Se la certella è aperta, anche se non attiva in quel momento, la attiveremo ed usciremo dalla routine, viceversa useremo una delle istruzioni viste sopra per aprirla: Sub CercaApri() For Each wb In Workbooks If wb.Name = "Nomecartella.xls" Then http://ennius.interfree.it/
Pagina 28
MANUALE VBA
X EXCEL
wb.Activate Exit Sub End If Next ‘..e qui vanno le istruzioni per aprire il file se non è già aperto, ‘va bene anche questa: Workbooks.Open Filename:="percorso e nome file completo di estensione", ReadOnly:=False
End Sub
AGGIORNAMENTO 09/10/03 Quando si desideri, lavorando su un foglio, disporre di un comando per aprire contemporaneamente più cartelle di Excel contenute in una determinata Cartella dell'hard-disk (o Directory che dir si voglia), possiamo usare la seguente routine messa a punto su una richiesta ricevuta. In questo esempio sfruttiamo l' "oggetto" FileSearch (che restituisce il numero di file trovati e il nome di ciascuno di essi) e la sua proprietà LookIn per la ricerca di files all'interno di un percorso predefinito. La ricerca può essere fatta sia per nome di file, sia per estensione del file, che per solo una parte del nome, usando il carattere Jolly asterisco ( * ). Per determinare il nome o il tipo del file (estensione) possiamo usare due proprietà del FileSearch: la proprietà FileName, alla quale assegneremo il nome o l'estensione del o dei files da cercare, es: .FileName = "*.xls" e saranno cercati tutti ( * ) i file di Excel .FileName = "Fat*.xls" e saranno cercati tutti i files Excel che iniziano per Fat ( * ) la proprietà FileType, alla quale è necessario assegnare una costante MsoFileType (vedi guida in linea, digitando "FileType" e selezionando "Proprietà FileType"), che per le cartelle di Excel è la seguente. .FileType = msoFileTypeExcelWorkbooks Sub ApriTutteCartelle() Set fs = Application.FileSearch FileSearch With fs
'impostiamo con la variabile "fs" il
'con la variabile "fs"
.LookIn = "C:\Temp" cercare
'indicare il percorso della cartella in cui
'.Filename = "*.xls"
'alternativa se si sceglie di usare FileName
.FileType = msoFileTypeExcelWorkbooks 'in questo esempio uso la costante If .Execute() > 0 Then zero
'se la ricerca dei file è superiore al numero
'si fornisce un messaggio con il numero dei files trovati nel ‘percorso scelto. Questa 'istruzione può essere omessa, se non ‘interessa. MsgBox "Trovati " & .FoundFiles.Count & " file(s) xls." 'si inizia un ciclo For Next che a partire dal primo file trovato e fino all'ultimo, (file contati 'con .FoundFiles.Count) For i = 1 To .FoundFiles.Count http://ennius.interfree.it/
Pagina 29
MANUALE VBA
X EXCEL
'si aprono di seguito tutte le cartelle trovate, usando Workbooks.Open Workbooks.Open .FoundFiles(i) Next i Else 'altrimenti si avvisa col messaggio che non ci sono i files richiesti MsgBox "Non ci sono file xls" End If End With End Sub E' evidente che in questa routine il percorso dove cercare i file è un percorso fisso, inserito nelle istruzioni. Se si volesse decidere di volta in volta la cartella dove "pescare" i files .xls, basta rendere variabile il percorso assegnato al .FileName usando uno degli esempi presenti su questa pagina.
http://ennius.interfree.it/
Pagina 30
MANUALE VBA
X EXCEL
Calcolare Valori nelle celle Uno dei vantaggi del vba è quello di poter usare delle istruzioni per modificare, ricalcolandolo, un valore presente in una cella ottenendo nella cella stessa il nuovo valore. Classico esempio quello di volere applicare un aumento ai prezzi di un listino, ma potrebbe trattarsi di altre necessità, come voler dividere per un certo valore, i valori contenuti in una tabella, e presenti in una o più colonne. L'importante è NON applicare le modifiche a celle contenenti formule, pena la perdita delle stesse, e comunque non avrebbe senso in quanto il valore ottenuto in una cella per effetto di una formula ivi residente, è sempre influenzato dai valori delle celle richiamate nella formula stessa: basta cambiare quei valori. Vediamo come procedere, seguendo l'esempio dell'aumento di un listino: Identificare l'area su cui vogliamo effettuare il ricalcolo. L'area possiamo definirla in vari modi: Usare i riferimenti precisi dell'area : Set zona = ActiveSheet.Range("A1:A500") Usare il riferimento a tutta una colonna: Set zona = ActiveSheet.Range("A:A") Usare il riferimento alla cella iniziale della Colonna (A nell'esempio) e alla cella finale (con End) dell'area : Set zona = Range(Cells(1, 1), Cells.End(xlDown)) Usare il riferimento a tutta l'area che contiene dati sfruttando la proprietà UsedRange del foglio di lavoro : Set zona = ActiveSheet.UsedRange Ognuno sceglierà la forma più appropriata, tenendo presente che scegliere il riferimento a tutta la colonna comporta il fatto che verranno calcolate tutte le celle della colonna, compreso le celle vuote, ottenendo in questo caso un rallentamento (sono 65536 righe) e lo zero come risultato in quelle vuote. In questo caso basterà usare un istruzione ("Se la cella è vuota passa alla successiva.."). Vediamo gli esempi secondo l'ordine su esposto: Sub multi1() Dim cella As Range Set zona = ActiveSheet.Range("A1:A500") For Each cella In zona cella = cella + ((cella * 10) / 100) Next End Sub
Sub multi2() Dim cella As Range Set zona = ActiveSheet.Range("A:A") For Each cella In zona cella = cella + ((cella * 10) / 100) Next End Sub ora l'esempio sopra ma con l'esclusione delle celle vuote: Sub multi2i() Dim cella As Range Set zona = ActiveSheet.Range("A:A") For Each cella In zona http://ennius.interfree.it/
Pagina 31
MANUALE VBA
X EXCEL
If cella.Value = "" Then GoTo 10 cella = cella + ((cella * 10) / 100) 10: Next End Sub
Sub multi3() Dim cella As Range Set zona = Range(Cells(1, 1), Cells.End(xlDown)) For Each cella In zona cella = cella + ((cella * 10) / 100) Next End Sub
Sub multi4() Dim cella As Range Set zona = ActiveSheet.UsedRange For Each cella In zona cella = cella + ((cella * 10) / 100) Next End Sub Come vedete, le istruzioni cambiano solo nel metodo di identificazione dell'area. Se invece si volesse modificare il tipo di calcolo da eseguire, basterà modificare l'istruzione della riga dove si esegue il calcolo sul valore della "cella". Alcuni esempi: cella = cella - ((cella * 10) / 100) - per sottrarre una percentuale (10 nell'esempio) cella = cella / 100 - per dividere per 100 (o un'altro valore) il valore della cella cella = cella + ((cella * Range("H1").Value) / 100) - per calcolare un incremento in percentuale con il valore rappresentato da ciò che inseriremo in una cella esterna (H1). Potremo quindi rendere variabile la percentuale. cella = cella / Range("H1").Value - per dividere il valore di una cella per un valore rappresentato da ciò che inseriremo in una cella esterna (H1). Potremo quindi rendere variabile il divisore. I tipi di calcoli applicabili sono lasciati al libero arbitrio, è inutile esemplificarli tutti.
http://ennius.interfree.it/
Pagina 32
MANUALE VBA
X EXCEL
Calcolo date in VBA Abbiamo già detto che in Excel esistono due modi per poter lavorare : inserendo formule e funzioni direttamente nelle celle del foglio di lavoro, oppure attraverso l'inserimento di opportune istruzioni sfruttando il codice, il VBA, usando l'Editor di Visual Basic. Parlando di date, è necessario vedere le differenze tra i due modi di operare. Premesso che Excel pone come inizio calendario la data del 01/01/1900, e che vede una data inserita in una cella come UN NUMERO che rappresenta la differenza DEI GIORNI tra la data immessa e l'inizio calendario, parlando di gestione date, è necessario capire che esistono due modi di interpretazione delle date in Excel: Excel considera come primo giorno la data 01/01/1900 il VBA invece parte dal 30/12/1899 In questa sezione ci occuperemo delle date viste dal VBA, va precisato comunque che in programmazione, la conversione tra i due sistemi non impegna il programmatore: se si inserisce tramite una textbox (in una UserForm) una variabile di tipo "Date" nella cella del foglio di lavoro, la data che ivi apparirà sarà quella giusta. Per memorizzare una data o un orario, e necessario definire una variabile di tipo Date e ricorrere per assegnare un valore alla variabile, ad una sintassi particolare che prevede di inserire la data tra due caratteri "cancelletto" (#): per esempio, il 10 agosto 2002, può essere scritto: Private Sub CommandButton1_Click() Dim Mydate As Date Mydate = #08/10/2002#
'Mese/Giorno/Anno
TextBox1 = Mydate WorkSheets(1).Range("A1") = TextBox1 End Sub Come si può notare il formato prevede l'inserimento della data alla maniera inglese, invertito rispetto alla maniera italiana, cioè Mese/Giorno/Anno, questo perchè, lo ripeto, le istruzioni in codice vba devono essere compilate in inglese. Ci penserà lo stesso VisualBasic a convertire immediatamente nel formato standard, per cui nella TextBox1 noi vedremmo la data scritta all'italiana : 10/08/2002 e lo stesso nella cella A1 del foglio di lavoro. (la visualizzazione del formato data con anno a 4 cifre, dipende dalle impostazioni di settaggio contenute in "Pannello di Controllo/Impostazioni Internazionali/Data, voce:"Formato data breve": se è impostato gg/MM/aa tutte le visualizzazioni delle date saranno con l'anno a due cifre (le ultime due), se impostato gg/MM/aaaa invece l'anno sarà visualizzato a quattro cifre. Questo come visualizzazione standard in tutti i programmi; le celle di un foglio di Excel consentono di scegliere il formato data voluto, indipendentemente dalle impostazioni di sistema. Gli ultimi S.O. sono normalmente settati con anno a quattro cifre). Vorrei richiamare l'attenzione su un particolare: nell'esempio sopra, la parola "Date" è stata usata per definire il tipo di variabile (Dim Mydate è una Data), ma la parola "Date" è anche una Funzione e un'istruzione, e può essere usata per definire la data di sistema (data del giorno in cui si lavora). Vediamo l'esempio sopra modificato in questo senso: Private Sub CommandButton1_Click() TextBox1 = Date WorkSheets(1).Range("A1") = TextBox1 End Sub E nella textbox1 e in A1 vedremmo la data del giorno nel formato italiano. Questa istruzione ci consente di evitare di scrivere la data in tutte le occasioni in cui la registrazione di una data corrisponde alla data del giorno. (nel vba l'istruzione Date equivale alla Funzione =OGGI() inserita in una cella del foglio di lavoro). http://ennius.interfree.it/
Pagina 33
MANUALE VBA
X EXCEL
Ritornando al primo esempio (usato per definire la sintassi del formato data), notiamo che l'istruzione non ci consente di gestire liberamente l'inserimento di una data, in quanto la data è fissata via codice; (questo andrebbe bene se dovessimo inserire sempre una stessa data, e avremmo potuto fare a meno della textbox1, bastava infatti scrivere : WorkSheets(1).Range("A1") = Mydate ), come fare allora per far capire che stiamo inserendo una data e al tempo stesso usare la textbox per inserire date a nostro piacere? Possiamo usare usare due metodi : il primo sarà quello di assegnare una variabile di tipo Date alla textbox interessata, il secondo di impostare la formattazione della textbox in "formato data (Format)" prima della scrittura sul foglio di lavoro. Vediamo i due metodi: primo metodo: Private Sub CommandButton1_Click() Dim Mydate As Date Mydate = TextBox1 WorkSheets(1).Range("A1") = Mydate End Sub Con l'istruzione sopra otterremo il riconoscimento da parte di Excel della data come tale, e verrà rispettato anche l'eventuale formato cella preimpostato: abbiamo scelto per A1 il formato cella : categoria Data, e come tipo : Data 14 marzo 2001 (cioè con la data scritta col nome del mese e l'anno a 4 cifre), avremmo questo risultato anche se nella textbox abbiamo scritto la data usando il formato classico:
Non solo, ma l'istruzione precedente passa la data (immessa nella textbox1) ad Excel COME DATA, ed Excel l'avrebbe riconosciuta come tale, anche se avessimo lasciato la cella A1 con il formato cella impostato a "Generale", che è il Formato cella di default di tutte le celle. Sarebbe solo stata scritta con il formato data tradizionale, cioè 15/10/02, MA, sottolineo, come DATA, e non come diversamente potrebbe avvenire, come TESTO; visivamente avremmo visto lo stesso in A1 scritto 15/10/02, ma a sinistra nella cella (come un testo) e cosa più importante, sarebbero sorti problemi se avessimo utilizzato il dato contenuto in A1 per conteggi su date. Definirei l'istruzione sopra, come l'istruzione TIPO da usare quando si usino textbox per trasferire DATE al foglio di lavoro. Se poi vorremo un tipo di Formato data personalizzato, lo IMPOSTEREMO non via codice (Format), ma come Formato cella. Nel caso si avesse più di una textbox dove si immettono date e si userà un unico comando per trasferire le date sul foglio di lavoro, sarà sufficiente dimensionare la variabile una volta sola (Dim), assegnandogli più variabili (X,Y,Z), ,una per ogni textbox usata. Vediamo un esempio: Private Sub CommandButton1_Click() Dim X, Y, Z As Date X = TextBox1 Y = TextBox2 http://ennius.interfree.it/
Pagina 34
MANUALE VBA
X EXCEL
Z = TextBox3 WorkSheets(1).Range("A1") = X WorkSheets(1).Range("A2") = Y WorkSheets(1).Range("A3") = Z End Sub secondo metodo: Introduciamo esempi in cui si fa uso della Funzione "Format". E' infatti possibile definire il Formato di date personalizzato utilizzando la parola Format e, tra parentesi, il valore da formattare (textbox1), seguito dal tipo di formato dati inserito tra doppi apici. Vista la molteplicità dei formati, riporto di seguito una tabella completa e loro significato: Carattere
(:)
(/)
c
d dd ddd dddd ddddd
dddddd
aaaa w ww m
mm mmm mmmm http://ennius.interfree.it/
Descrizione Separatore di ora. In alcune impostazioni internazionali, possono essere utilizzati come separatori di ora altri caratteri. Il separatore di ora separa ore, minuti e secondi quando vengono formattati i valori di ora. Il carattere effettivo utilizzato come separatore di ora nell'output formattato è determinato dalle impostazioni del sistema in uso. Separatore di data. In alcune impostazioni internazionali possono essere utilizzati come separatori di data altri caratteri. Il separatore di data separa giorno, mese e anno quando vengono formattati i valori di data. Il carattere effettivo utilizzato come separatore di data nell'output formattato è determinato dalle impostazioni del sistema in uso. Visualizza la data come ddddd e l'ora come ttttt, nell'ordine. Visualizza solo informazioni relative alla data se nel numero seriale della data non è presente una parte frazionaria; visualizza solo le informazioni relative all'ora se non è presente alcuna parte intera. Visualizza il giorno come numero senza zero iniziale (1 – 31). Visualizza il giorno come numero con zero iniziale (01 – 31). Visualizza il giorno abbreviato (dom – sab). Visualizza il giorno per esteso (domenica – sabato). Visualizza una data completa (comprendente giorno, mese e anno), formattata in base alle impostazioni relative al formato di data breve del sistema in uso. Il formato di data breve predefinito è d/m/yy. Visualizza il numero seriale di una data come data completa (comprendente giorno, mese e anno), formattata in base alle impostazioni relative alla data estesa del sistema in uso. Il formato di data estesa predefinito è dd mmmm, yyyy. Equivale a dddd, ma corrisponde alla versione localizzata della stringa. Visualizza il giorno della settimana come numero (1 per domenica-7 per sabato). Visualizza la settimana di un anno come numero (1 – 54). Visualizza il mese come numero senza zero iniziale (1 – 12). Se m segue immediatamente h o hh, viene visualizzato il valore relativo ai minuti anziché il mese. Visualizza il mese come numero con zero iniziale (01 – 12). Se m segue immediatamente h o hh, viene visualizzato il valore relativo ai minuti anziché il mese. Visualizza il mese abbreviato (gen – dic). Visualizza il mese con il relativo nome per esteso (gennaio – Pagina 35
MANUALE VBA q y yy yyyy h Hh N Nn S Ss
ttttt
AM/PM
am/pm
A/P
a/p
AMPM
X EXCEL dicembre). Visualizza il trimestre dell'anno come numero (1 – 4). Visualizza il giorno dell'anno come numero (1 – 366). Visualizza l'anno come numero di due cifre (00 – 99). Visualizza l'anno come numero di quattro cifre (100 – 9999). Visualizza l'ora come numero senza zero iniziale (0 – 23). Visualizza l'ora come numero con zero iniziale (00 – 23). Visualizza i minuti come numero senza zero iniziale (0 – 59). Visualizza i minuti come numero con zero iniziale (00 – 59). Visualizza i secondi come numero senza zero iniziale (0 – 59). Visualizza i secondi come numero con zero iniziale (00 – 59). Visualizza un'ora come ora completa, ovvero vengono indicati l'ora, i minuti e i secondi, formattata con il separatore di ora definito dal formato di ora impostato nel sistema in uso. Viene visualizzato uno zero iniziale se è selezionata l'opzione Zero iniziale e l'ora è antecedente alle 10.00. Il formato di ora predefinito è h:mm:ss. Utilizza il formato 12 ore e visualizza AM in maiuscolo accanto alle ore precedenti mezzogiorno e PM in maiuscolo accanto alle ore comprese fra mezzogiorno e le 23.59. Utilizza il formato 12 ore e visualizza am in minuscolo accanto alle ore precedenti mezzogiorno e pm in minuscolo accanto alle ore comprese fra mezzogiorno e le 23.59. Utilizza il formato 12 ore e visualizza una A maiuscola accanto alle ore precedenti mezzogiorno e una P maiuscola accanto alle ore comprese fra mezzogiorno e le 23.59. Utilizza il formato 12 ore e visualizza una a minuscola accanto alle ore precedenti mezzogiorno e una p minuscola accanto alle ore comprese fra mezzogiorno e le 23.59. Utilizza il formato 12 ore e visualizza la stringa di caratteri AM definita dal sistema in uso accanto alle ore precedenti mezzogiorno e la stringa di caratteri PM definita dal sistema in uso accanto alle ore comprese fra mezzogiorno e le 23.59. AM e PM possono essere sia in maiuscolo che in minuscolo, tuttavia le maiuscole e minuscole della stringa visualizzata vengono adeguate alla stringa definita dalle impostazioni del sistema in uso. Il formato predefinito è AM/PM.
Formati predefiniti di data e ora (come Funzione Format) Visualizza una data e/o un'ora. Per i numeri reali visualizza una data e un'ora (ad esempio 3/4/93 17.34); se la parte frazionaria non è presente visualizza solo una data (ad esempio 3/4/93); se la parte intera non è presente General Date visualizza solo l'ora (ad esempio 17.34). La visualizzazione della data è determinata dalle impostazioni del sistema in uso. Visualizza una data in base al formato di data estesa del Long Date sistema in uso. Visualizza una data utilizzando il formato di data breve adeguato della versione dell'applicazione host.(Qualsiasi Medium Date applicazione che supporta l'utilizzo di Visual Basic, Applications Edition. Ad esempio, Microsoft Excel, Microsoft Project e così via.) Visualizza una data utilizzando il formato di data breve del Short Date sistema in uso. Long Time Visualizza un'ora utilizzando il formato di ora estesa del http://ennius.interfree.it/
Pagina 36
MANUALE VBA
X EXCEL
Medium Time Short Time
sistema in uso. Include le ore, i minuti e i secondi. Visualizza l'ora nel formato breve di 12 ore, indicando le ore, i minuti e l'identificatore AM/PM. Visualizza l'ora utilizzando il formato di 24 ore (ad esempio 17.45).
gli esempi : Private Sub CommandButton1_Click() TextBox1 = Format(TextBox1, "mm/dd/yy") WorkSheets(1).Range("A1") = TextBox1 End Sub va bene anche : Private Sub CommandButton1_Click() X = Format(TextBox1, "dd/mmm/yy") WorkSheets(1).Range("A1") = X End Sub Con le due istruzioni sotto, se si dichiara una variabile (X) come Date e gli si assegna un Formato, il Formato NON viene considerato e passa solo il formato Date della variabile che è quello classico 12/03/02, a meno che non si sia predisposto il Formato cella diversamente. Private Sub CommandButton1_Click() Dim X As Date X = Format(TextBox1, "dd/mmm/yy") WorkSheets(1).Range("A1") = X End Sub Oppure Private Sub CommandButton1_Click() Dim X As Date X = Format(TextBox1, "General Date") WorkSheets(1).Range("A1") = X End Sub Dal momento che si comincia a fare confusione, e non credo per colpa mia, ho riassunto nella tabella sotto i risultati che si ottengo usando sia l'istruzione che ho definito TIPO (con dimensionamento come Date), sia l'istruzione Format (senza dimensionamento come Date) e proveremo a trarre delle conclusioni. Nella colonna A ci sono i risultati delle date scritte nella textbox1, sempre scritti col formato classico: 12/02/02 ecc., nella colonna B, se è stata usata la Variabile Date o la formattazione e di che tipo, nella colonna C si trova il "formato cella" impostato in A, e nella colonna D , dove è stato impostato il formato cella a Data, il tipo di formato data. Nella colonna A le date riconosciute da excel come data, sono scritte a destra nella cella, le altre scritte sulla sinistra, vengono impostate come TESTO (senza triangolino verde in alto a sinistra nella cella) e dove appare il triangolino, Excel rileva che è necessaria una precisazione: confermare un formato data, considerarlo errore, ignorare e lo considera testo. La colonna C, dove si legge "generale > data", vuol dire che il formato cella, prima dell'inserimento della data, era impostato a "generale", dopo l'inserimento viene aggiornato da excel il formato cella in "data" e quindi si desume che la formattazione via codice è stata riconosciuta giusta. http://ennius.interfree.it/
Pagina 37
MANUALE VBA
X EXCEL
Non mi sono dilungato a proporre tutti i formati, per non diventare scemo. Chi vuole si farà tutte le prove che vorrà. Questa è una panoramica su uno degli aspetti di Excel che giudico molto confusionari, ma tant'è. Rimane ancora una cosa da vedere : fino ad ora abbiamo visto come impostare il codice per il trasferimento di una data dalla textbox al foglio di lavoro, ma cosa dobbiamo fare se vogliamo che anche la data inserita nella textbox assuma il formato voluto? Dovremo usare l'istruzione Format inserita in un evento della textbox. Non è possibile ottenere una formattazione durante la digitazione, e gli unici eventi utilizzabili sono l'evento AfterUpdate oppure l'evento Exit (simile a LostFocus del Vb). Ecco due esempi: Private Sub TextBox1_AfterUpdate() TextBox1 = Format(TextBox1, "dd-mm-yy") End Sub
Private Sub TextBox1_Exit(ByVal Cancel As MSForms.ReturnBoolean) TextBox1 = Format(TextBox1, "dd-mmmm-yy") End Sub Entrambi gli eventi si verificano quando si toglie il focus dalla textbox1: o spostandoci su un'altra textbox, oppure nell'istante in cui si preme sul commandbutton. ATTENZIONE!! Questo tipo di formattazione serve solo per la visualizzazione della data nella textbox, per cui NON bisogna usare il sistema inglese (Mese/Giorno/Anno), ma il nostro sistema (Giorno/Mese/Anno). Esiste un altro evento che potremo però utilizzare SOLO se usiamo una textbox per mostrarci delle date presenti in una cella : è l'evento Change della textbox, quindi useremo un'istruzione per caricare la textbox con il contenuto della cella A1, usando l'evento Activate della UserForm, Private Sub UserForm_Activate() TextBox1 = Worksheets(1).Range("A1").Value End Sub e nell'evento Change della textbox1 metteremo l'istruzione per visualizzare la data nel formato che avremo scelto e che può essere diverso dal formato data presente in A1: Private Sub TextBox1_Change() http://ennius.interfree.it/
Pagina 38
MANUALE VBA
X EXCEL
TextBox1 = Format(TextBox1, "dd-mmmm-yy") End Sub Volendo evitare di scrivere due istruzioni, potremo saltare l'evento Change e usare la formattazione nell'evento Activate dell'UserForm, così: Private Sub UserForm_Activate() TextBox1 = Worksheets(1).Range("A1").Value TextBox1 = Format(TextBox1, "dd-mmmm-yy") End Sub ComboBox e le date Ancora una precisazione: Quando si lavora usando delle ComboBox prese da "Strumenti di Controllo" (o casella degli strumenti), essendo oggetti che fanno parte delle ActiveX e quindi tipici del VisualBasic piuttosto che di Excel, è necessario provvedere alla "formattazione" dei dati che ricevono dal foglio di lavoro. Trattando di date, una ComboBox il cui ListFillRange va a "pescare" dati in una colonna di date, mostrerà nel menù a tendina, le date così come sono scritte nelle celle del foglio di lavoro, MA, nel momento in cui selezioniamo una di queste date per averla nella cella collegata (LinkedCell), le date presenti nel menù si trasformano in numeri : sono i numeri seriali con i quali excel "vede" le date, anche se sul foglio noi vediamo le date scritte con il giusto formato, e come tali le passa alla combobox. Per ovviare a questo comportamento, sono necessarie delle istruzioni che consentano il mantenimento del formato data scelto nelle celle, e, evitando di collegare la proprietà LinkedCell della combobox a nessuna cella, usare a suo posto una riga di istruzione. Nell'esempio sotto vediamo il costrutto : sfruttiamo l'evento Change della combobox in modo che ad ogni selezione di una data presente nel menù della stessa, si attivi l'esecuzione del codice: prima impostiamo la formattazione della data nel formato 5-feb-02 (d-mmm-yy)(scritto col formato italiano ma usando i termini inglesi), poi dichiariamo la variabile mydate di tipo Date, poi assegniamo la variabile alla combobox, indi gli diciamo quale cella del foglio di lavoro dovrà essere collegata (LinkedCell) al dato selezionato nella combobox. Le celle del foglio di lavoro interessate alle date dovranno avere il formato cella impostato uguale al formato scelto per formattare la combobox (personalizzato : g-mmm-aa)(sul foglio di lavoro i termini sono solo in italiano) : Private Sub ComboBox1_Change() ComboBox1 = Format(ComboBox1, "d-mmm-yy") Dim mydate As Date mydate = ComboBox1 Range("G5") = mydate 'G5 è la cella che va a sostituire la proprietà LinkedCell della combobox End Sub Per la trattazione degli orari, è previsto un nuovo paragrafo che seguirà questo.
http://ennius.interfree.it/
Pagina 39
MANUALE VBA
X EXCEL
Calcolo con le date in VBA 2 Nel paragrafo precedente abbiamo avuto un primo "assaggio" di cosa sono le date per il VBA. Ora vediamo su come poter lavorare con le date, faremo esempi che ci aiuteranno nel Calcolo su date. Vediamo il primo, un divertente esempio in cui, sfruttando due inputbox, chiederemo di introdurre dei dati e forniremo risposte ai quesiti posti (in verde sono i commenti che andranno tolti): Private Sub CommandButton2_Click() Dim Message, Title Message = "Inserisci la tua data di nascita :" 'Imposta il testo del messaggio. Title = "Giorni Vissuti" ' Imposta il titolo. Dim natoil As Date Dim giorni, anno As Integer natoil = InputBox(Message, Title) giorni = Date - natoil ‘viene calcolata la diff. tra la data del giorno e quella immessa ‘nell'inputbox (natoil) e 'viene restituito un valore che è il ‘Numero seriale dei giorni che intercorrono tra le due date e ‘memorizzato 'nella. variabile giorni MsgBox "hai vissuto per " & giorni & " giorni!" 'appare il messaggio che dice quanti giorni sono trascorsi anno = giorni / 365 'poi trasformiamo il Numero di giorni diviso 365 per ottenere a ‘quanti anni corrispondono MsgBox "e oggi hai " & anno & " anni!" End Sub e questo è il risultato (Date = 21/09/02):
Un esempio un pò più articolato e quello che presento sotto. Ho sfruttato una UserForm per mostrare due tipi di calcolo con date : ottenere la differenza tra due date, per poter eseguire calcoli sul risultato della differenza (in giorni, settimane, mesi o anni) moltiplicato per un costo unitario, e ottenere un totale. ottenere una data finale aggiungendo un periodo (giorni, settimane, mesi o anni) ad una data iniziale.
http://ennius.interfree.it/
Pagina 40
MANUALE VBA
X EXCEL
Le funzioni su cui si basano i calcoli sulle date sono : DateDiff e DateAdd. Funzione DateDiff Restituisce un valore Variant (Long) corrispondente al numero di intervalli di tempo tra due date specificate. Sintassi : DateDiff(interval, date1, date2[, firstdayofweek[, firstweekofyear]]) interval
Obbligatoria. Espressione stringa corrispondente all'intervallo di tempo utilizzato per calcolare la differenza tra date1 e date2. (vedi sotto interval)
date1, date2
Obbligatoria. Variant (Date). Date da utilizzare nel calcolo.
firstdayofweek
Facoltativa. Costante che specifica il primo giorno della settimana. Se non è specificata, verrà utilizzata Domenica.
firstweekofyear
Facoltativa. Costante che specifica la prima settimana dell'anno. Se non è specificata, verrà utilizzata la settimana nella quale cade il 1 gennaio.
Le possibili impostazioni dell’argomento interval sono: Impostazione Descrizione yyyy
Anno
q
Trimestre
m
Mese
y
Giorno dell’anno
d
Giorno
w
Giorno della settimana
ww
Settimana
h
Ora
n
Minuti
s
Secondi
Osservazioni : http://ennius.interfree.it/
Pagina 41
MANUALE VBA
X EXCEL
È possibile utilizzare la funzione DateDiff per determinare quanti intervalli di tempo specificati esistono tra due date. È, ad esempio, possibile utilizzare DateDiff per calcolare il numero di giorni che intercorrono tra due date oppure il numero di settimane tra la data odierna e la fine dell'anno. Per ottenere il numero di giorni tra date1 e date2 è possibile utilizzare sia l'impostazione del giorno dell'anno ("y") che quella del giorno ("d"). Quando per interval viene impostato il giorno della settimana ("w"), DateDiff restituisce il numero di settimane che intercorrono tra le due date. Se date1 cade di lunedì, DateDiff conterà il numero di lunedì fino a date2, includendo nel conteggio date2 ma non date1. Se per interval viene invece impostata la settimana ("ww"), la funzione DateDiff restituirà il numero di settimane del calendario comprese tra le due date. La funzione conterà il numero di domeniche che intercorrono tra date1 e date2, includendo nel conteggio date2 se cade di domenica, ma non date1, anche se cade di domenica. Se date1 è successiva a date2, la funzione DateDiff restituirà un numero negativo. Quando si esegue il confronto tra il 31 dicembre e l'1 gennaio dell'anno immediatamente successivo, DateDiff utilizzata con l'impostazione dell'anno ("yyyy") restituisce 1 anche se è trascorso un solo giorno. Funzione DateAdd Restituisce un valore Variant (Date) contenente una data alla quale è stato aggiunto un intervallo di tempo specificato. Sintassi DateAdd(interval, number, date) interval
Obbligatoria. Espressione stringa corrispondente all'intervallo di tempo che si desidera aggiungere.(vedi sotto)
number
Obbligatoria. Espressione numerica corrispondente al numero di intervalli da aggiungere. Può essere positiva, per ottenere date future, oppure negativa, per ottenere date passate.
date
Obbligatoria. Valore Variant (Date) o stringa che rappresenta una data a cui viene aggiunto l'intervallo.
Le possibili impostazioni dell’argomento interval sono: Impostazione Descrizione yyyy
Anno
q
Trimestre
m
Mese
y
Giorno dell’anno
d
Giorno
w
Giorno della settimana
ww
Settimana
h
Ora
n
Minuti
s
Secondi
Osservazioni :
http://ennius.interfree.it/
Pagina 42
MANUALE VBA
X EXCEL
La funzione DateAdd consente di aggiungere o sottrarre da una data un intervallo di tempo specificato. È, ad esempio, possibile utilizzare DateAdd per determinare la data risultante dall'aggiunta di 30 giorni alla data odierna oppure l'orario risultante dall'aggiunta di 45 minuti all'ora corrente. Per aggiungere giorni a date, è possibile utilizzare l'impostazione per il giorno dell'anno ("y"), per il giorno ("d") o per il giorno della settimana ("w"). La funzione DateAdd non restituisce una data non valida. In questo esempio, alla data 31 gennaio viene aggiunto un mese: DateAdd("m", 1, "31/01/95") In questo caso, DateAdd restituirà 28/02/95 e non 31/02/95. Se date è 31/01/96, la funzione restituirà 29/02/96, in quanto il 1996 è un anno bisestile. Se la data calcolata precede l'anno 100, vale a dire se si sottrae un numero di anni maggiore di quello presente in date, verrà generato un errore. Se number non è un valore Long, prima di essere valutato verrà arrotondato al numero intero più vicino. Nota Il formato del valore restituito dalla funzione DateAdd dipende dalle impostazioni del Pannello di controllo e non dal formato passato dall'argomento date. Nella form ho cercato di inserire anche alcuni passaggi che dovrebbero servire a familiarizzarsi con altri "oggetti" inseribili nelle form, come i "pulsanti di opzione" (OptionButton) che possono servire in molte altre occasioni per creare condizioni diverse all'esecuzione: in questo caso sono state abbinate alla scelta tra lavorare con date immesse dall'utente (inizio - fine) oppure usare la data di sistema (giorno attuale) come data iniziale, inserendo solo la data finale per ottenere una differenza futuribile su cui eseguire calcoli. La data di sistema l'ho abbinata solo a calcoli sulle differenze. Chi volesse usare questo metodo con le aggiunte periodi, potrà, leggendo come sono impostate le istruzioni in "differenza", modificare il codice. Desidero sottolineare che lo scopo di questo lavoro è quello di fornire indicazioni di massima su come operare, sarà compito di chi è interessato assimilare i concetti ed adattarli alle proprie esigenze. Ho inserito dei semplici controlli per la verifica della presenza dei dati nelle textbox, in modo che se ci si dimenticasse di inserire un dato, si venga fermati ed avvisati. Due dei controlli inseriti possono avere una validità anche in altri tipi di applicazioni su userform: controllo formato data: dovrà essere scritto come 02/11/01, cioè gg/mm/aa con le barre ( / ) e l'anno a due cifre (per evitare errori di digitazione): in caso di errore la data viene cancellata e si viene avvisati di quale sia il giusto formato, ed il focus si riposiziona sulla textbox con l'errore. (questo controllo l'ho abbinato solo al primo pulsante "differenza in giorni" e controlla l'inserimento sulle textbox2 e 3. Ovviamente è ripetibile per tutti gli altri pulsanti. controllo inserimento cifra con decimali e presenza della virgola di separazione dei decimali. Spesso viene usato il punto ( . ) in maniera impropria. questo controllo provvede a garantire l'inserimento di due soli decimali e della virgola; in caso contrario, si viene avvisati e la text box viene pulita. Questo controllo è abbinato solo al pulsante "calcola importo" e controlla l'inserimento sulla textbox5. Riporto di seguito le istruzioni usate per i due controlli menzionati (gli altri sono leggibili scaricando il file) 'controllo inserimento formato data If Mid(TextBox2, 3, 1) "/" Or Mid(TextBox2, 6, 1) "/" Then MsgBox "Scrivi la data come: 02/10/01" TextBox2 = "" TextBox2.SetFocus Exit Sub End If If Mid(TextBox3, 3, 1) "/" Or Mid(TextBox3, 6, 1) "/" Then MsgBox "Scrivi la data come: 02/10/01" TextBox3 = "" TextBox3.SetFocus Exit Sub End If http://ennius.interfree.it/
Pagina 43
MANUALE VBA
X EXCEL
'fine controllo----------'controllo inserimento 2 decimali con virgola Dim X X = Right(TextBox5, 2) If Right(TextBox5, 3) "," & X Then MsgBox "Gli importi vanno scritti con la virgola e due decimali" TextBox5 = "" TextBox5.SetFocus Exit Sub End If 'fine controllo Sono state usate due funzioni: Mid e Right di cui invito gli interessati a consultare la guida in linea per capire il funzionamento. Chi comunque avesse problemi a capire, troverà su questo sito, in futuro, una spiegazione. Il file è disponibile e scaricabile : Calcolo su date2000.zip 21 kb Ma non è ancora finita, ci risentiremo per completare l'argomento qui impostato e finire la panoramica.
http://ennius.interfree.it/
Pagina 44
MANUALE VBA
X EXCEL
Calcolo sulle date in VBA 3 Funzioni native per le date Continuiamo la panoramica dei calcoli con le date, esaminando alcune delle funzioni native del VBA che permettono di gestirle. Date abbiamo già visto nella pagina precedente l'impiego di questa parola; è opportuno riprenderla per sottolineare che viene utilizzata in contesti diversi con significati diversi : come tipo di dato assegnato ad una variabile, e che quindi è deputato a contenere date (o/e orari). come funzione per restituire la data di sistema, o per interrogazioni sul calendario del computer in questo esempio interroghiamo l'orologio interno e otteniamo il giorno della settimana in una finestra di messaggio, che porta nella barra del titolo il testo contenuto nell'istruzione Title: Dim X As Date X = Date MsgBox Prompt:=Format(X, "dddd"), Title:="Giorno della settimana per la data del " & X Time questa funzione restituisce un valore di tipo Date con l'orario corrente; quest'esempio mostra una finestra di messaggio con l'orario corrente: Dim x As Date x = Time MsgBox Time L'esempio è per vedere il costrutto, perchè in realtà per avere una messagebox con l'ora di sistema sarebbe sufficiente una semplice istruzione, così: MsgBox Time TimeValue questa funzione restituisce un valore Variant (Date) contenente un orario. Utile quando si voglia calcolare la differenza tra due orari. Questo esempio considera due celle, ad esempio la A1 e la B1 con due orari immessi, formato celle impostato a Ora, e restituisce la differenza in C1. Sub miaora() Dim cc As Date 'si impostano due variabili ( cc e pp ) come tipo Date Dim pp As Date pp = Range("A1").Value 'poi si assegna a pp l'orario che si trova in A1 y = TimeValue(pp) ' con y si memorizza la variabile pp come TimeValue cc = Range("B1").Value 'poi si assegna a cc l'orario che si trova in B1 z = TimeValue(cc) ' indi si memorizza con z la variabile cc come TimeValue Range("C1") = z - y 'e si restituisce in C1 la differenza tra i due orari http://ennius.interfree.it/
Pagina 45
MANUALE VBA
X EXCEL
End Sub Ricordo che tutto ciò che riguarda date ed orari NON può avere valore negativo, quindi dovremo sempre sottrarre l'orario minore da quello superiore, e non viceversa. Inoltre le ore partono dalle 0.0.0 e terminano alle 23.59.59. Se dobbiamo fare la differenza, per esempio tra le 17.30 e le 01.30, per calcolare un turno di lavoro che inizia alle 5.30 del pomeriggio e termina alle 01.30 di notte, non possiamo fare 01.30 - 15.20, perchè le 01.30 appartengono al giorno dopo. Infatti una differenza come l'esempio, viene vista come differenza tra le 5.30 del pomeriggio e le 01.30 di mattina, ma dello stesso giorno, e quindi negativa, con manifesta irritazione di Excel che segnala una serie infinita di cancelletti (#). Andrà infatti sommata la differenza tra le 23.59.59 e le 17.30, e tra le 01.30 e le 0.0.0 per ottenere il totale delle ore del turno. TimeSerial questa funzione restituisce un valore Variant (Date) che contiene l’orario corrispondente a un’ora, minuto e secondo specifici. Utile quando si voglia restituire un orario per differenza non da un'altro orario, ma da un certo numero di ore, minuti, secondi, da sottrarre ad un determinato numero di ore. Esempio (banale): supponiamo di voler sapere che ore saranno togliendo 5 ore e 25 minuti alle 17. Per meglio capire, vediamo prima la sintassi della funzione: TimeSerial(ora, minuti, secondi) La sintassi della funzione TimeSerial è composta dai seguenti argomenti predefiniti: Parte Ora Minuti Secondi
Descrizione Obbligatoria. Variant (Integer). Numero compreso tra 0 (12.00) e 23 (23.00) inclusi oppure un'espressione numerica. Obbligatoria. Variant (Integer). Qualsiasi espressione numerica. Obbligatoria. Variant (Integer). Qualsiasi espressione numerica.
E' necessario quindi fornire tutti e tre gli argomenti, (ore,minuti,secondi), separati da una virgola. Poichè la funzione consente di eseguire "direttamente" la differenza tra due valori ora, meno i minuti, meno i secondi, un istruzione va compilata in questo modo: TimeSerial(12 - 6, -15, 0) 12 è l'ora da cui vogliamo sottrarre 6 ore e 15 minuti, ovvero la funzione TimeSerial restituisce un'ora che corrisponde a sei ore (12 - 6) e a 15 minuti (0 - 15) prima di mezzogiorno, cioè le 5.45.00 Per indicare un'ora specifica, quale 11.59.59, ciascun argomento della funzione TimeSerial dovrà essere rappresentato da un numero incluso nell'intervallo di valori validi per quell'argomento. I valori validi per le ore vanno da 0 a 23, per i minuti e per i secondi da 0 a 59. Se un qualsiasi argomento non rientra nell'intervallo valido specifico dell'argomento, verrà incrementato all'unità superiore successiva in modo appropriato. Se si specifica, ad esempio, 75 minuti, l'argomento "minuti" verrà valutato come 1 ora e 15 minuti. Se si usasse un'ora + frazione di ora come 16,30 , verrà valutato solo l'argomento "ora" e non i minuti dopo la virgola. Se volessimo fare un esempio su un foglio di lavoro, dovremmo vedere una situazione del genere: C 1 2 3
D
E
F
G
16
4
20
0
dove in D3 abbiamo l'ora a cui vanno sottratte: 4 ore in E3 20 minuti in F3 0 secondi in G3 in C3 vorremo il valore restituito dalla funzione TimeSerial, che fornirà questo risultato: C
D
E
F
G
1 2 http://ennius.interfree.it/
Pagina 46
MANUALE VBA 3
X EXCEL
11.40.00 AM
16
4
20
0
e ora vediamo la procedura della macro: Sub Differenzaore() Dim gg 'si dichiara la variabile gg 'ora si assegnano a tre variabili i valori contenuti nelle celle x = Range("D3").Value 'con x si prende l'ora da cui sottrarre (16 o quel che vorrete) y = Range("E3").Value 'con y si prende il numero di ore da sottrarre da x vorrete)
(4 o quel che
z = Range("F3").Value 'con z si prende il numero di minuti da sottrarre da x vorrete)
(20 o quel che
gg = TimeSerial(x - y, -z, 0) 'ora si restituisce a gg il valore ottenuto con TimeSerial, usando le variabili a 'posto dei numeri. Per i secondi, visto che non ci interessano, usiamo lo zero (0) direttamente in formula. Range("C3") = gg 'poi si assegna a C3 il valore rappresentato da gg End Sub Attenzione: il formato delle celle da D a G 3 è "generale", in C3 provvederà il codice a formattare in formato personalizzato h.mm.ss. AM/PM. Se vorrete, potrete preimpostare la cella di destinazioni in un vostro formato preferito, esempio: personalizzato, h.mm.ss e la data sopra la vedreste 11.40.00 Capisco che siano argomenti un pò ostici, ma tant'è, e consiglio quindi di munirsi di santa pazienza e provare senza arrabbiarsi, per le proprie esigenze. E' sempre meglio comunque aiutarsi con la guida in linea.
http://ennius.interfree.it/
Pagina 47
MANUALE VBA
X EXCEL
Calendario perpetuo Lavoro realizzato in collaborazione con Marco Nocciolini ([email protected]) Questa applicazione è stata realizzata sfruttando la funzione =DATA() (inserita nel Range F5:F35) Sono state nascoste due colonne, la E e la H che riportano, la prima il riferimento alle due celle richiamate nella funzione DATA e, dal Range E5 al E35, le celle con inserito il numero progressivo dei giorni in un mese, la seconda, sempre nel Range H5:H35, la funzione =GIORNO.SETTIMANA() che converte il dato (contenuto nella stessa riga ma nella colonna F) in un numero da 1 a sette e che sono i corrispondenti di domenica, lunedi, martedi ecc. fino a sabato. Questo è il numero che viene letto dalla routine che provvede a evidenziare in rosso le celle che corrisponderanno a "domenica" (routine che trovate in calce). La colonna G (G5:G35) riporta una semplice uguaglianza alla stessa riga ma alla colonna F, solo che, avendo in F una data espressa in formato data, in G vogliamo che di questa data venga riportato il relativo giorno della settimana, ed è sufficiente formattare le celle G5:G35 scegliendo: "Numero/Personalizzato e scrivere "gggg" nella finestrina di immissione formato. Per quanto riguarda le due combobox, la prima, che "pesca" gli Anni dalla colonna S ("S5:S105") è stata presa dalla "Casella degli Strumenti" ed ha nell'evento Change, la routine che provvede prima a riportare le celle della colonna G (i giorni della settimana) ai colori iniziali, indi, trovato il giorno "domenica", colora di rosso la cella e di bianco il font, in modo che ad ogni "cambio" di anno vengano aggiornati i colori sui rispettivi giorni "domenica". La seconda, che deve fornire uno dei valori numerici che servono alla funzione =DATA(), è stata presa dalla finestra dei "moduli" che "pesca" i Mesi dalla colonna P (P5:P16), che mostra nel menù i nomi dei mesi, MA che restituisce non il mese selezionato ma il suo indice di riga e quindi un valore numerico. A questa seconda combobox (che non è un "oggetto" e non possiede eventi) è stata associata una macro con le stesse istruzioni dell'altra combobox. In questo modo, cambiando l'anno o il mese, avremo sempre lo stesso effetto di evidenziazione della "domenica". Sotto, la foto dell'applicazione e dopo la routine ed il file scaricabile.
Sub Macro1() With Range("G5:G35") .Interior.ColorIndex = Null .Font.ColorIndex = 1 End With Dim CL As Object
For Each CL In Range("h5:h35") http://ennius.interfree.it/
Pagina 48
MANUALE VBA
X EXCEL
If CL.Value = "1" Then
CL.Offset(0, -1).Font.ColorIndex = 2 CL.Offset(0, -1).Interior.ColorIndex = 3 End If Next End Sub
http://ennius.interfree.it/
Pagina 49
MANUALE VBA
X EXCEL
Cambiare i Fonts sul foglio di lavoro. Un indicazione per tutti coloro che intendono sostituire il font (carattere) in Celle o Range di celle sul foglio di lavoro, usando codice VBA. Il font predefinito di Excel è l'Arial, ma come tutti sappiamo è possibile modificare questa impostazione selezionando nella finestra dei font il tipo e la dimensione del carattere, scegliendo tra i font installati sul nostro computer. E' possibile però utilizzare una macro, da associare ad un pulsante oppure sfruttando l'evento Worksheet_Change del foglio di lavoro per modificare il o i font. Come procedere: useremo una cella dove scriveremo il nome del font che ci interessa utilizzare usando il suo nome esatto (che possiamo reperire nella su menzionata finestra), e poi usare queste istruzioni: supponendo di usare la cella A1 come vettore per il nome del font, affideremo il riferimento a questa cella ad una variabile che chiameremo "X". Dovremo inoltre identificare la cella o le celle alle quali applicare il font scelto, quindi vediamo il primo esempio, in cui la cella o le celle di destinazione del font è lasciata ad una selezione sulla quale agirà l'istruzione Sub MioFont() Dim X X = Range("A1").Value 'in A1 scriveremo il nome di un font With Selection.Font .Name = "" & X & "" 'assegnazione alla proprietà Font.Name del nome scritto in A1 .Size = 16 'questa proprietà determina la grandezza del font (di default è 10) .Strikethrough = False .Superscript = False .Subscript = False .OutlineFont = False .Shadow = False .Underline = xlUnderlineStyleNone .ColorIndex = xlAutomatic End With End Sub secondo esempio con definizione di un range di celle sul quale agire: Sub MioFont2() Dim X X = Range("A1").Value With Range("A3:C5").Font 'range sul quale applicare il font scritto in A1 .Name = "" & X & "" .Size = 14 .Strikethrough = False http://ennius.interfree.it/
Pagina 50
MANUALE VBA
X EXCEL
.Superscript = False .Subscript = False .OutlineFont = False .Shadow = False .Underline = xlUnderlineStyleNone .ColorIndex = xlAutomatic End With End Sub terzo esempio inserito nel Worksheet_Change, in questo modo la routine viene applicata sul Range designato ad ogni cambio di valore in una cella, ma solo sulle celle del range : Private Sub Worksheet_Change(ByVal Target As Range) Dim X X = Range("A1").Value With Range("A3:C5").Font .Name = "" & X & "" .Size = 14 .Strikethrough = False .Superscript = False .Subscript = False .OutlineFont = False .Shadow = False .Underline = xlUnderlineStyleNone .ColorIndex = xlAutomatic End With End Sub E' possibile ridurre le istruzioni delle proprietà che non interessano, ottenendo comunque l'impostazione con il font scelto, aggiungendo magari solo il "grassetto" (Bold), in questo esempio: Private Sub Worksheet_Change(ByVal Target As Range) Dim X X = Range("A1").Value With Range("A13:C15").Font .Name = "" & X & "" .Size = 10 .Bold = True 'imposta il grassetto End With End Sub Per i curiosi, è possibile controllare il significato delle proprietà (eliminate) usando la guida in linea.
http://ennius.interfree.it/
Pagina 51
MANUALE VBA
X EXCEL
Caricare una ListBox Un breve esercizio basato sul metodo AddItem per caricare i dati, anzichè sfruttare la proprietà ListFillRange se la ListBox si trova sul foglio di lavoro, oppure RowSource se la ListBox è su una UserForm. Stiamo parlando di una ListBox ActiveX, presa quindi da "Strumenti di controllo" (o Casella degli strumenti) anzichè da Moduli. In questo esempio ipotizziamo di voler reperire i dati non da una sola colonna, come di solito facciamo, ma da un certo numero di colonne, tutte conseguenti, ma di cui non sappiamo nè il numero di colonne che contengono i dati che vogliamo caricare, nè quante righe di dati saranno presenti nelle singole colonne. Si tratta quindi di una situazione che spesso si verifica, quella di non conoscere l'esatta dimensione della zona contenente i dati, e allora vediamo come impostare le routine: Useremo la proprietà End per reperire, data una cella iniziale, sia l'ultima colonna occupata, sia l'ultima riga occupata di ogni colonna. Per fare questo sarà necessario che i dati siano su celle contigue, senza celle vuote nel mezzo, altrimenti End si fermerebbe all'ultima cella occupata anche se i dati continuassero su celle sottostanti. Lo stesso dicasi per le colonne. Una volta reperita l'ultima Colonna occupata da dati, conteremo quante sono le colonne con Columns.Count. Il numero così ottenuto lo useremo come indice estremo in un ciclo For Next che scorra le colonne, una alla volta, partendo dalla prima fino all'ultima rappresentata dal numero indice. Lo stesso faremo, con un altro ciclo For Next interno al primo ciclo, per reperire quante sono le righe di ogni colonna. Otteniamo quindi che avviata la macro, verranno contate le colonne, poi inizieremo dalla prima e ne conteremo le righe, poi il ciclo passerà alla seconda colonna e verranno contate le righe di questa, e così via fino all'ultima colonna. Ad ogni scorrimento di cella, verrà preso il dato (valore) ivi presente, assegnato ad una variabile, e questa variabile restituirà il valore alla ListBox che l'aggiungerà nella lista. vediamo allora le istruzioni, e nell'esempio useremo come cella iniziale del nostro elenco la cella A1: Sub CaricaLista() 'con zonac impostiamo l'area che si trova dalla cella A1 fino ‘all'ultima colonna occupata, 'andando verso destra (End(xlToRight)) Set zonac = Range(Cells(1, 1), Cells(1, 1).End(xlToRight)) 'con x memorizziamo in numero di quante colonne sono in zonac x = zonac.Columns.Count 'prima di avviare i cicli che caricheranno i dati, puliamo la ListBox ActiveSheet.ListBox1.Clear 'ora inizializiamo il primo ciclo che scorrerà le colonne, partendo dalla numero 1, 'assegnando alla variabile z il numero indice For z = 1 To x 'ora impostiamo con zonar, andando verso il basso (End(xlDown)) l'area che comprende 'le righe occupate da dati, e con la variabile y memorizziamo il numero di queste righe Set zonar = Range(Cells(1, z), Cells(1, z).End(xlDown)) y = zonar.Rows.Count 'ora iniziamo il secondo ciclo, che scorrerà tutte le righe contate, nella colonna il cui 'numero è ora rappresentato da z, assegnando alla variabile t il numero indice di riga, 'partendo dalla 1 e fino alla numero y For t = 1 To y 'ora memorizziamo con w il valore che verrà trovato nella cella, riga t, colonna z w = Cells(t, z).Value http://ennius.interfree.it/
Pagina 52
MANUALE VBA
X EXCEL
'e carichiamo con AddItem questo valore (w) nella ListBox ActiveSheet.ListBox1.AddItem w
Next 'con questo Next passiamo al secondo indice t (riga successiva), e finito il ciclo alla 'riga y, passiamo al Next successivo che si occuperà di scorrere nella successiva colonna, 'ritornando al primo For (z) ripetendo poi il ciclo sulle righe della nuova colonna. Next
End Sub Mi sembra che le spiegazioni siano chiare, e vediamo un'immagine che illustra l'esercizio
Ho evidenziato con le frecce rosse i valori presenti nelle ultime celle di ogni riga per mostrare come avviene la sequenza di caricamento, progressiva per ogni colonna. Si vede altresì che si possono caricare indifferentemente sia testo, sia date, sia numeri.
http://ennius.interfree.it/
Pagina 53
MANUALE VBA
X EXCEL
Cerca e seleziona con evidenziazione, nell'area trovata, dei caratteri in rosso e in grassetto. Questa volta l'esercizio proposto rientra in una casistica spesso richiestami: come evidenziare una zona di dati compresa da due valori presenti in una tabella, ma di cui non conosciamo la posizione, sia dei valori nella tabella, sia della tabella stessa. Supponiamo di avere quindi una tabella come quella sotto (in cui per mia comodità ho inserito dei numeri con valore univoco. Attenzione: i valori devono essere univoci, altrimenti le istruzioni qui usate non funzionano; con valori doppi o ripetuti dovremo usare altre istruzioni, ma facciamo una cosa per volta, la routine per i valori doppi la trovate alla fine di questa pagina).
Vediamo i singoli passaggi per capirli meglio, poi alla fine ricostruiamo l'intera sequenza. identificare la posizione della tabella sul foglio di lavoro; per fare questo ci affideremo all' UsedRange, (di cui esistono spiegazioni in questa sezione, paragrafo "Copia/Incolla 4") e assegneremo con l'istruzione Set, un nome alla tabella (zona) dell'UsedRange nel foglio attivo: Set zona = ActiveSheet.UsedRange faccio presente che la proprietà UsedRange rintraccia una tabella anche se non sono presenti valori, ma purchè le celle destinate a formare la tabella siano state "bordate". Inserimento dell'istruzione che ripristina il colore del font a nero e toglie il grassetto ai font che eventualmente fossero stati evidenziati; per fare questo useremo il costrutto With oggetto....End With, in modo da poter con un'unica istruzione, modificare tutte le "proprietà" volute, all'interno delle celle comprese nell'UsedRange: With zona .Font.ColorIndex = 0 .Font.Bold = False End With inseriamo l'istruzione per chiamare una InputBox (finestra di dialogo) nella quale scriveremo il primo valore da cercare tra i dati presenti nella tabella ( chiamata "zona" ). Il valore che scriveremo dovrà essere memorizzato e quindi dimensioniamo una variabile, la X, atta a contenere il dato immesso nella InputBox. Nell'istruzione dovremo dichiarare, tra parentesi, il messaggio che la finestra di dialogo ci mostrerà ("dato uno" o qualunque altra frase che noi vorremo) e il titolo (che apparirà nella barra della InputBox: "inserisci il primo dato" ): X = InputBox("dato uno", "inserisci il primo dato")
http://ennius.interfree.it/
Pagina 54
MANUALE VBA
X EXCEL
per evitare che il debugger, in caso di mancanza di inserimento del dato nella inputbox, o per aver premuto il pulsante "annulla", si "arrabbi" e ci segnali un errore, inseriremo una semplice istruzione : se il valore rappresentato dalla X sarà vuoto, usciremo dalla routine: If X = "" Then Exit Sub inizializiamo ora il ciclo di ricerca assegnando un nome ( CL, ma l'avremmo potuta chiamare "pippo", sarebbe lo stesso ) ad una variabile, ed il "tipo" di variabile come "Object" (oggetto ) (Dim stà per : dimensioniamo). Dim CL As Object l'istruzione seguente dice : per ogni CL (l'oggetto cella) presente in "zona", se il valore presente nella cella è uguale al valore memorizzato nella X, allora assegnamo alla variabile "uno" l'indirizzo (Address) della cella trovata col valore uguale (indirizzo = riferimento). I cicli For Each....Next consento di eseguire le istruzioni di ricerca, cella per cella, fino alla fine dell'area assegnata. Poi, visto che stiamo lavorando con dei numeri (se si facesse cercare del testo non sarebbe necessario), E' NECESSARIO dichiarare il "tipo" di dati restituito dalla InputBox, altrimenti il codice non riconoscerebbe come numero il dato inserito. Per fare questo usiamo la funzione Val() che restituisce i numeri inclusi in una stringa sotto forma di valore numerico di tipo appropriato. Dobbiamo inoltre ricordare che la funzione Val riconosce solo il punto (.) come separatore decimale valido, se quindi lavoreremo con numeri decimali, nella InputBox dovremo scrivere il valore da cercare usando il punto ( . ) come separatore e NON la virgola, mentre ovviamente sul foglio di lavoro i decimali dovranno essere regolarmente inseriti con la virgola ( , ). Oppure definire come "tipo" di dati non più Val ma CDbl (es. CDbl(X) ) in questo modo potremo usare la virgola ( , ) come separatore, nell'inserimento del valore nella InputBox. For Each CL In zona If CL.Value = Val(X) Then uno = CL.Address Next Dal momento che potremmo cercare un dato che potrebbe essere non presente nell'area, avremo bisogno di essere avvisati con un messaggio, e sopratutto di impedire che la routine prosegua generando poi un errore perchè l'indirizzo assegnato alla variabile "uno" non è stato trovato. Useremo quindi la seguente istruzione: se "uno" è vuoto, avvisa con il messaggio ed esci dalla routine: If uno = "" Then MsgBox "Valore non presente" Exit Sub End If se invece il valore è presente, proseguiamo con la ricerca, sempre tramite InputBox, del secondo valore da cercare. Le istruzioni sono le stesse usate per il primo ciclo di ricerca, cambiano solo i nomi assegnati alle variabili: Y = InputBox("dato due", "inserisci il secondo dato") If Y = "" Then Exit Sub For Each CL In zona If CL.Value = Val(Y) Then due = CL.Address Next
If due = "" Then MsgBox "Valore non presente" Exit Sub http://ennius.interfree.it/
Pagina 55
MANUALE VBA
X EXCEL
End If dato per scontato, di avere trovato tutte e due i valori cercati, e di essere quindi in possesso dei riferimenti delle celle, selezioneremo tutta l'area rappresentata dai riferimenti, con la seguente istruzione : Range(uno & ":" & due).Select ora che abbiamo selezionato l'area, assegneremo le proprietà ai font presenti: il colore dei font, e il grassetto. Per il colore useremo il codice colore voluto (io ho usato il rosso = 3) e usciremo dalla routine (End Sub) With Selection .Font.ColorIndex = 3 .Font.Bold = True End With End Sub E questo sarà il risultato della nostra macro, ipotizzando di avere scelto come primo valore 13 e come secondo 53 :
E questa la routine completa: Sub selezona()
Set zona = ActiveSheet.UsedRange
With zona .Font.ColorIndex = 0 .Font.Bold = False End With
X = InputBox("dato uno", "inserisci il primo dato") If X = "" Then Exit Sub
http://ennius.interfree.it/
Pagina 56
MANUALE VBA
X EXCEL
Dim CL As Object For Each CL In zona If CL.Value = Val(X) Then uno = CL.Address Next
If uno = "" Then MsgBox "Valore non presente" Exit Sub End If 'secondo ciclo Y = InputBox("dato due", "inserisci il secondo dato") If Y = "" Then Exit Sub For Each CL In zona If CL.Value = Val(Y) Then due = CL.Address Next
If due = "" Then MsgBox "Valore non presente" Exit Sub End If
Range(uno & ":" & due).Select
With Selection .Font.ColorIndex = 3 .Font.Bold = True End With
End Sub Stessa procedura con variante per selezionare un area cercando due valori uguali presenti nella tabella (zona). vediamo i passaggi che differiscono dalla precedente: ovviamente inseriremo lo stesso valore sia nella prima che nella seconda InputBox. premessa: il ciclo For Each.....Next "gira" cercando nella zona assegnata tutte le occorrenze uguali al valore da cercare, e nel caso di più valori uguali, li rintraccerebbe tutti ma si fermerebbe sempre all'ultimo valore trovato. dovremo quindi "interrompere" l'azione di ricerca del primo valore dopo che sarà stato trovato e sarà stato memorizzato il suo Address (indirizzo o riferimento cella), in modo da passare alla seconda InputBox che ci chiederà il secondo valore da cercare e inizializzerà il secondo ciclo di ricerca. Per fare questo ci affidiamo all'istruzione GoTo che ci farà uscire dal primo ciclo se il primo valore sarà stato trovato e ci manderà direttamente al secondo ciclo. Questo secondo ciclo, leggerà anche il primo valore, ma si fermerà solo all'ultimo valore trovato e memorizzerà SOLO questo Address. If CL.Value = Val(X) Then http://ennius.interfree.it/
Pagina 57
MANUALE VBA
X EXCEL
CL.Select uno = ActiveCell.Address GoTo 10 GoTo 10 vuol dire semplicemente : "vai al numero di riga 10" e il compilatore si sposterà alla riga indicata senza eseguire le istruzioni che si trovano tra il GoTo e la riga indicata. In questo modo saltiamo l'istruzione Next posta alla fine del primo ciclo interrompendolo in quanto il primo valore è stato trovato, e dovremo cercare il secondo o ultimo. Inseriremo quindi il numero 10 seguito dal segno dei due punti ( : ) immediatamente prima dell'istruzione che dovrà essere eseguita: 10: Y = InputBox("dato due", "inserisci il secondo dato") If Y = "" Then Exit Sub For Each CL In zona If CL.Value = Val(Y) Then due = CL.Address Next In questo modo otteniamo che: se il secondo valore o ultimo non esiste in quanto il valore nella tabella è univoco, verrà evidenziato solo il primo. nel caso di errore di digitazione si cercasse un numero che non è presente, si verrà avvisati che il numero non esiste. se il secondo numero è diverso dal primo ed è presente in tabella, verranno evidenziati tutti i valori compresi tra i due ( e si verifica quello che otteniamo con la prima routine sopra indicata ). e questa la seconda routine completa: Sub selezonadue()
Set zona = ActiveSheet.UsedRange
With zona .Font.ColorIndex = 0 .Font.Bold = False End With
X = InputBox("dato uno", "inserisci il primo dato") If X = "" Then Exit Sub Dim CL As Object For Each CL In zona If CL.Value = Val(X) Then CL.Select uno = ActiveCell.Address GoTo 10 If uno = "" Then MsgBox "Valore non presente" Exit Sub http://ennius.interfree.it/
Pagina 58
MANUALE VBA
X EXCEL
End If End If
Next
10: Y = InputBox("dato due", "inserisci il secondo dato") If Y = "" Then Exit Sub For Each CL In zona If CL.Value = Val(Y) Then due = CL.Address Next
If due = "" Then MsgBox "Valore non presente" Exit Sub End If
Range(uno & ":" & due).Select With Selection .Font.ColorIndex = 3 .Font.Bold = True End With End Sub
http://ennius.interfree.it/
Pagina 59
MANUALE VBA
X EXCEL
Un esercitazione interessante : Ricerca tra due date con totale dei valori correlati. Una delle necessità che spesso incontriamo nella realizzazione dei nostri lavori è la possibilità di ottenere dei totali di valori inseriti in una tabella (o elenco), relativi a due parametri che vogliamo poter definire di volta in volta, e quindi variabili. Un tipico esempio potrebbe essere quello di voler scegliere, tra due date, quanto fatturato, o incassato, o comunque quanto è il totale dei valori registrati tra le due date, e magari per tipi di prodotti, o argomenti, o quello che vi pare. Presento quindi un esempio basato su una tabella dove avremo una colonna, la A, dove scriveremo le date, e tre colonne (B,C,D) dedicate a tre tipi di prodotti, dove registreremo gli incassi del giorno. Scopo della routine sarà quello i poter selezionare due date, tra quelle presenti, ed ottenere il totale dei valori dei tre prodotti, nel periodo specificato. Va da sè che la soluzione presentata si presta a tutte le varianti che vorrete, quello che importa è vedere le istruzioni impiegate. Sotto la tabella usata per l'esempio, nella cella F4, il risultato della somma richiesta dei valori relativi alle tre colonne dei prodotti, tra le date 12/01/03 e 25/01/03:
Come si diceva si possono variare tutte le impostazioni, per esempio di chiedere la somma colonna per colonna (basterà variare i riferimenti su cui operare, e/o ripetere le istruzioni per tante colonne quante vorremo, oppure usare delle InputBox per definire su quali riferimenti colonna lavorare (vedi ultimo esempio)). Ho basato questo progettino su due InputBox che ci chiedono di indicare le due date tra cui eseguire la somma dei valori di tutte e tre le colonne comprese nel periodo. Le date vengono cercate nella Colonna A, e ho inserito un controllo che avvisa se la/le date inserite non figurano nell'elenco, e viene riproposta la richiesta di inserire una data presente in elenco. Per quanto riguarda i riferimenti che servono a definire la zona da sommare, le colonne le abbiamo già, ma non sappiamo a priori quali righe saranno interessate, e allora ho preparato una Funzione Utente (enn) che cerca il numero di riga relativo alle date che inseriremo nelle InputBox e questi dati vengono memorizzati in due variabili. A questo punto basterà legare le variabili "riga" alle colonne, per avere i riferimenti necessari alla funzione =SOMMA (=SUM in inglese). Poichè anche la funzione enn può "girare a vuoto" se non viene trovata la data immessa, è stata munita di un controllo che la fa uscire dalla funzione e che fornisce zero come risultato della funzione. Vediamo le istruzioni, prima la Funzione Utente (vedi event. paragrafo precedente in questa stessa sezione), e poi la procedura per la somma. Function enn(Intervallo, Valore) If Valore = "" Then Exit Function For Each c In Intervallo If c.Value = Valore Then http://ennius.interfree.it/
Pagina 60
MANUALE VBA
X EXCEL
Y = c.Row 'con Y prendiamo il numero di riga corrispondente al Valore trovato Exit For End If Next c enn = Y 'enn sarà uguale al numero di riga trovato in Intervallo End Function e questa la procedura Sub mia() Dim datauno As Date 'dichiarazioni delle variabili necessarie. con datauno prendiamo il 'valore che verrà fornito dalla prima inputbox, e bisogna definirla come data perchè il 'codice la renda come tale. Dim datadue As Date 'come sopra ma per la seconda inputbox. Dim mioval, titolo, messaggio 'variabili per le inputbox
Set Intervallo = Range("A4:A20") 'con Intervallo definiamo il range su cui intervenire per cercare le ‘date 10: 'rimando per riavviare l'inputbox in Intervallo
nel caso di prima data non trovata
titolo = "Inserisci una data" messaggio = "Scrivi la PRIMA data del periodo" mioval = InputBox(messaggio, titolo) If mioval = "" Then Exit Sub 'se non si scrive nulla nella inputbox si esce datauno = mioval 'datauno ora è uguale alla data scritta nell'imputbox rigauno = enn(Intervallo, datauno) 'si ottiene il numero di riga con la "funzione utente" e si 'assegna a rigauno If rigauno = 0 Then 'se rigauno è zero (data non trovata) si dà il messaggio sotto MsgBox "la data non è presente. Scegliere un'altra data" GoTo 10 'e si ritorna a 10 per riavviare la inputbox di richiesta data End If 'si ripete sotto per la seconda data http://ennius.interfree.it/
Pagina 61
MANUALE VBA
X EXCEL
20: titolo = "Inserisci una data" messaggio = "Scrivi la SECONDA data del periodo" mioval = InputBox(messaggio, titolo) If mioval = "" Then Exit Sub datadue = mioval rigadue = enn(Intervallo, datadue) If rigadue = 0 Then MsgBox "la data non è presente. Scegliere un'altra data" GoTo 20 End If 'ora convertiamo i numeri di riga in riferimenti della zona da sommare, decidendo noi che 'il primo numero di riga si lega con la colonna B ed il secondo con la colonna D. E' qui 'che possiamo decidere su quali colonne (o quale) intervenire: rifuno = "B" & rigauno rifdue = "D" & rigadue 'sotto assegniamo alla cella F4 la funzione somma (è una cella come un'altra) Range("F4").Formula = "=Sum(" & rifuno & ":" & rifdue & ")" 'Questa sotto invece è una variante su come usare la funzione somma, al posto di quella sopra, usando il "WorksheetFunction" spiegato nel paragrafo precedente 'Dim miorang As Range 'questa riga può essere omessa 'Set miorang = Worksheets("Foglio1").Range(rifuno & ":" & rifdue) 'Range("F4") = WorksheetFunction.Sum(miorang) End Sub L'esempio seguente invece è modificato con l'aggiunta delle richieste (InputBox) di quali Colonne (prodotti) intendiamo avere i totali. In questo modo ampliamo la gestibilità della procedura, disponendo anche di una scelta completa, su richiesta, della zona su cui vogliamo agire. Sarà sufficiente alla prima richiesta, di inserire la lettera che identifica la colonna e fare altrettanto con la seconda richiesta. Se vorremo, ad esempio, solo il totale tra due date, ma solo della colonna B, scriveremo B sia nella prima che nella seconda richiesta. Sub miaricerca() Dim datauno As Date Dim datadue As Date Dim mioval, titolo, messaggio
Set Intervallo = Range("A4:A20") 10: titolo = "Inserisci una data" messaggio = "Scrivi la PRIMA data del periodo" http://ennius.interfree.it/
Pagina 62
MANUALE VBA
X EXCEL
mioval = InputBox(messaggio, titolo) If mioval = "" Then Exit Sub datauno = mioval rigauno = enn(Intervallo, datauno) If rigauno = 0 Then MsgBox "la data non è presente. Scegliere un'altra data" GoTo 10 End If Range("G1").Value = rigauno
20: titolo = "Inserisci una data" messaggio = "Scrivi la SECONDA data del periodo" mioval = InputBox(messaggio, titolo) If mioval = "" Then Exit Sub datadue = mioval rigadue = enn(Intervallo, datadue) If rigadue = 0 Then MsgBox "la data non è presente. Scegliere un'altra data" GoTo 20 End If Range("G2").Value = rigadue
'ora inseriamo due InputBox per le richieste delle o della 'colonna di cui vorremo i totali. titolo = "Specifica la Colonna" messaggio = "Scrivi la Colonna del prodotto di cui vuoi il totale" mioval = InputBox(messaggio, titolo) If mioval = "" Then Exit Sub coluno = mioval indicata
'coluno è uguale alla prima lettera della colonna
titolo = "Specifica la seconda Colonna" messaggio = "Scrivi la Colonna del prodotto di cui vuoi il totale" mioval = InputBox(messaggio, titolo) If mioval = "" Then Exit Sub coldue = mioval indicata
'coldue è uguale alla seconda lettera della
colonna
'ora convertiamo i numeri di riga 'in riferimenti della zona da sommare rifuno = coluno & rigauno riga http://ennius.interfree.it/
'uniamo la prima colonna e il primo n° di
Pagina 63
MANUALE VBA
X EXCEL
rifdue = coldue & rigadue di riga
'uniamo la seconda colonna e il secondo n°
Range("F4").Formula = "=Sum(" & rifuno & ":" & rifdue & ")" 'quindi avvisiamo anche con un messaggio 'il totale così ottenuto MsgBox "Il totale richiesto è " & Range("F4").Value & "" End Sub
http://ennius.interfree.it/
Pagina 64
MANUALE VBA
X EXCEL
Ricerca di tre valori Ancora un esempio scaturito da una richiesta: controllare se esistono tre valori su una stessa riga di una tabella, e se trovati selezionare (restituire) un'altro valore. La richiesta: "Se io però volessi trovare in una tabella una combinazione di numeri, esempio:12-54-33 in questa sequenza, cioè in maniera tale che il 12 sia nella colonna B, il 54 nella colonna C e il 33 nella colonna D, contemporaneamente (non uno alla volta) in maniera tale da evidenziare poi a parte in quale giorno si è verificata (o non si è verificata.....non necessariamente la combinazione deve essere presente) come posso fare?" Vorrei provare a ragionare con la testa del pellegrino neofita, che pur trovando sul sito molti esempi di come trovare un dato, si trova a chiedersi: "Ok, come si trova un dato l'ho capito, ma se i dati da trovare sono due, o tre, come faccio?" Allora vediamo: intanto devo trovare in quale modo poter dire al codice quali sono i numeri che voglio trovare. Poi devo trovare cosa dire al codice perchè mi evidenzi la cella con la data se mi trova i tre numeri. mi rispondo al primo quesito: posso usare tre celle del foglio di lavoro, dove scrivere i numeri, e poi usare i riferimenti a queste celle da usare come vettori per eseguire la ricerca; oppure, forse è meglio, usare tre InputBox, ognuna delle quali memorizzerà con l'assegnazione ad una variabile (vettore) il valore che immetterò nella InputBox. Mi sembra l'idea giusta. Ricapitoliamo: come trovare un dato l'ho capito: devo identificare un range di celle nella colonna che uso per la ricerca, applico un ciclo For Each...Next per controllare tutte le celle del range, i tre valori a questo punto me li ritrovo con le InputBox, ma non ho ancora presente come far leggere i valori nelle celle accanto ad ogni cella controllata dal ciclo. Aspetta un pò, da qualche parte ho letto che con Offset posso "mirare" ad altre celle. Ottimo, ora so cosa scrivere per controllare anche le altre celle, ma come le lego insieme le istruzioni? Vediamo un pò: io nel linguaggio parlato dico: vorrei che se il valore nella cella X e il valore nella cella Y e il valore nella cella Z; uso quindi l'operatore "e" per unire insieme le condizioni....vuoi vedere che anche il codice accetta questo sistema? (Solo che il codice ragiona in inglese e il concatenatore "E" si scrive "And"). A questo punto mi rispondo al secondo quesito, usando ancora Offset, per identificare la cella con la data. Mettiamoci al lavoro................ Il pellegrino ennius a questo punto presenta la soluzione. Vediamo una tabella d'esempio:
Nella colonna A le celle con le date, che vorremo evidenziate se la ricerca di tre valori nelle colonne BC-D saranno uguali ai valori immessi nelle inputbox. Per variare l'esempio, cercheremo due numeri ed un testo, così capiamo come diversificare l'assegnazione del "tipo" di dati assegnato ad ogni variabile. Sub cerca() 'con CL dimensioniamo una variabile di tipo Object (CL identificherà gli oggetti (celle) 'presenti nel Range assegnato) Dim CL As Object 'assegnazione alle variabili "uno" "due" e "tre" che diventano vettori dei valori che introdurremo 'nelle rispettive InputBox uno = InputBox("inserisci il primo numero") due = InputBox("inserisci il secondo numero") tre = InputBox("inserisci il terzo numero") http://ennius.interfree.it/
Pagina 65
MANUALE VBA
X EXCEL
'si inizia il ciclo For Each, che vuol dire: per ogni CL (cella nel Range B1:B100) For Each CL In Range("B1:B100") 'esegui questo controllo: SE il valore della cella in quel momento identificata dal ciclo è uguale 'al vettore "uno" E la cella immediatamente a destra (Offset(0, 1)) è uguale al vettore "due" E la 'cella, due celle a destra (Offset(0, 2)) è uguale al vettore "tre", Allora (Then)...Da notare che per i vettori "uno e "due", trattandosi di numeri, è necessario dichiarare il tipo di dati usando 'Val, mentre per il terzo vettore che sarà testo, dichiariamo CStr (stringa). La riga sotto è tutta 'una riga, anche se per necessità di spazio la vedete su due righe. If CL.Value = Val(uno) And CL.Offset(0, 1).Value = Val(due) And CL.Offset(0, 2).Value = CStr(tre) Then 'con l'istruzione sotto, se vengono riscontrati i tre valori uguali ai vettori, si seleziona con Offset 'la cella, stessa riga, immediatamente a sinistra della cella (CL). CL.Offset(0, -1).Select End If Next End Sub
http://ennius.interfree.it/
Pagina 66
MANUALE VBA
X EXCEL
Trovare un dato (vettore) in una tabella, con restituzione di un dato correlato. La ricerca di dati è uno degli "sport" preferiti dagli Excelnauti. La varietà delle necessità delle ricerche è tale, che è impensabile esemplificarli tutti. Stasera una richiesta mi fornisce lo spunto per esaminare un metodo di ricerca che può essere semplice da capire e al tempo stesso adattabile, con poche modifiche, in modo che i concetti usati siano sfruttabili da chiunque. Se vogliamo, l'esempio che faremo, richiama a grandi linee il comportamento delle funzioni CERCA.VERT e CERCA.ORIZ unite insieme: ognuna di queste funzioni, ricerca un dato (vettore) in un elenco di dati su più colonne o righe, restituendo il valore che si troverà sulla stessa riga, o colonna, del vettore trovato, ma in una colonna o riga scelta in precedenza. L'esempio: una tabella di dati su un foglio (Foglio1), formata da più colonne; nella colonna A della tabella una serie di valori. un'altra tabella su un'altro foglio (Foglio2), formata anch'essa da più colonne e righe: da B1 a Z100, in ognuna di queste celle si trovano dei valori, e in una sola delle celle di questa zona si può trovare uno qualsiasi dei valori presenti nella colonna A del foglio1. Nella colonna A ma sempre del Foglio2 (quindi da A1 a A100) si trovano dei valori correlati. esercizio : vogliamo che selezionando una cella contenente un valore nella colonna A del foglio1, venga trovato lo stesso valore nella tabella sul foglio2, nella zona B1:Z100, ma non vogliamo il valore trovato, bensì il valore presente nella stessa riga ma nella colonna A. I valori presenti nel Range(B1:Z100) sono univoci, ma non sappiamo dove sono. La procedura è semplice: selezioniamo sul foglio1 la cella con il valore da cercare, attiviamo la macro che avremo associato ad un pulsante, indichiamo con una variabile il valore della cella da cercare (X), iniziamo un ciclo di ricerca di X nella zona Foglio2 , Range B1:Z100, trovato il valore, selezioniamo la cella, ci spostiamo a inizio riga, selezioniamo quindi la cella stessa riga ma della colonna A, e restituiamo il valore della cella ora attiva, con una MessageBox. Ovviamente potremmo restituire detto valore in qualunque cella lo volessimo, con un semplice: Range("tuacella").Value = ActiveCell.Value Questa la procedura: Sub cercalapippo() 'evitiamo i saltellamenti a schermo Application.ScreenUpdating = False 'siamo sul foglio1, abbiamo selezionatoo una cella e ne assegnamo il valore ad una 'variabile, la X X = Selection.Value 'ci spostiamo sul foglio2 dove esiste la zona in cui eseguire la ricerca Range("B1:Z10") Worksheets(2).Select 'dichiariamo una variabile di tipo Object Dim CL As Object 'inizia il ciclo di ricerca: per ogni cella (CL) nel range previsto For Each CL In Worksheets(2).Range("B1:Z100") 'se il valore della cella (CL) è uguale al valore della X If CL.Value = X Then 'allora selezioniamo la cella (CL) CL.Select 'ora dalla cella selezionata, ci spostiamo a inizio riga e selezioniamo la cella inizio riga http://ennius.interfree.it/
Pagina 67
MANUALE VBA
X EXCEL
With ActiveCell Cells(ActiveCell.Row, 1).Select End With 'rendiamo il valore della cella attiva nella colonna A con un messaggio MsgBox "Il valore è " & ActiveCell & "" End If Next 'ritorniamo sul foglio1 Worksheets(1).Select End Sub E' evidente che potrete avere in "restituzione" qualunque valore di qualsiasi colonna. Uno dei problemi delle ricerche è che spesso non sappiamo dove si troverà il valore che cerchiamo, e risulta problematico quindi fornire richieste di dati correlati, visto che Excel lo può fare solo se gli si danno delle coordinate precise. Impossibile quindi impostare a priori istruzioni che dicano "restituisci il valore che si trova due colonne a destra o 3 colonne a sinistra", visto che non sapremo quale sarà la colonna in cui esisterà il valore cercato. L'istruzione Cells(ActiveCell.Row, 1).Select invece ci permette di determinare con esattezza di quale colonna, stessa riga, vorremo il valore restituito, modificando il numero presente nell'istruzione; se avessimo voluto in valore che si trovava nella colonna C avremmo dovuto scrivere: Cells(ActiveCell.Row, 3).Select Se il valore cercato (X) non è presente nella zona ricerca (Range("B1:Z100")), la routine non porterà risultato e quindi nessun messaggio. Se invece saranno presenti più valori X, verrà riportato il valore associato alla prima riga dove sarà presente il primo valore X, poi il successivo, fino all'ultimo dove il ciclo però si fermerà, e solo l'ultimo sarà visualizzato, o memorizzato nel caso si usi una cella per la destinazione del valore correlato, a meno che non si usi un'istruzione che a partire dalla cella di destinazione prevista, non cerchi una cella in una riga libera; in questo caso saranno registrati tanti valori X correlati quanti saranno i valori X trovati.
http://ennius.interfree.it/
Pagina 68
MANUALE VBA
X EXCEL
Cercare File(s) sull'Hard-Disk. Un esercizio utile a chi voglia ricercare se esiste un file, oppure di avere l'elenco di tutti i file con una determinata estensione presenti in una cartella. Sfrutteremo il metodo Execute applicato all'oggetto FileSearch. Con Execute possiamo usare due costanti che ci forniranno l'elenco dei file trovati ordinati per nome e in ordine crescente (da A a Z). ( Execute inizia la ricerca per il file o i file specificati. Restituisce un oggetto Long; zero (0) se non viene trovato alcun file o un numero positivo se vengono trovati uno o più file). Le istruzioni si basano sul settaggio di una variabile assegnata ad Application.FileSearch, che saranno sempre le stesse, a cui seguiranno istruzioni su dove cercare il file e il nome o l'estensione da trovare; queste ultime le renderemo variabili per poter eseguire tipi diversi di ricerca.. Ma partiamo col primo esempio, la ricerca di un file in una cartella. Di questo esempio, con i parametri di ricerca fissi e impostati nelle istruzioni, non vedo una pratica utilità, serve solo a avvicinarsi ai concetti di queste ricerche; in verde i commenti: Sub cercaunfile() Set fs = Application.FileSearch 'assegnazione alla variabile "fs" del metodo di ricerca With fs
'con la variabile "fs"
.LookIn = "C:\Documenti" .Filename = "Pippo.doc"
'cerca nella cartella documenti ' il file col nome di Pippo.doc
If .Execute() > 0 Then 'se eseguendo la ricerca trovi il file (execute sarà 1, quindi 'maggiore di zero), avvisi col il seguente messaggio: MsgBox "Il File è presente." Else 'altrimenti avvisi con questo messaggio: MsgBox "File non trovato." End If
'fine condizione
End With
'fine con "fs"
Set fs = Nothing
'cancelliamo dalla memoria "fs"
End Sub Nel secondo esempio invece esaminiamo le procedure per la ricerca di file impostando non più i parametri nel codice, ma usando tre celle del foglio di lavoro da usare come contenitori (vettori) che assegneremo a tre variabili, e saranno le tre variabili a prendere il posto del nome della cartella entro cui cercare nome del file (o dei files) da cercare estensione dei file da cercare In questo modo potremo cercare in qualunque cartella, un file specifico o, impostando un asterisco ( carattere jolly * al posto del nome file, così verranno cercati TUTTI i file), variare il tipo di estensione dei file, per ottenere di volta in volta tutti i tipi di documento Word (doc), o di file di Excel (xls), o di immagini (bmp, jpg, ecc.) ecc. ecc. Nell'esempio proposto uso le celle A1, B1 e C1 (ma potranno essere quelle che vorrete) per scrivere rispettivamente: il nome cartella, il nome del file, l'estensione da cercare. Esempio delle tre celle con, rispettvamente: nome della cartella - asterisco - estensione :
1
A Documenti
B *
C txt
Altro accorgimento: inserendo nelle istruzioni la proprietà SearchSubFolders impostata a True , sarà possibile eseguire la ricerca in tutte le cartelle del nostro Hard-Disk, SENZA bisogno di indicare il nome di una cartella, in questo modo verranno trovati TUTTI i file con l'estensione richiesta presenti in tutte le cartelle, Esempio in cui la cella A1 viene lasciata vuota: http://ennius.interfree.it/
Pagina 69
MANUALE VBA
X EXCEL A
1
B *
C txt
Una cosa da considerare è che non sapremo il numero di quanti files saranno presenti in una determinata cartella, e visto che una volta lanciata la routine, il metodo Execute restituisce un SOLO messaggio col totale dei files trovati e a seguire, MA SENZA INTERRUZIONE, un messaggio col nome ed il percorso dei ogni file trovato, se vogliamo interrompere il ciclo anzitempo, è doveroso prevedere una domanda condizionale se vorremo uscire (dal ciclo) oppure no. Vediamo le istruzioni, in verde i commenti: Sub cercafiledue() 'sotto assegnazione a tre variabili del contenuto delle tre celle usate: cartella = Range("A1").Value nome = Range("B1").Value este = Range("C1").Value
Set fs = Application.FileSearch With fs 'il metodo NewSearch viene utilizzato per ripristinare i criteri di ricerca predefiniti prima 'di iniziare una nuova ricerca. .NewSearch 'sotto cercheremo sul C:\ il nome della cartella digitato in A1 .LookIn = "C:\" & cartella & "" 'sotto: istruzione per cercare in tutte le cartelle e sottocartelle se verrà lasciata vuota la variabile "cartella" (cioè la cella A1) .SearchSubFolders = True 'sotto usiamo i valori scritti in B1 e C1 per cercare nome file o tutti i file (*) con 'l'estensione voluta .Filename = "" & nome & "." & este & "" 'sotto: utilizzo delle costanti di execute per ottenere la ricerca in ordine alfabetico If .Execute(SortBy:=msoSortByFileName,
_
SortOrder:=msoSortOrderAscending) > 0 Then 'primo messaggio che avvisa quanti files sono stati trovati (con .FoundFiles.Count) MsgBox "Ci sono " & .FoundFiles.Count & file(s) trovati." 'inizio del ciclo For Next per restituire per ogni file trovato, il nome e il suo percorso; 'assegnazione alla variabile I di detto nome, che cambierà ad ogni ciclo fino alla fine dei 'files trovati, e restituiti uno dopo l'altro con un messaggio For I = 1 To .FoundFiles.Count MsgBox .FoundFiles(I) 'sotto: inserimento in questo punto, cioè dopo che sarà riportato il nome di un file con la 'msgbox sopra, che inseriamo la domanda se vorremo uscire dimmi = MsgBox("Vuoi uscire dalla ricerca ?", vbYesNo) http://ennius.interfree.it/
Pagina 70
MANUALE VBA
X EXCEL
'se la risposta sarà SI allora: If dimmi = vbYes Then 'cancelliamo fs dalla memoria Set fs = Nothing 'usciamo dal ciclo e dalla routine Exit For End If Next I 'sotto: altrimenti (else) se nessun nome o estensione richiesta verrà trovato, si avvisa con il 'messaggio: Else MsgBox "File(s) non trovato." End If End With Set fs = Nothing End Sub Alcuni accorgimenti: la routine sopra l'abbiamo impostata con, tra i criteri di ricerca, la possibilità di scegliere noi l'estensione da cercare. Bene, esiste una variante usando la proprietà FileType con la costante msoFileTypeOfficeFiles , di ottenere tutti i file caratterizzati dalle seguenti estensioni: *.doc, *.xls, *.ppt, *.pps, *.obd, *.mdb, *.mpd, *.dot, *.xlt, *.pot, *.obt, *.htm o *.html. - In questo caso non useremo nelle istruzioni la proprietà FileName, sostituendo la riga dell'istruzione .Filename = "" & nome & "." & este & "" con questa: .FileType = msoFileTypeOfficeFiles
http://ennius.interfree.it/
Pagina 71
MANUALE VBA
X EXCEL
Controllo comunicazioni MSCOMM32.OCX per comunicare col Modem e/o chiamare numeri telefonici. Premetto che non ho nè provato, nè intendo farlo, ad usare le istruzioni che seguono per eseguire chiamate a numeri telefonici, ma spinto da alcune richieste ricevute, provo a segnalare ai "volenterosi" queste istruzioni : se ne avranno voglia, si cimenteranno nel seguirle. Fornisco comunque alcune considerazioni di massima: per telefonare dal computer è necessario un Modem ed una porta di connessione al modem: la COM1 o la COM2. Inoltre il Modem deve essere munito dei ModemAudioDevice, driver che in genere vengono installati dal Sistema Operativo al momento del riconoscimento del Modem stesso, insieme ai driver del modem. Perchè Excel possa utilizzare il "controllo" per comunicare, dovrà essere inserito nella UserForm. Trattandosi di un "controllo" OCX, dovremo, nell'editor di visual basic, scegliere dal menù "Strumenti" e poi "Riferimenti" - In questa finestra dovremo premere sul pulsante "Sfoglia", si aprirà la finestra "Aggiungi riferimento": qui dovremo selezionare in "Tipo di file", il tipo ocx (Controlli ActiveX *.ocx). A questo punto nella zona centrale cercheremo e selezioneremo MSCOMM32.OCX, che è il controllo che serve. Daremo OK, OK, e troveremo il nostro controllo "caricato" nella "casella degli strumenti" della UserForm.
A questo punto selezioneremo il "controllo" nella casella degli strumenti e spostandoci sulla UserForm, trasciniamo fino ad ottenere l'icona del controllo inserito (in esecuzione il controllo non è visibile). Questa l'immagine che vedremo :
e questa l'immagine della finestra delle "proprietà" del controllo:
http://ennius.interfree.it/
Pagina 72
MANUALE VBA
X EXCEL
Passiamo alle spiegazioni trovate su un libro: Controllo comunicazioni MSCOMM32.OCX Il controllo comunicazioni consente di aggiungere nell'applicazione funzioni semplici per comunicazioni attraverso porte seriali nonché funzioni avanzate per la creazione di uno strumento di comunicazione completo basato su eventi. Il controllo comunicazioni MSCOMM32.OCX Il controllo comunicazioni fornisce un'interfaccia a un gruppo di comandi di comunicazione standard e consente di stabilire una connessione a una porta seriale, di connettersi a un'altra periferica di comunicazione, ad esempio un modem, di inviare comandi, di scambiare dati nonché di eseguire il monitoraggio e rispondere ai vari eventi ed errori generati durante una connessione seriale. Esempi di utilizzo • Composizione di numeri telefonici • Monitoraggio di una porta seriale per il controllo di dati in entrata • Creazione di programmi per terminale completi • Nozioni fondamentali sulle comunicazioni seriali In tutti i computer sono disponibili una o più porte seriali COM1, COM2 e così via. In un PC standard il mouse è in genere collegato alla porta COM1, un modem alla porta COM2, uno scanner alla porta COM3 e così via. Le porte seriali rappresentano il canale di trasmissione dei dati inviati attraverso queste periferiche seriali esterne. La funzione fondamentale della porta seriale consiste nell'agire da interprete tra la CPU e la periferica seriale. Durante la trasmissione di dati dalla CPU alla porta seriale, i valori Byte vengono convertiti in bit seriali, che vengono quindi riconvertiti in valori Byte durante la ricezione dei dati. Per completare la trasmissione dei dati, è tuttavia necessario un ulteriore livello di interpretazione. Nel sistema operativo di Windows viene utilizzato il driver di comunicazione Comm.drv per inviare e ricevere dati utilizzando le funzioni standard dell'API di Windows. Il produttore della periferica seriale fornisce un driver specifico per la connessione dell'hardware a Windows. Quando si utilizza il controllo comunicazioni, vengono eseguite funzioni dell'API che vengono quindi interpretate dal driver Comm.drv e passate al driver della periferica. Un programmatore deve occuparsi esclusivamente dell'interazione di Windows. Un programmatore di Visual Basic deve invece occuparsi dell'interfaccia fornita dal controllo comunicazioni alle funzioni dell'API del driver di comunicazione di Windows, ovvero deve impostare ed eseguire il monitoraggio delle proprietà e degli eventi del controllo comunicazioni. Connessione seriale Per utilizzare il controllo comunicazioni è innanzitutto necessario stabilire la connessione alla porta seriale. Nella tabella seguente sono elencate le proprietà che consentono di stabilire la connessione seriale. http://ennius.interfree.it/
Pagina 73
MANUALE VBA
X EXCEL
Proprietà
Descrizione
CommPort
Imposta e restituisce il numero della porta di comunicazione.
Settings
Imposta e restituisce in forma di stringa i valori di velocità in baud, parità, bit di dati e bit di stop.
PortOpen
Imposta e restituisce lo stato di una porta di comunicazioni, oltre ad aprire e a chiudere la porta.
Apertura di porte seriali Per aprire una porta seriale, è necessario impostare le proprietà CommPort, PortOpen e Settings. Ad esempio: ' Apre la porta seriale MSComm1.CommPort = 2 MSComm1.Settings = "9600,N,8,1" MSComm1.PortOpen = True La proprietà CommPort consente di impostare la porta seriale da aprire. Se un modem è collegato alla porta COM2, l'esempio precedente imposta il valore su 2 (COM2) ed esegue la connessione al modem. È possibile impostare il valore della proprietà CommPort su un numero compreso tra 1 e 16 (il valore predefinito è 1). Se tuttavia si imposta questo valore su una porta COM non disponibile per il sistema in cui l'applicazione viene eseguita, verrà generato un errore. La proprietà Settings consente di specificare la velocità in baud, la parità e il numero di bit di dati e bit di stop. Per impostazione predefinita, la velocità in baud è impostata su 9600. L'impostazione della parità è relativa alla convalida dei dati. In genere non viene utilizzata ed è pertanto impostata su "N". L'impostazione dei bit di dati specifica il numero di bit che rappresenta un blocco di dati. Il bit di stop indica quando un blocco di dati è stato ricevuto. Dopo aver specificato la porta che si desidera aprire e la modalità di gestione della comunicazione di dati, è necessario stabilire la connessione impostando la proprietà PortOpen. Si tratta di un valore booleano, ovvero True o False. Se tuttavia la porta non è attiva, la proprietà CommPort è impostata in modo non corretto oppure la periferica non supporta le impostazioni specificate, verrà generato un errore oppure la periferica esterna non funzionerà correttamente. Quando la proprietà PortOpen viene impostata su False la porta viene chiusa. Utilizzo di un modem Nella maggior parte dei casi il controllo comunicazioni viene utilizzato per programmare l'applicazione in modo che possa essere eseguita insieme a un modem. Il controllo consente di utilizzare il gruppo di comandi Hayes compatibili standard per comporre un numero telefonico o per connettersi e interagire con un altro modem. Dopo aver stabilito la connessione con la porta seriale tramite le proprietà CommPort, Settings e PortOpen, è necessario attivare il modem impostando la proprietà Output con cui è possibile eseguire i comandi per il controllo dell'interazione tra due modem. Ad esempio: ' Attiva il modem e compone un numero telefonico. MSComm1.Output = "ATDT 555-5555" & vbCr Nell'esempio precedente il comando "AT" avvia la connessione, "D" compone il numero e "T" specifica la composizione a toni, anziché quella a impulsi. Quando si invia output a un terminale, è necessario specificare un carattere di ritorno a capo (vbCr). Questa operazione non è invece necessaria per l'output di matrici di byte. Per controllare se il comando viene elaborato in modo corretto, è sufficiente verificare che venga restituito il codice "OK". http://ennius.interfree.it/
Pagina 74
MANUALE VBA
X EXCEL
Ulteriori informazioni Per un elenco completo dei comandi Hayes compatibili, vedere la documentazione del modem. Impostazione delle proprietà Receive e Transmit Buffer in fase di progettazione Quando una porta viene aperta vengono creati buffer di trasmissione e di ricezione. Per la gestione di tali buffer, sono disponibili alcune proprietà del controllo comunicazioni che è possibile impostare in fase di progettazione nella finestra Pagine proprietà del controllo. Impostazione delle proprietà dei buffer in fase di progettazione Allocazione delle memoria dei buffer Le proprietà InBufferSize e OutBufferSize consentono di specificare la quantità di memoria allocata ai buffer di ricezione e trasmissione. Per impostazione predefinita le due proprietà vengono impostate sui valori indicati nell'illustrazione. Maggiore è il valore, minore è la quantità di memoria disponibile per l'applicazione. Se le dimensioni del buffer sono troppo piccole, potrebbe verificarsi un overflow del buffer, a meno che non si utilizzi la sincronizzazione. Nota Data la quantità di memoria attualmente disponibile nella maggior parte dei PC, l'allocazione di memoria ai buffer non è un'operazione di fondamentale importanza, in quanto è disponibile un maggior numero di risorse. In altri termini, l'impostazione di valori del buffer maggiori non ha alcun effetto negativo sulle prestazioni delle applicazioni. Proprietà RThreshold e SThreshold Le proprietà RThreshold e SThreshold consentono di impostare o restituire il numero di caratteri che dovranno essere ricevuti nel buffer di ricezione e di trasmissione prima che venga generato l'evento OnComm. L'evento OnComm consente di eseguire il monitoraggio e rispondere alle modifiche dello stato della comunicazione. Con l'impostazione di entrambe le proprietà su zero (0) è possibile impedire che l'evento OnComm venga generato, mentre con l'impostazione su un valore diverso da 0, ad esempio 1, l'evento OnComm viene generato ogni volta che un carattere viene ricevuto in uno dei buffer. Ulteriori informazioni Per informazioni su queste proprietà, vedere "Evento OnComm e proprietà CommEvent" più avanti in questa sezione. Proprietà InputLen e EOFEnable Se si imposta la proprietà InputLen su 0 il controllo comunicazioni esegue la lettura dell'intero contenuto del buffer di ricezione quando si utilizza la proprietà Input. Durante la lettura di dati in un computer in cui l'output è formattato come blocchi di dati di lunghezza fissa, è possibile impostare il valore di questa proprietà in modo appropriato. La proprietà EOFEnable consente di segnalare l'individuazione di un carattere di fine del file, o EOF, durante l'ingresso dei dati. Se la proprietà è impostata su True, l'ingresso dei dati viene interrotto e viene generato l'evento OnComm per indicare che si è verificata questa condizione. Ulteriori informazioni Vedere "Gestione dei buffer di ricezione e di trasmissione" e "Evento OnComm e proprietà CommEvent" più avanti in questa sezione. Gestione dei buffer di ricezione e di trasmissione I buffer di ricezione e di trasmissione vengono creati in corrispondenza dell'apertura di una porta e utilizzati per la memorizzazione dei dati in arrivo e per la trasmissione dei dati in uscita. Il controllo comunicazioni consente di gestire questi buffer tramite alcune proprietà per l'inserimento e il recupero di dati, per la restituzione delle dimensioni di ciascun buffer e per la gestione sia di testo che di dati binari. Quando si utilizza il controllo comunicazioni, è estremamente importante gestire questi buffer in modo corretto. Buffer di ricezione La proprietà Input consente di memorizzare e recuperare dati dal buffer di ricezione. Se, ad esempio, si desidera recuperare dati dal buffer di ricezione per visualizzarli in una casella di testo, è possibile utilizzare il codice seguente: TxtDisplay.Text = MSComm1.Input Per recuperare l'intero contenuto del buffer di ricezione, è innanzitutto necessario impostare la proprietà InputLen su 0 in fase di progettazione o di esecuzione. È inoltre possibile ricevere dati in arrivo come testo o come dati binari impostando la proprietà InputMode sulle costanti Visual Basic comInputModeText o comInputModeBinary. I dati verranno in tal http://ennius.interfree.it/
Pagina 75
MANUALE VBA
X EXCEL
modo recuperati in forma di stringa o di dati binari in una matrice Byte. È necessario impostare la proprietà su comInputModeText per i dati che utilizzano il set di caratteri ANSI e la costante comInputModeBinary per tutti gli altri dati, ad esempio i dati che includono caratteri di controllo incorporati, valori Null e così via. Ciascun byte di dati ricevuto viene inserito nel buffer di ricezione e il valore della proprietà InBufferCount viene incrementata di una unità. Tale valore può quindi essere utilizzato per recuperare il numero di byte del buffer di ricezione. È inoltre possibile impostare la proprietà su 0 per svuotare il buffer di ricezione. Buffer di trasmissione La proprietà Output consente di inviare comandi e dati al buffer di trasmissione. In modo analogo alla proprietà Input, è possibile trasmettere i dati in forma di testo o di dati binari. Con la proprietà Output è tuttavia necessario trasmettere testo o dati binari specificando una matrice String o Byte. La proprietà Output consente di inviare comandi, stringhe di testo o dati di matrice Byte. Ad esempio: ' Invia un comando AT MSComm1.Output = "ATDT 555-5555"
' Invia una stringa di testo MsComm1.Output = " Questa è una stringa di testo"
' Invia dati di matrice Byte MSComm1.Output = Out Le righe di trasmissione devono terminare con un carattere di ritorno a capo (vbCr). Nell'esempio precedente Out è una variabile definita come matrice Byte, ovvero Dim Out() As Byte. Se fosse un valore Variant di tipo String, sarebbe definito come Dim Out() As String. È possibile controllare il numero di byte del buffer di trasmissione tramite la proprietà OutBufferCount e svuotare il buffer impostando questa proprietà su 0. Sincronizzazione Una parte integrante della procedura di gestione dei buffer di ricezione e di trasmissione consiste nell'assicurare che la trasmissione dei dati venga eseguita correttamente in entrambe le direzioni, ad esempio che la velocità di ricezione dei dati non superi i limiti del buffer. Con il termine sincronizzazione viene fatto riferimento al protocollo di comunicazione interno in base a cui i dati vengono trasferiti dalla porta hardware al buffer di ricezione. Quando la porta seriale riceve un carattere sotto forma di dati, la periferica di comunicazione deve trasferirlo nel buffer di ricezione in modo che possa essere letto dal programma. Un protocollo di sincronizzazione assicura che non si verifichi alcuna perdita di dati dovuta a un overrun del buffer. Questa situazione si verifica quando i dati raggiungono la porta ad una velocità troppo elevata che ne impedisce il trasferimento nel buffer di ricezione. Per specificare il protocollo di sincronizzazione da utilizzare nell'applicazione, è necessario impostare la proprietà Handshaking. Per impostazione predefinita, questo valore viene impostato su comNone, ovvero nessun protocollo. Le possibili impostazioni sono le seguenti:
http://ennius.interfree.it/
Pagina 76
MANUALE VBA
X EXCEL
Impostazione
Valore
Descrizione
comNone
0
Nessuna sincronizzazione (impostazione predefinita).
comXOnXOff
1
Sincronizzazione XOn/XOff.
comRTS
2
Sincronizzazione RTS/CTS (Request To Send/Clear To Send).
comRTSXOnXOff
3
Sia Request To Send che XON/XOFF.
La scelta del protocollo si basa sulla periferica a cui ci si connette. Con l'impostazione comRTSXOnXOff è supportato sia il protocollo Request To Send che il protocollo XON/XOFF. In molti casi, la sincronizzazione viene gestita dal protocollo stesso. Di conseguenza, l'impostazione della proprietà su un valore diverso da comNone può generare conflitti. Nota Se si imposta la proprietà su comRTS o comRTSXOnXOff, è necessario impostare la proprietà RTSEnabled su True. In caso contrario, sarà possibile connettersi e inviare dati, ma non ricevere dati.. Evento OnComm e proprietà CommEvent A seconda dell'area di validità e della funzionalità dell'applicazione, potrebbe essere necessario controllare e rispondere a un certo numero di eventi o errori generati durante la connessione a un'altra periferica oppure durante la ricezione o la trasmissione dei dati. L'evento OnComm e la proprietà CommEvent consentono di intercettare e verificare il valore degli eventi e degli errori di comunicazione. In corrispondenza di un evento o di un errore di comunicazione, viene generato l'evento OnComm e il valore della proprietà CommEvent viene modificato. Se necessario, è pertanto possibile verificare il valore della proprietà CommEvent ogni volta che l'evento OnComm viene generato. Dato che la qualità della comunicazione è imprevedibile, soprattutto nel caso di comunicazioni telefoniche, l'intercettazione di questi eventi ed errori consente di fornirvi una risposta adeguata. Nella tabella seguente sono elencati gli eventi di comunicazione che generano l'evento OnComm. I valori vengono quindi scritti nella proprietà CommEvent. Costante
Valore
Descrizione
comEvSend
1
Il numero di caratteri del buffer di trasmissione è inferiore al valore di SThreshold.
comEvReceive
2
Numero di caratteri RThreshold ricevuti. Questo evento viene generato continuamente fino a quando i dati non vengono rimossi dal buffer di ricezione tramite l'impostazione della proprietà Input.
comEvCTS
3
Modifica della linea CTS (Clear To Send).
comEvDSR
4
Modifica della linea DSR (Data Set Ready). Questo evento viene generato solo con la modifica di DSR da 1 a 0.
comEvCD
5
Modifica della linea CD (Carrier Detect).
comEvRing
6
Individuato squillo. Alcuni trasmettitori-ricevitori asincroni universali o UART (Universal Asynchronous ReceiverTransmitter) potrebbero non supportare questo evento.
comEvEOF
7
Ricezione di un carattere di fine del file, o EOF (carattere ASCII 26).
http://ennius.interfree.it/
Pagina 77
MANUALE VBA
X EXCEL
L'evento OnComm viene inoltre generato quando vengono individuati gli errori indicati di seguito, con la conseguente scrittura di un valore nella proprietà CommEvent. Impostazione
Valore
Descrizione
comEventBreak
1001
Ricezione di un segnale di interruzione.
comEventFrame
1004
Errore di frame. L'hardware ha individuato un errore di frame.
comEventOverrun
1006
Overrun della porta. L'hardware non ha letto un carattere prima dell'arrivo del successivo e il carattere è andato perduto.
comEventRxOver
1008
Overflow del buffer di ricezione. Spazio esaurito nel buffer di ricezione.
comEventRxParity
1009
Errore di parità. È stato rilevato un errore di parità.
comEventTxFull
1010
Buffer di trasmissione pieno. Spazio esaurito nel buffer di trasmissione durante il tentativo di inserimento di un carattere.
comEventDCB
1011
Errore imprevisto durante il recupero di DCB (Device Control Block) per la porta.
http://ennius.interfree.it/
Pagina 78
MANUALE VBA
X EXCEL
Chiamare usando il telefono da Excel (con modem). ovvero: utilizzare il programma Dialer.exe Forse non molti sanno che i Sistemi Operativi a partire dalla versione Windows 95, possiedono un'utility che consente di telefonare usando il computer per la composizione dei numeri di telefono, e di effettuare la chiamata utilizzando un modem voice. Questo piccolo programma si chiama : Dialer.exe A secondo il S.O. installato, cambia la versione di questo eseguibile, che comunque si può richiamare dal menù Start/Esegui (sul Desktop), e nella finestrina digitare: dialer Le opzioni del programma dialer.exe differiscono a secondo la versione, comunque hanno una finestra per l'inserimento del numero telefonico da chiamare, una rubrica in cui memorizzare i numeri di più frequente consultazione, o la possibilità di comporre il numero attraverso un tastierino numerico, oltre alle opzioni per selezionare la periferica adatta alla connessione. Ma vediamo come "richiamare" questo dialer da Excel. Useremo quindi il sistema già spiegato su questa sezione, paragrafo "Apertura applicativi". Creeremo quindi la nostra macro che richiameremo con un pulsante posto sul foglio di lavoro. Poichè cambia il percorso dove si trova il file, in dipendenza del sistema operativo installato, vi presento le relative istruzioni: su Windows XP - dialer.exe si trova in C:\Programmi\Windows NT\Dialer.exe e quindi questa l'istruzione: Sub fonosxp() Dim x x = Shell("C:\Programmi\Windows NT\dialer.exe", 1) End Sub Su Windows98SE - dialer.exe si trova nella cartella di sistema C:\Windows\Dialer.exe e quindi questa l'istruzione: Sub fonosw98() Dim x x = Shell("C:\Windows\dialer.exe", 1) End Sub Non ho presenti gli altri sistemi operativi (W95 - W98 - ME) ma ritengo che il percorso di dialer.exe sia lo stesso di W98SE. Queste istruzioni comunque attivano il programma Dialer, e sarà sufficiente a questo punto, lavorare direttamente sul programma così aperto. Note sui collegamenti: Perchè sia possibile usare il Dialer.exe per effettuare chiamate telefoniche, è necessario disporre di un modem con gli innesti PHONE e LINE, come nello schema che vediamo sotto:
Nell'innesto LINE inseriremo il cavetto che ci collegherà alla linea telefonica, mentre in PHONE collegheremo il cavetto del telefono (quello che normalmente và direttamente alla linea telefonica). In questo modo la doppia presa funziona come bypass: se il modem non sarà in funzione, potremo usare il telefono normalmente, sia per chiamare che per ricevere. Quando dal Dialer useremo dal http://ennius.interfree.it/
Pagina 79
MANUALE VBA
X EXCEL
menù Telefono/Componi, apparirà una finestra come quella sotto, dove dovremo selezionare "Chiamata telefonica" da "Modalità chiamata", indi premere il pulsante "Effettua chiamata". Apparirà sul video, in alto a sinistra, la finestra di "Chiamata in corso" ed una finestrina con un pulsante "Alza il Ricevitore". Quando appare la scritta "Connessione effettuata", immediatamente potremo premere "Alza il Ricevitore" e contemporaneamente alzare la cornetta del telefono e iniziare a parlare. La chiamata dal computer via telefono sarà in corso..
http://ennius.interfree.it/
Pagina 80
MANUALE VBA
X EXCEL
Chiamare usando il telefono da Excel (con modem), ma composizione automatica di un numero di telefono presente in un elenco sul foglio di lavoro. Presento delle istruzioni che ho trovato e rese freeware (riporto il testo originale in inglese), adattate e modificate da me per l'utilizzo in una cartella di Excel. Il testo originale: 'AutoDial - Telephone dialer demo program 'Copyright (c) 1996-97 SoftCircuits 'Redistributed by Permission. 'This Visual Basic 5.0 example program demonstrates how an application 'can dial a telephone number under Windows 95 using Assisted Telephony 'which is a subset of TAPI. This code is simple because it relies on a 'call manager applet to perform the actual dialing. 'This program may be distributed on the condition that it is 'distributed in full and unchanged, and that no fee is charged for 'such distribution with the exception of reasonable shipping and media 'charged. In addition, the code in this program may be incorporated 'into your own programs and the resulting programs may be distributed 'without payment of royalties. 'This example program was provided by: ' SoftCircuits Programming ' http://www.softcircuits.com ' P.O.Box 16262 ' Irvine, CA 92623 ' Downloaded from http://surf.to/VbArea Il progetto su citato si basa sull'uso delle librerie TAPI32.DLL, che sono le stesse utilizzate dal Dialer.exe citato nel paragrafo precedente. Ma vediamo come gestire le istruzioni. Visto che avremo necessità di disporre di un elenco telefonico dal quale attingere i numeri da chiamare, bisognerà creare una "Rubrica" con nominativo e relativo numero telefonico, posta su un foglio di lavoro. Poi useremo una UserForm con una casella combinata che "peschi" i dati, su due colonne, presenti nella "rubrica". Con la selezione di un nominativo, otterremo che in una textbox compaia il numero correlato, e con un pulsante attiveremo la routine che chiamerà il dialer usando come numero da comporre il numero presente nella textbox. Un semplice controllo che la textbox non sia vuota, impedirà l'esecuzione della routine. Istruzioni da inserire nella sezione Generale - Dichiarazioni della UserForm Option Explicit
Private Declare Function tapiRequestMakeCall& Lib "TAPI32.DLL" (ByVal DestAddress$, ByVal AppName$, ByVal CalledParty$, ByVal Comment$) 'Questa è tutta una riga Private Const TAPIERR_NOREQUESTRECIPIENT = -2& Private Const TAPIERR_REQUESTQUEUEFULL = -3& Private Const TAPIERR_INVALDESTADDRESS = -4& http://ennius.interfree.it/
Pagina 81
MANUALE VBA
X EXCEL
Istruzioni da inserire nell'apertura della UserForm per assegnare alla ComboBox1 il range di celle che formerà il RowSource (elenco nomi e numeri che nell'esempio pongo in A1:B10) Private Sub UserForm_Activate() ComboBox1.RowSource = "A1:B10" End Sub Impostazione delle "proprietà" della ComboBox perchè ci mostri due colonne: colonna A con i nomi e colonna B con i numeri. Le proprietà interessate sono: ColumnCount - che andrà impostata a 2 (imposta a due le colonne visualizzate nella combobox) BoundColumn - che andrà impostata a 2 (Identifica la fonte dati di un controllo ComboBox a colonne multiple. Il valore indicato (in questo caso 2) assegna al controllo il valore della colonna specificata. Le colonne vengono numerate a partire da 1). e questo è l'aspetto dell'elenco sul foglio e della ComboBox a due colonne in cui si notano i nomi e relativi numeri:
Poichè avremo bisogno che selezionando un nominativo nella ComboBox, nella TextBox1 compaia il numero associato e presente nella seconda colonna della stessa combo, useremo l'evento Change della ComboBox indicando però quale colonna ci interessa (BoundColumn) ma sfruttando l'argomento Value anzichè Text (con Text verrebbe restituito il nome e non il numero di colonna. Con Value si identifica il numero di colonna assegnato a BoundColumn, indipendentemente dal fatto che nella colonna siano presenti numeri o testo ) Private Sub ComboBox1_Change() ComboBox1.BoundColumn = 2 TextBox1 = ComboBox1.Value TextBox1.SetFocus End Sub e questo è il risultato:
http://ennius.interfree.it/
Pagina 82
MANUALE VBA
X EXCEL
E queste sono le istruzioni collegate al pulsante "Componi..." , chiamato cmdDial: Private Sub cmdDial_Click() 'controllo se la textbox1 è vuota, allora avviso ed esco dalla routine If TextBox1 = "" Then MsgBox "Seleziona il Nominativo da chiamare o scrivi un numero di telefono" Exit Sub End If 'inizio delle istruzioni originali da me riportate Dim buff As String Dim nResult As Long
'Invoke tapiRequestMakeCall. If tapiRequestMakeCall returns 0, the 'request has been accepted. It is up to the call manager application 'to do any further work. The second-to-last argument should be 'changed to be the name of the person you are dialing. 'riga sotto: modifica con TextBox1(casella dove si trova il numero da chiamare) assegnata a nResult nResult = tapiRequestMakeCall&(Trim$(TextBox1), CStr(Caption), "Test Dial", "") 'Display message if error If nResult 0 Then buff = "Error dialing number : " Select Case nResult Case TAPIERR_NOREQUESTRECIPIENT buff = buff & "No Windows Telephony dialing application is running and none could be started." Case TAPIERR_REQUESTQUEUEFULL buff = buff & "The queue of pending Windows Telephony dialing requests is full." Case TAPIERR_INVALDESTADDRESS buff = buff & "The phone number is not valid." http://ennius.interfree.it/
Pagina 83
MANUALE VBA
X EXCEL
Case Else buff = buff & "Unknown error." End Select MsgBox buff End If End Sub
http://ennius.interfree.it/
Pagina 84
MANUALE VBA
X EXCEL
Codici a barre in Excel. Premesso che non uso e non so usare le procedure per utilizzare la conversione di stringhe numeriche o alfanumeriche in codice a barre, ho trovato un sito che interesserà tutti coloro che ne sono in cerca. Questo sito propone loro soluzioni che si basano sull'uso di Funzioni specifiche per la conversione, e che sono, da quanto letto, liberamente fruibili purchè non si alteri o modifichi il codice compilato. E' infatti possibile scaricare dal sito un file zip d'esempio. Solo che per poter veramente funzionare, è necessario ACQUISTARE i fonts necessari da installare sul sistema operativo in modo che le Funzioni trovino i fonts necessari per eseguire la conversione. Poichè la cosa è di per sè molto interessante, segnalo questo sito, tutto in inglese, anche se ben strutturato e con chiare indicazioni sulle modalità operative; questo l'indirizzo: http://www.bizfonts.com/vba/ Questa è un'immagine della homepage del sito:
ed un'immagine di come lavora la funzione con il font adatto installato (AdvC128b) :
http://ennius.interfree.it/
Pagina 85
MANUALE VBA
X EXCEL
Colore dei caratteri (font) Questa routine simile alla precedente, consente di ottenere un colore carattere diverso per ogni variabile impostata, in modo da evidenziare, appunto con colore diverso, lettere, numeri, parole o frasi. Come la precedente potrà essere impostata con valori preimpostati nel codice, o assegnati a variabili inserite in opportune celle. La variante principale rispetto alla precedente, è che dovrà essere inserita nell'evento SelectionChange del foglio di lavoro. Supponiamo quindi di avere delle lettere che vogliamo evidenziare con colori diversi, questa è la routine: (in verde i commenti) Private Sub Worksheet_SelectionChange(ByVal Target As Range) Dim CL As Object For Each CL In Range("A1:F100")
'per ogni cella in A1:F100
If CL.Value = "A" Then
'se il valore della cella è uguale ad A
CL.Font.ColorIndex = 3 rosso
'mi metti il colore del carattere uguale a
ElseIf CL.Value = "B" Then
'se invece il valore è uguale a B ecc.ecc
CL.Font.ColorIndex = 4 ElseIf CL.Value = "C" Then CL.Font.ColorIndex = 8 ElseIf CL.Value = "D" Then CL.Font.ColorIndex = 9 ElseIf CL.Value = "E" Then CL.Font.ColorIndex = 5 ElseIf CL.Value = "F" Then CL.Font.ColorIndex = 7 End If Next End Sub Appena dopo aver scritto la lettera e confermato con "invio", si attiva la routine e se la lettera corrisponderà ad una di quelle riportate nelle istruzioni, si colorerà con il colore scelto. In questa sezione, al paragrafo "Colori e Colorindex" potrete trovare la tabella indice/colori.
http://ennius.interfree.it/
Pagina 86
MANUALE VBA
X EXCEL
ComboBox, ListBox, Label e Image : Ovvero: come utilizzare una ComboBox o una ListBox per visualizzare delle immagini (foto) in un controllo "Image" o in una Label (etichetta). Abbiamo visto nel paragrafo precedente, come "caricare" con dati una ComboBox o una ListBox: sfruttando la proprietà "RowSource", alla quale va associato il riferimento alle celle che contengono i dati da visualizzare. In questo esercizio ci occupiamo di come associare la selezione di un dato presente nel "menu" dei due controlli, con la comparsa di un immagine associata al dato stesso. Per poter visualizzare delle immagini è necessario di disporre di due "controlli" (o "oggetti") presenti nella "casella degli strumenti" tipica di una UserForm: il controllo "Image", che grazie alle sue "proprietà" consente una migliore gestione di come visualizzare una determinata foto o immagine, oppure di una normale "Label" che offre decisamente poche possibilità. Vediamo le caratteristiche di questi due controlli: Controllo Image - proprietà: A destra, la finestra delle proprietà di un controllo Image: tre sono le proprietà più importanti: Picture: a lato andrebbe scritto il percorso completo che porta all'immagine da visualizzare, ma che invece gestiremo via codice. PictureAlignment: di default impostato su 2, cioè con l'immagine piazzata nel centro del controllo. PictureSizeMode: cioè il modo con cui la foto verrà visualizzata, e quindi: fmPictureSizeModeClip indica che si desidera visualizzare l'immagine con le dimensioni e l'ingrandimento originali. Se il controllo Image ha dimensioni inferiori rispetto all'immagine, verrà visualizzata soltanto la parte dell'immagine che rientra nel controllo. L'impostazione fmPictureSizeModeStretch ingrandisce l'immagine verticalmente e orizzontalmente, fino a farle raggiungere i margini del contenitore o del controllo. fmPictureSizeModeZoom ingrandisce l'immagine fino a quando questa non raggiunge i margini orizzontali oppure i margini verticali del del controllo. Se l'immagine raggiunge prima i margini orizzontali, lo spazio rimanente tra l'immagine e i margini verticali resterà vuoto. Se l'immagine raggiunge prima i margini verticali, lo spazio rimanente tra l'immagine e i margini orizzontali resterà vuoto. PictureTiling: di default impostata a False. Se le dimensioni di un'immagine sono inferiori a quelle del controllo che la contengono, è possibile affiancare più copie dell'immagine nel controllo. La modalità di affiancamento dipende dall'impostazione corrente delle proprietà PictureAlignment e PictureSizeMode. Se ad esempio PictureAlignment è impostata su fmPictureAlignmentTopLeft, la disposizione inizia nell'angolo superiore sinistro e l'immagine viene ripetuta verso destra e verso il basso nel form o nella pagina. Se PictureSizeMode è impostata su fmPictureSizeModeClip, quando l'ultima immagine affiancata non rientra completamente nei margini del controllo, tale immagine verrà ritagliata Controllo Label. Le proprietà di una Label interessate a ricevere un immagine sono in pratica due: La proprietà Picture (già vista sopra) http://ennius.interfree.it/
Pagina 87
MANUALE VBA
X EXCEL
La proprietà PicturePosition, che di default è impostata a : fmPicturePositionAboveCenter e che centra l'immagine all'interno della Label. 12 sono le impostazioni settabili, e invito i pellegrini a scoprirle da soli. Ma veniamo all'esempio di questo esercizio. In una zona del foglio di lavoro, creeremo un elenco che formerà il RowSource della ComboBox o della ListBox. Le ho usate tutte e due solo per mostrare le istruzioni usate che comunque differiscono di poco, e perchè la destinazione delle immagini collegate all'elenco richiamato dalla ListBox sarà la Label1. Il controllo Image supporta i seguenti formati di file: *.bmp - *.cur - *.gif - *.ico - *.jpg - *.wmf Condizione necessaria sarà comunque di definire una cartella che conterrà le immagini, e sarà opportuno chiamare le immagini con un nome che sarà lo stesso che scriveremo nel nostro elenco sul foglio di lavoro. Io ho chiamato le immagini : foto1.ipg, foto2.jpg, ecc.ecc. mentre nell'elenco ho usato solo il nome: foto1, foto2, ecc. senza estensione: in questo modo nelle istruzioni imposto una sola volta il formato dell'estensione: .jpg. Vediamo le istruzioni: Nell'UserForm_Activate faccio caricare le liste dei controllo ComboBox e ListBox : Private Sub UserForm_Activate() ComboBox1.RowSource = "A1:A10" ListBox1.RowSource = "A1:A10" End Sub Il comando per il caricamento dell'immagine nel controllo Image, si avvale della funzione LoadPicture seguita dall'indicazione del percorso dove risiede l'immagine. Poichè dovremo poter caricare più immagini a scelta, uso una variabile ( X ) per definire di volta in volta il nome che verrà selezionato nella ComboBox, sfruttando quindi l'evento ComboBox_Click per inserire le istruzioni, queste: Private Sub ComboBox1_Click() Dim X X = ComboBox1.Text Image1.Picture = LoadPicture("C:\Temp\" & X & ".jpg") End Sub Per la ListBox stesso discorso, con poche varianti, in particolare la destinazione che è una Label: Private Sub ListBox1_Click() Dim X X = ListBox1.Text Label1.Picture = LoadPicture("C:\Temp\" & X & ".jpg") End Sub E questa un immagine dei risultati: frecce verdi per ListBox1 e Label1, frecce rosse per ComboBox1 e Image1. Nella colonna A vediamo l'elenco che forma il RowSource:
http://ennius.interfree.it/
Pagina 88
MANUALE VBA
http://ennius.interfree.it/
X EXCEL
Pagina 89
MANUALE VBA
X EXCEL
ComboBox : ordinare la lista dei dati. Una delle domande che spesso mi viene rivolta è : come eliminare gli spazi vuoti dall'elenco di una ComboBox (delle ActiveX); spazi vuoti rappresentati dalle celle senza dati presenti nel range assegnato alla proprietà ListFillRange ,se la combo è inserita sul foglio di lavoro, o dalla proprietà RowSource se la combo è inserita in una UserForm. Il discorso vale comunque anche per le ListBox. Vedremo anche come far caricare una sola volta i dati se questi sono doppi. Ma diamo un occhiata ad un esempio di un elenco preparato ad hoc:
Come si nota, i dati nella colonna B (da B5 in giù) non sono in righe contigue, e la ComboBox che "pesca" la lista (con la proprietà ListFillRange) in questo range, presenta analogamente un elenco con spazi vuoti. La cosa di per se non rappresenta un errore, ma può dare fastidio dover scorrere un menù più lungo, alla ricerca del valore voluto. Inoltre i dati nel menù, rispecchiando fedelmente l'origine dati, non sono in ordine alfabetico, cosa che ci farebbe piacere specie con elenchi lunghi dove risulterebbe più veloce spostarsi ad una determinata lettera iniziale. Qualcuno potrebbe obiettare che basterebbe creare un ordinamento sulla tabella di origine, per vederli in ordine anche nella combobox, ma esistono molti casi in cui questo non è possibile, specie con elenchi impostati su chiavi che non sono le stesse dei dati richiamati dalla combobox, o ancora casi in cui l'inserimento di dati nella tabella avviene in progressione, senza poter determinare un ordine di inserimento basato sui dati richiamati nella combobox. Insomma, qualunque sia il motivo, andiamo avanti. Un modo abbastanza veloce e semplice per la nostra esigenza può essere questo: Copiare in una altra colonna del foglio, in una colonna fuori vista (io userò la colonna H nell'esempio), i dati contenuti nella colonna B. Applicare un ordinamento ascendente su questa colonna (la H) per ordinare alfabeticamente i nomi. Con questo passo le celle vuote vengono automaticamente accodate all'elenco che si ordinerà. Assegnare questa colonna (H) come ListFillRange della ComboBox, sfruttando End per reperire l'ultima cella occupata. In questo modo il nostro elenco sarà depurato dagli spazi vuoti, e con i nomi in ordine alfabetico. Queste sono le immagini della ComboBox "epurata" e ordinata e della nuova lista che si crea nella colonna H
http://ennius.interfree.it/
Pagina 90
MANUALE VBA
X EXCEL
Ed ora vediamo come procedere, e poi le istruzioni, che ho posizionato nell'evento Click della ComboBox1. In verde le spiegazioni. iniziamo ad assegnare, in "modalità progettazione" (che si attiva appena inserita la ComboBox sul foglio), nella finestra delle sue proprietà, oltre alla LinkedCell, anche un range di celle alla proprietà ListFillange. Operazione NECESSARIA per inizializzare un elenco nella stessa combo, altrimenti sarebbe vuota e non si genererebbe nè l'evento Click, nè l'evento Change necessari ad attivare le istruzioni, oppure potremmo sfruttare l'evento Workbook_Open per "caricare" la lista della combo box, con un'istruzione tipo: Private Sub Workbook_Open() Worksheets("Foglio1").ComboBox1.ListFillRange = "B5:B100" 'e già che ci siamo, impostiamo anche la LinkedCell Worksheets("Foglio1").ComboBox1.LinkedCell = "B2" 'o la cella che vorrete End Sub Non ha importanza il range che viene caricato in quanto al primo click nella lista della combobox otterremo l'attivazione della macro e dell'ordinamento. Private Sub ComboBox1_Click() 'sotto. "puliamo" la ListFillRange della Combobox ComboBox1.ListFillRange = "" 'sotto : puliamo la colonna H predisponendola alla copia del nuovo elenco ActiveSheet.Range("H:H").ClearContents 'sotto : impostiamo con "zona" il range che dovrà essere copiato; poichè non è possibile 'usare End per determinare la fine elenco, dovremo assegnare un numero di celle che 'preveda successivi inserimenti di dati Set zona = Range("B5:B100") zona.Select
'si seleziona il range con i dati e le celle vuote (zona)
zona.Copy Range("H5")
'e la si copia nella colonna H a partire da H5
'sotto : poichè avremo una copia che contiene celle vuote anche in H, non potremo usare 'End per gli stessi motivi, assegneremo noi un range che copra quello di origine dati, e lo 'selezioniamo Range("H5:H100").Select http://ennius.interfree.it/
Pagina 91
MANUALE VBA
X EXCEL
'sotto: ora si esegue l'ordinamento sulla selezione (H) Selection.Sort Key1:=Range("H5"), Order1:=xlAscending, Header:=xlGuess, _ OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom 'sotto: ora che le celle vuote sono tutte finite a fine elenco, possiamo usare End per 'reperire l'ultima cella con i dati, ed assegniamo a due variabili, gli indirizzi della cella 'iniziale ( x ) e finale ( y ) x = Cells(5, 8).Address y = Cells(5, 8).End(xlDown).Address 'sotto : ora con i riferimenti esatti alle celle, carichiamo la ListFillRange della combobox ComboBox1.ListFillRange = "" & x & ":" & y & "" Range("B2").Select l'area in H
' poi selezioniamo una cella per deselezionare
End Sub Se invece vorremo una lista che non preveda le voci doppie, dovremo inserire nella precedente routine, altre istruzioni per l'eliminazione dei doppioni. (routine peraltro già presente in questa sezione, articolo "Eliminare dati doppioni". Vediamo sotto come integrare le due serie di istruzioni. Non ripeto le spiegazioni: Private Sub ComboBox1_Change() ComboBox1.ListFillRange = "" ActiveSheet.Range("H:H").ClearContents Set zona = Range("B5:B100") zona.Select zona.Copy Range("H5") Range("H5:H100").Select Selection.Sort Key1:=Range("H5"), Order1:=xlAscending, Header:=xlGuess, _ OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom 'sotto, inizia il controllo dei doppioni, nel caso venga trovato un dato doppio, viene 'cancellata la CELLA Set currentCell = Worksheets("Foglio1").Range("H5") Do While Not IsEmpty(currentCell) Set nextCell = currentCell.Offset(1, 0) If nextCell.Value = currentCell.Value Then currentCell.Cells.Delete End If Set currentCell = nextCell Loop 'poi si continua come sopra x = Cells(5, 8).Address y = Cells(5, 8).End(xlDown).Address ComboBox1.ListFillRange = "" & x & ":" & y & "" Range("B2").Select End Sub http://ennius.interfree.it/
Pagina 92
MANUALE VBA
X EXCEL
ComboBox e ListBox. Ovvero: come caricare una ListBox selezionando una voce nella Combobox Le ComboBox e ListBox che useremo fanno parte delle ActiveX, e le troviamo nella "Casella degli Strumenti" o "Strumenti di controllo" (a seconda delle versioni di Excel) dal menù "Visualizza/Barre degli strumenti". Questi due "oggetti" sono diversi dalle ComboBox e ListBox presi da "Moduli". Gli "oggetti" facenti parte delle ActiveX sono inseribili sia in un foglio di lavoro, sia in una UserForm, anch'esso un "oggetto" inseribile ma dall'Editor di Visual Basic (VBE). Tutti gli "Oggetti" possiedono delle "proprietà", ed una di queste proprietà presenti sia nelle ComboBox sia nelle ListBox è quella che ci consente di determinare la provenienza dei dati che saranno poi visualizzati nelle stesse. A secondo il posizionamento di questi due "controlli" (su foglio o su UserForm) , cambia il nome della "proprietà", anche se il "funzionamento" è il solito: con oggetto sul foglio di lavoro la proprietà è : ListFillRange con oggetto su una UserForm la proprietà è : RowSource Entrambe queste proprietà sono programmabili, sia scrivendo direttamente nella finestra delle loro "proprietà" il percorso della zona che contiene i dati da visualizzare, sia attraverso istruzioni inserite in un evento. Dopo queste premesse, veniamo al problema preso come esempio. Supponiamo di avere una tabella sul foglio1 (o elenco, o database che dir si voglia), dove sono inseriti degli articoli, con codice articolo (campo necessariamente univoco), descrizione, prezzo, ecc. Ogni articolo però è formato da sottocategorie, ognuna con caratteristiche diverse, e non tutti gli articoli possiedono lo stesso numero di sottocategorie. Non è quindi pensabile di realizzare un'unica tabella che comprenda tutte queste disponibilità. Ci affideremo quindi ad un secondo foglio (Foglio2) dove costruire la nostra tabella delle sottocategorie. Questa tabella dovrà necessariamente possedere il campo "Codice articolo", un campo "categoria" (che potrà essere a sua volta formata da un codice), ed altri dati correlati che ne identifichino le diversità. Il problema nasce quando vorremo, per un determinato articolo, visualizzare tutti i dati pertinenti l'articolo, presenti nelle sottocategorie. Ovviamente questo esercizio si presta ad un'infinità di varianti, sia come tipo di dati sia come scelta dei campi correlati che vorremo vedere, se ci pensiamo bene, corrisponde un pò a "filtrare" i dati, anche se con tecniche diverse dall'uso del "Filtro" e con risultati diversi. Stabilito qual'è il problema, vediamo come affrontarlo : useremo una UserForm, che chiameremo da un pulsante posto sul Foglio1, e nella U.F. inseriremo una Combobox ed una Listbox. Useremo l'apertura della UserForm (UserForm1_Activate) per "caricare" l'elenco nella ComboBox1 assegnando via codice, la zona che contiene i "Codici Articolo" della nostra tabella, ciòe la colonna A, dalla riga 1 fino a dove vorremo, usando questa istruzione: Private Sub UserForm_Activate() 'carico la lista degli articoli nella colonna A del foglio1 ComboBox1.RowSource = ("A1:A10") End Sub e questo sarà l'effetto sulla Combobox (a sinistra l'elenco sul foglio 1)
http://ennius.interfree.it/
Pagina 93
MANUALE VBA
X EXCEL
A questo punto dovremo sviluppare delle istruzioni che ci consentano di ottenere , selezionando uno degli articoli presenti nella lista, che nella ListBox vengano caricate tutte le occorrenze relative al codice scelto. Il sistema che presento è semplice: faccio cercare nella tabella sul Foglio2, tutti i codici articolo uguali a quello selezionato. Individuato un articolo, faccio estrarre i dati che mi interessano (nella stessa riga ma nelle due colonne a destra) e li copio in una zona appositamente assegnata, sullo stesso Foglio2 (colonne G e H), con ricerca della prima riga libera, in modo da comporre un elenco. Questa zona con l'elenco così composto, è la zona che assegno alla proprietà RowSource della ListBox. Un'altra proprietà della ListBox da sfruttare, è la "ColumnCount" normalmente impostata a 1 e che determina in numero delle colonne visibili nella Listbox. Noi imposteremo questa proprietà a 2, perchè due sono i campi che abbiamo deciso di estrarre. Un'altro accorgimento da usare, sarà quello di pulire la zona che forma il RowSource della Listbox, in modo che ad ogni selezione nella ComboBox si azzeri il contenuto della ListBox e si rinfreschi con nuovi dati (Refresh). Per queste istruzioni sfrutteremo l'evento Click della Combobox: Private Sub ComboBox1_Click() 'questo sotto evita il saltellamento a schermo Application.ScreenUpdating = False 'faccio pulire la zona che uso come estrazione dati Worksheets(2).Range("G1:H10").ClearContents
Dim CL As Object
'dichiaro la variabile CL come Object
Worksheets(2).Select
'seleziono il foglio2
'per ogni cella nel range A1:A20 foglio2 For Each CL In Worksheets(2).Range("A1:A20") 'se il valore nella cella combobox
(CL) è uguale al valore selezionato nella
If CL = ComboBox1.Text Then 'allora seleziono le due celle a destra di quella trovata Range(CL.Offset(0, 1), CL.Offset(0, 2)).Select 'copio tutte e due i valori Selection.Copy 'cerco la prima riga libera a partire dalla riga 1 e nella colonna 7 (la G ) Dim iRow As Integer iRow = 1 http://ennius.interfree.it/
Pagina 94
MANUALE VBA
X EXCEL
While Cells(iRow, 1).Columns(7).Value "" iRow = iRow + 1 Wend 'trovata la riga con la cella vuota, incollo i dati copiati con Selection.Copy (sopra) Selection.Copy Cells(iRow, 1).Columns(7)
End If Next 'finito il ciclo di ricerca, carico la listbox con i dati ora contenuti nelle colonne G e H ListBox1.RowSource = "Foglio2!G1:H10" 'poi ritorno sul foglio1 Worksheets(1).Select End Sub e questo sarà il risultato (a sinistra la zona del Foglio2 con la seconda tabella)
Appare evidente che i range usati nell'esempio sono modificabili a piacere e sarà possibile usare anche la funzione End per non determinare a priori la lunghezza di un elenco.
http://ennius.interfree.it/
Pagina 95
MANUALE VBA
X EXCEL
ComboBox (ActiveX) collegata ad un Range su Foglio di un'altra Cartella. A volte si ha la necessità di richiamare un elenco di dati che risiedono su un foglio diverso da quello sul quale stiamo lavorando, e vogliamo usare una ComboBox (da "Casella degli Strumenti" o "Strumenti di controllo") per avere un riepilogo di cui desideriamo operare una scelta (Menù lista). La proprietà di una ComboBox, posizionata sul foglio di lavoro, interessata a reperire il range di celle (sempre in verticale) da mostrarci nel menù a tendina della stessa C.box, è la proprietà ListFillRange(cambia se usiamo ua C.box su una UserForm, in questo caso è RowSource). La sintassi da usare nella Finestra delle Proprietà della ComboBox, (in "Modalità Progettazione", visibile nell'Editor di Visual Basic), per ListFillRange, è la seguente (esempio): ListFillRange A1:A100 Ovviamente il range è riferito al foglio di lavoro nel quale è stata inserita la ComboBox, ed è sufficiente indicare solo i Riferimenti alle celle. Quando invece si usa la Combobox inserita su un Foglio, ma vogliamo "caricare" un range di celle che sono però su un altro foglio della stessa cartella di lavoro, bisogna anteporre al range, l'indicazione di quale foglio conterrà detto range, seguito da un punto esclamativo ( ! ). Esempio: ListFillRange Foglio2!A1:A100 E' possibile però cercare e caricare un elenco di dati che risiedono su un'altro Foglio di un'altra Cartella di lavoro. Condizione necessaria sarà che ANCHE l'altra Cartella di lavoro sia aperta. Attenzione alla sequenza con la quale apriremo le due cartelle: dovremo per PRIMO aprire la cartella che contiene i dati richiamati dalla ComboBox presente sull'altra cartella, e POI aprire quest'ultima. Ma vediamo la sintassi da usare in questo caso per istruire il ListFillRange della combobox: ListFillRange '[Pippo.xls]Foglio1'!B10:B50 Come si nota, il riferimento viene scritto riportando, tra Parentesi Quadre, il nome del file che porta i dati, poi l'indicazione del Foglio dove risiede il range dei dati, il punto esclamativo, indi i riferimenti al range di celle. Inoltre la stringa NomeFile/Nome foglio, va inserita tra apici semplici ( ' ). Nell'eventualità che si apra invece per primo il foglio dove risiede la combobox, apparirà all'apertura, una finestra che ci chiede se vogliamo aggiornare i dati : a questo punto, sarà indifferente premere "aggiorna" o "non aggiornare" , visto che Pippo.xls è ancora chiuso e i dati chiamati dalla combobox non sono disponibili. E però possibile aprire anche Pippo.xls, selezionare il foglio che contiene la combobox, che non si aggiornerà perchè già aperto, ma predisporre un CommandButton ad quale affidare la seguente routine : Private Sub CommandButton1_Click() ComboBox1.ListFillRange = " ' [Trova.xls]Foglio1'!A1:A5" End Sub Basterà un click sul CommandButton e verrà ripristinato il collegamento, a fogli aperti, e la combobox riporterà ora l'elenco voluto. Anche per le Caselle Combinate (simili a ComboBox, ma prese dalla finestrina "Moduli"), dove si usa, anzichè il ListFillRange di cui sono sprovviste, l' "Intervallo di input" (da: Formato Controllo) come indicazione dell'area dati da "caricare", andrà scritto il percorso che identifica l'area di provenienza dati in questa maniera: http://ennius.interfree.it/
Pagina 96
MANUALE VBA
X EXCEL
'[Trova.xls]Foglio1'!A1:A5 Per le ComboBox ActiveX poste su UserForm, ricordo che la proprietà che identifica l'origine dati non è la ListFillRange ma RowSource. Il discorso non cambia, e potremo usare la stessa sintassi: ComboBox1.RowSource = " ' [Trova.xls]Foglio1'!A1:A5"
http://ennius.interfree.it/
Pagina 97
MANUALE VBA
X EXCEL
Un CommandButton personalizzabile. (Controllo ActiveX) Per tutti coloro che usando CommandButton su UserForm, vogliano renderli più carini, facendo cambiare colore ed effetto (ma anche cambiare ev. immagini sul pulsante stesso) a secondo dell'azione che compiamo sul commandbutton stesso (al passaggio del mouse o sul click), presento un controllo ActiveX reperito sul Web, e rilasciato freeware, cioè di libero utilizzo, che risulta di facile settaggio per ottenere i risultati voluti. Ma vediamo come inserirlo e le sue proprietà. Si tratta di un ocx e si chiama : MyHover.ocx: una volta scaricato e decompresso, va posto nella cartella System di Windows (o System32, a secondo delle versioni di Windows). Una volta aperto Excel, dal suo editor di visual basic, inseriremo una UserForm, e dalla sua casella degli strumenti, cliccandoci destro di mouse, sceglieremo : "Controlli aggiuntivi":
si aprirà la finestra per la selezione dei controlli disponibili e su questa metteremo un segno di spunta al controllo "MyHoverButton Button" (nell'immagine vediamo anche il percorso dove risiede il controllo. (seconda freccia in basso))
Se il controllo non appare nella finestra "Controlli aggiuntivi", bisognerà seguire un altro percorso: dal menù "Strumenti" del VBE, scegliere la voce "Riferimenti", si aprirà la finestra dei "Riferimenti disponibili", sceglieremo il pulsante "Sfoglia" che aprirà la finestra "Aggiungi riferimento", che ci mostrerà i file presenti nella cartella System (o System32); in questa finestra, nel menù inferiore, quello relativo al "Tipo di file", dovremo selezionare la voce "Controlli ActiveX (*.ocx)"; a questo punto nella finestra dei file compariranno tutti i file .ocx, cercheremo e selezioneremo MyHover.ocx e premeremo il pulsante "Apri". Questa operazione porterà il controllo scelto nella finestra "Riferimenti Disponibili", basterà mettere un segno di spunta al controllo voluto che sarà quindi disponibile per essere "caricato" nella "casella degli http://ennius.interfree.it/
Pagina 98
MANUALE VBA
X EXCEL
strumenti" della UserForm. (icona rossa indicata da una freccia nella prima immagine). Cliccheremo questa icona e, "trascinando" nella UserForm, otterremmo il nostro commandbutton:
Il commandbutton così inserito avrà tutte le proprietà tipiche di un command button, con in più la possibilità di sceglie, oltre al colore e alla Caption di base: un colore ed una Caption diversa quando si passerà sul pulsante con il mouse, oppure: un'immagine da inserire sfruttando la proprietà "Picture" relativa un colore ed una Caption diversa quando si premerà il pulsante, oppure: un'immagine da inserire sfruttando la proprietà "Picture" relativa
Le proprietà interessate sono quelle evidenziate con le frecce, e di facile comprensione, comunque vediamole insieme: BackColor - il colore di base Caption - la parola o frase che identifica l'azione DownBackColor - il colore che sostituirà il colore di base all'atto della pressione sul pulsante. DownCaption - la parola o la frase che apparirà all'atto della pressione sul pulsante. DownPicture - un immagine che apparirà all'atto della pressione sul pulsante. HoverBackColor - il colore che sostituirà il colore di base al passaggio del mouse. HoverCaption - la parola o la frase che apparirà al passaggio del mouse HoverPicture - un immagine che apparirà al passaggio del mouse Picture - l'immagine scelta come base
Come vedete, un simpatico modo di abbellire i nostri lavori; questi tre esempi:
http://ennius.interfree.it/
Pagina 99
MANUALE VBA
http://ennius.interfree.it/
X EXCEL
Pagina 100
MANUALE VBA
X EXCEL
Usare un CommandButton (ActiveX) per una doppia istruzione. Ovvero: come far eseguire allo stesso CommandButton due istruzioni alternate sfruttando l'evento Click. Tutte le operazioni eseguite tramite una routine vba non sono intercettabili dall'opzione "Annulla" del menù "Modifica/Annulla". Una volta che il codice vba è stato eseguito, le istruzioni eseguite sono permanenti e si potranno annullare solo se prevediamo un routine che esegua al contrario le stesse istruzioni. A questo punto avremmo bisogno di un'altro commandbutton con le istruzioni per il ripristino, ma si può fare tutto con un solo commandbutton, sfruttando la sua proprietà Caption. Sarà sufficiente creare un'istruzione condizionale ( If... Then ) che legga il valore della Caption e su questo valore esegua l'istruzione appropriata. Il segreto di tutto è: facciamo cambiare il valore della Caption al primo Click sul commandbutton e lo ripristiniamo al secondo Click: in questa alternanza impostiamo le condizioni che il codice legge, ed eseguirà l'istruzione relativa. Facciamo un esempio, supponiamo di voler cambiare il colore delle celle in un certo range, e poi di volerlo ripristinare (è un esempio banale, ma serve allo scopo di vedere i passaggi) se necessario. Per prima cosa, una volta inserito il CommandButton prendendolo da "Casella degli strumenti" o da "Strumenti di controllo" ( a secondo le versioni di Excel), e quindi un "controllo" ActiveX, ci rechiamo nell'editor di visual basic, in modalità "progettazione", e nella finestra delle proprietà del CommanButton, alla proprietà Caption, assegniamo un nome, ad esempio "Pigia". Questa sarà la parola che vedremo scritta sul pulsante, e questa parola sarà il valore di default assegnato al commandbutton, ovvero all'apertura della cartella quello è il nome con cui il pulsante si presenta. Ora vediamo come compilare le istruzioni, da inserire nell'evento Click dello stesso CommandButton: Private Sub CommandButton1_Click() 'si controlla il valore della Caption : se è uguale a Pigia, si esegue If CommandButton1.Caption = "Pigia" Then 'l'istruzione che colora di giallo il range di celle e Range("C1:D10").Interior.Colorindex = 6 'ora rinomina la Caption in Annulla; in questo modo al prossimo click, con la caption 'impostata ad "Annulla", verrà eseguita CommandButton1.Caption = "Annulla" 'con Else, questa istruzione, che Else 'ripristina il colore togliendolo Range("C1:D10").Interior.Colorindex = xlNone 'e rinomina la Caption Pigia CommandButton1.Caption = "Pigia" End If End Sub E' evidente che potrà essere eseguita qualunque istruzione, compresa la sostituzione di valori e il relativo ricalcolo.
http://ennius.interfree.it/
Pagina 101
MANUALE VBA
X EXCEL
Conto alla rovescia (Countdown) Un simpatico esercizio per crearsi un "Conto alla rovescia". Le istruzioni si basano sull'utilizzo della funzione Timer, che ci consente di "temporizzare" delle istruzioni. Vogliamo infatti usare una cella (ma anche una MsgBox) dove veder scorrere i secondi all'indietro fino ad un'ora X. Queste le istruzioni, ottenute modificando un esempio preso dalla guida in linea. Sub ContoRovescia() 'assegniamo ad una cella un valore espresso in secondi che rappresenta il tempo massimo 'iniziale Range("B1").Value = 60 10:
'indice riga a cui si ritorna per continuare il ciclo
Dim PauseTime, Start
'dimensionamento delle due variabili
PauseTime = 1 ' Imposta la durata espressa in secondi Start = Timer ' Imposta l'ora di inizio. Do While Timer < Start + PauseTime DoEvents ' Passa il controllo ad altri processi. Loop 'quindi ad ogni secondo trascorso temporizzato dal timer si sottrae 1 al valore presente 'nella cella B1 (sotto) Range("B1").Value = Range("B1").Value - 1 'inseriamo l'istruzione: se B1 è uguale a zero If Range("B1").Value = 0 Then 'avvisiamo con un messaggio MsgBox "L'ora X è scoccata" Exit Sub
'si esce dalla routine
End If 'altrimenti (se non siamo a zero in B1), si ritorna alla riga 10 GoTo 10
End Sub
Attenzione: con valori di PauseTime bassi (un secondo come nell'esempio), se si lavora sul foglio di lavoro si interrompe in conto alla rovescia.
http://ennius.interfree.it/
Pagina 102
MANUALE VBA
X EXCEL
Controllo immissione dati Questa routine consente di verificare se i dati che vengono inseriti in una cella corrispondono a parametri fissi stabiliti in precedenza. Facciamo un esempio: nella zona che comprende tutte le celle da A1 a F100 vogliamo inserire solo lettere che corrispondano ad A, B, C, D, E, e che ci venga segnalato immediatamente se scriviamo qualunque cosa che sia diversa: questa è la routine da inserire nell'evento Change del Foglio su cui lavoriamo: (in verde sono i commenti) Private Sub Worksheet_Change(ByVal Target As Range) Dim CL As Object
For Each CL In Range("A1:F100") If CL.Value = "" Then GoTo 10 (la successiva)
'per ogni cella nella zona A1:F100
'se la cella è vuota passa a 10: Next
If CL.Value = "A" Or CL.Value = "B" Or CL.Value = "C" _ Or CL.Value = "D" Or CL.Value = "E" Then GoTo 10 'se la cella contiene una di queste queste lettere, vai alla successiva Else CL.Select
'altrimenti 'seleziona la cella col valore diverso
MsgBox ("Errore.Valore non Ammesso!!")
'avvisami con questo messaggio
End If 10: Next
End Sub Appare evidente che potremo, modificando i parametri assegnati a CL.Value =, eseguire controlli sia su lettere, sia su numeri, sia su frasi. Con questa soluzione avremo un controllo "predefinito" in quanto il valore da controllare nelle celle è inserito direttamente nel codice. Se invece volessimo che il controllo fosse eseguito non su valori fissi perchè "preimpostati", ma assegnati da variabili, potremmo utilizzare delle celle (al di fuori del range su cui si esegue il controllo) nelle quali potremmo inserire dei valori che possiamo variare a nostro piacimento. Per esempio potremo assegnare le celle G1:G4 alla routine, e in queste celle scrivere ciò che vogliamo sia usato come valore di confronto. Un unico appunto: se in G1 scriviamo "pippo" (ed avremo inserito in alcune celle del range A1:F100 la stessa parola), sostituendolo in G1 con "peppo", la routine troverà "pippo" e lo segnalerà come errore. Comunque questa è la routine da usare con valori variabili: Private Sub Worksheet_Change(ByVal Target As Range) Dim CL As Object For Each CL In Range("A1:F100") 'per ogni cella nella zona A1:F100 If CL.Value = "" Then GoTo 10 'se la cella è vuota passa a 10: Next (la successiva) If CL.Value = Range("G1").Value Or CL.Value = Range("G2").Value _ Or CL.Value = Range("G3").Value Or CL.Value = Range("G4").Value Then GoTo 10 'se la cella contiene un valore uguale a questi Range, vai alla successiva Else 'altrimenti CL.Select 'seleziona la cella col valore diverso MsgBox ("Errore.Valore non Ammesso!!") 'avvisami con questo messaggio http://ennius.interfree.it/
Pagina 103
MANUALE VBA
X EXCEL
End If 10: Next End Sub In questo modo, ciò che verrà scritto nelle celle G1 ecc, sarà usato come valore di confronto.
http://ennius.interfree.it/
Pagina 104
MANUALE VBA
X EXCEL
Gestire Convalida usando il VBA Visto l'interesse che desta l'utilizzo dello strumento Convalida, che non richiede assolutamente nessuna istruzione in codice vba (di cui trovate tre diversi utilizzi sul sito, sezione "Formule"), era naturale che a qualcuno venisse la curiosità di chiedermi "ma si può gestire Convalida via vba?". E quindi in questa pagina parleremo delle istruzioni che si possono usare per ottenere quello che in vba si chiama "Validation". Prenderò come esempio quanto riportato in "Usare CONVALIDA (3)", per mostrare come si possa ottenere lo stesso risultato con l'impiego del Vba. Senza stare a ripetere cosa vorremo ottenere (basta leggere la pagina in "formule"), passo alla routine: Sub MacroConvalida() With Range("D2:D7").Validation .Delete .Add Type:=xlValidateWholeNumber, AlertStyle:=xlValidAlertStop, _ Operator:=xlBetween, Formula1:="1", Formula2:="6" .IgnoreBlank = True .InCellDropdown = True .InputTitle = "inserire valore " .ErrorTitle = "avviso" .InputMessage = "valori ammessi compresi tra 1 e 6" .ErrorMessage = "hai inserito un valore errato" .ShowInput = True .ShowError = True End With End Sub Attenzione! La routine sopra, funziona solo con numeri interi, NON decimali, in base all'impostazione: .Add Type:=xlValidateWholeNumber, se si volesse invece un controllo che intervenga per numeri decimali, come nel caso di valuta Euro, andrà sostituito WholeNumber con Decimal, così: .Add Type:=xlValidateDecimal E' inoltre possibile assegnare dei valori variabili, usando il sistema di usare due celle del foglio di lavoro, come "contenitori" dei valori che servono di confronto, e poi via codice, assegnando il valore di queste celle a due variabili ( X e Y ), richiamare queste al posto di valori predefiniti nelle istruzioni Formula1:= e Formula2:= ; sarà necessario modificare anche la stringa assegnata ad "InputMessage" perchè possa presentare un messaggio aggiornato con i valori delle variabili, come nell'esempio sotto fatto per valori decimali: Sub MacroModificata() Dim X, Y X = Range("J1").Value 'J1 cella che conterrà il valore minimo Y = Range("K1").Value 'K1 cella che conterrà il valore massimo With Range("D2,E7,C3").Validation 'definizione delle celle per la convalida .Delete .Add Type:=xlValidateDecimal, AlertStyle:=xlValidAlertStop, _ Operator:=xlBetween, Formula1:=X, Formula2:=Y .IgnoreBlank = True .InCellDropdown = True .InputTitle = "inserire valore " .ErrorTitle = "avviso" .InputMessage = "valori ammessi compresi tra " & X & " e " & Y & "" .ErrorMessage = "hai inserito un valore errato" .ShowInput = True .ShowError = True End With End Sub E questa è la routine per togliere dalle stesse celle, il controllo convalida: Sub TogliConvalida() With Range("D2:D7").Validation http://ennius.interfree.it/
Pagina 105
MANUALE VBA
X EXCEL
.Delete End With MsgBox "E' stata tolta la convalida alle celle richieste" End Sub Alcune precisazioni. Si può assegnare, come range di applicazione della Convalida, non solo un range di celle contigue, come nell'esempio ( Range("D2:D7" ), ma anche celle "sparse", dove appunto ci interessi eseguire un controllo, basterà indicare nei riferimenti del Range, i riferimenti alle celle, separate da una virgola, così : Range("D1, C2, D3, F10, A1)" . Poichè le combinazioni del controllo convalida sono diverse, suggerisco di usare il "Registratore di macro", Se avviate il registratore e poi scegliete i vari passaggi di Convalida, alla fine, premendo lo stop del registratore, nell'editor di visual basic, nel Modulo che troverete, potrete leggere tutto il codice corrispondente alle azioni da voi fatte e quindi vedere, imparare e modificare, se credete, le istruzioni che il registratore ha compilato per voi. Registrate gente, registrate.
http://ennius.interfree.it/
Pagina 106
MANUALE VBA
X EXCEL
Tipo di dati. Formati di Conversione del tipo di dati. Conosciamo ed abbiamo già visto in questa sezione, la funzione vba Format, che ha il compito di predisporre il formato di un dato in modo che noi possiamo vederlo restituito con il formato che abbiamo deciso. Mi sono accorto che esiste una certa confusione di interpretazione sul comportamento di ciò che in realtà otteniamo con la funzione Format in vba, e vorrei cercare di chiarire alcuni aspetti. Quando siamo sul foglio di lavoro e scegliamo "Formato Celle", noi in realtà scegliamo il "TIPO DI DATI" che una cella dovrà contenere, e nella finestra "Numero", una volta selezionato il Tipo di dato, nella parte destra appare il tipo di Formato con il quale vogliamo che il dato ci venga mostrato nella cella.
E' quindi Excel che provvede, attraverso le nostre decisioni, ad impostare sia il TIPO di dato, sia il FORMATO nella cella. Quando ci troviamo a lavorare di codice, dovremo invece essere noi a compilare le necessarie istruzioni sia per definire il formato, sia per il tipo di dati. Per quanto riguarda il formato, useremo la funzione Format (vba) che ha esclusivamente il compito di predisporre il modo in cui un dato ci appare, per esempio in una TextBox, o come il codice lo passa d una cella del foglio di lavoro. E' soprattutto questa differenza tra Formato (Format) e Tipo di dati che genera confusione. In parole povere questa istruzione: TextBox1 = Format(TextBox1, "#,###.00") determina SOLO il modo in cui il valore verrà mostrato nella textbox o in una cella, ma NON dice assolutamente ad Excel di che TIPO di dati viene passato dal vba. Per comunicare quindi ad Excel il TIPO di dati, è necessario dichiarare di che TIPO di dati si tratta. Questa operazione si chiama "CONVERSIONE DEL TIPO DI DATI". Dovremo usare una sintassi specifica, per definire se è un numero intero o un numero con decimali, se è testo, o una data, o un orario, ecc, in modo che il vba passi ad Excel il dato facendogli capire di che cosa si tratta. Per fare questo possiamo procedere in diversi modi: Assegnare il Tipo di dati ad una variabile: Dim MioValore As Double MioValore = TextBox1.Value Cells(1, 7).Value = MioValore oppure, molto più semplicemente, senza usare il dimensionamento, così: MioValore = TextBox1.Value Cells(1, 7).Value = CDbl(MioValore) o, più veloce ancora: Cells(1, 7).Value = CDbl(TextBox1) Tutti questi esempi restituiscono tutti un valore Double, cioè un numero con decimali, e anche senza il Format nella Textbox, il dato che viene passato al foglio viene riconosciuto come Double e corrisponderebbe all'azione che avremmo fatto sul foglio di lavoro scegliendo da "Formato Celle", "Numero con decimali". Ovviamente l'impiego della funzione Format in abbinamento all'indicazione del Tipo di dato, ci avrebbe consentito di decidere anche con quanti decimali il valore sarebbe stato poi visualizzato nella cella. Esempio: TextBox1 = Format(TextBox1, "#,###.00") http://ennius.interfree.it/
Pagina 107
MANUALE VBA
X EXCEL
Cells(1, 7).Value = CDbl(TextBox1) oppure, in un unica istruzione: Cells(1, 7).Value = CDbl(Format(TextBox1, "#,###.00")) ed il valore restituito in G7 sarà Double a due decimali anche se noi avremo 1354,8792 Quando si adopera "Tipi di dati" numerici, bisogna sempre ricordarsi che la textbox non può restare vuota, pena un errore di debug, (il vuoto per il vba NON corrisponde a zero) ma bisogna impostare un istruzione tipo questa If TextBox1 = "" Then TextBox1 = 0 e ovviamente va posizionata prima del comando di trasferimento al foglio. If TextBox1 = "" Then TextBox1 = 0 Cells(1, 7).Value = CDbl(TextBox1) Per quanto riguarda il Tipo di dati, la guida a questo proposito è comunque ben fornita, solo che bisogna conoscere come cercare. Nella guida dell'editor del visual basic, andare sull'indice, digitare : Funzione, e nella finestra sotto più grande (la 3) dove c'è scritto: selezionare un argomento, scorrere fino a trovere. "Funzioni di conversione del tipo", selezionare l'argomento, e sulla destra ve li trovate tutti. E comunque i più usati sono: CDate - per indicare che si sta trasferendo una data, ed è utile usare prima una variabile per definire il formato data, esempio MyStr = Format(TextBox1, "h:m:s") Cells(1, 7).Value = CDate(MyStr) CDbl - per tutti i numeri positivi o negativi con decimali (idem c.s. per definire il formato numerico) CLng - per interi numerici CInt - tipo di dati integer CStr - tipo di dati stringa (formato testo) Val - più generico per numeri o testo (o solo numeri se presenti in una stringa) Cells(1, 7).Value = Val(TextBox1)
http://ennius.interfree.it/
Pagina 108
MANUALE VBA
X EXCEL
La procedura Indiceriga. Utilizzo: Copia/Incolla con ricerca della prima riga libera per incollare. Questa procedura non esiste come funzione predefinita in Excel e non è documentata, quindi non cercatela nella Guida in Linea. Si basa sul dimensionamento di una variabile: "Indiceriga". Esistono altre procedure che consentono di "incollare" dati su fogli di lavoro, cercando la prima riga libera per "scaricare" i dati precedentemente copiati, ma si basano su una ricerca (della prima riga libera), che parte dall' ultima riga, e sale verso la prima: questo vuol dire che se noi avessimo destinato per l'incollaggio, per esempio, la zona che va dalla prima alla 50esima riga, e nella riga 60 avessimo scritto altri dati, l'incollaggio avverrebbe non nella zona che noi vorremmo (righe 1-50), ma al di sotto della riga 60. La procedura che propongo, permette di "mirare" con esattezza la zona di destinazione, e qui, cercare la prima riga libera per incollare i dati copiati, indipendentemente dalla presenza di dati in altre righe del foglio di destinazione. I riferimenti alle celle di questo esempio dovranno ovviamente essere adattati alle vostre esigenze, come pure il nome del/dei fogli (pincopallino). In verde sono i commenti: Sub CopiaIncolla() 'dichiaro la variabile Indiceriga Dim Indiceriga As Integer ActiveSheet.Range("A1:D1").Select 'seleziono le quattro celle da copiare(o 'quelle che vorrete) sul foglio attivo Selection.Copy 'copio il contenuto 'poi vado al foglio su cui devo Sheets("pincopallino").Select incollare(incollerò a 'partire dalla D2:G2) For Indiceriga = 2 To 200 'dimensiono la lunghezza in righe in cui incollare (dalla '2 alla 200) 'con il foglio di destinazione selezionato With Worksheets("pincopallino").Cells(Indiceriga, 4) 'qui dico di iniziare con la cella che parte nella '2° riga(di Indiceriga), ma nella colonna 4 (la D) If .Value = "" Then GoTo 10 ' se trovo una cella vuota nella colonna D, passo al '10: End With Next Indiceriga 'se ho trovato la cella 2 occupata (non vuota) passo alla 'successiva Exit Sub 10: 'ho trovato una cella vuota e devo ripetere le selezioni per incollare Sheets("pincopallino").Select Cells(Indiceriga, 4).Select 'seleziono la prima riga vuota trovata Colonna D Selection.PasteSpecial Paste:=xlValues, Operation:=xlNone, SkipBlanks:= _ False, Transpose:=False 'incollo solo i valori, non eventuali formule Application.CutCopyMode = False 'elimina il tratteggio delle celle copiate 'ed esco End Sub Per chi ha un minimo di conoscenza in Vba non risulterà difficile adattare le istruzioni alle proprie esigenze. La procedura lavora anche sul copia/incolla di più righe: se si fosse selezionato A1:D5, cioè tutti i dati compresi dalla cella A1 alla D5 (cinque righe), il Paste (incollaggio) sarebbe avvenuto occupando 5 righe vuote, in sequenza.
http://ennius.interfree.it/
Pagina 109
MANUALE VBA
X EXCEL
Un'altra procedura per copiare/incollare. Utilizzo: Copia/Incolla con ricerca della prima riga libera per incollare. Questa procedura è meno complessa della precedente, ed è documentata in parte su Excel. Riguarda la poprietà End Select . Riporto la spiegazione che ne dà la guida in linea: Proprietà End Restituisce un oggetto Range che rappresenta la cella alla fine dell'area contenente l'intervallo di origine. Equivale a premere FINE+ FRECCIA SU, FINE+ FRECCIA GIÙ, FINE+ FRECCIA SINISTRA o FINE+ FRECCIA DESTRA. Proprietà di tipo Range di sola lettura. espressione.End(Direction) espressione: Argomento necessario. Un'espressione che restituisce un oggetto nell'elenco Si applica a Direction : Argomento necessario di tipo XlDirection. Specifica la direzione dello spostamento. XlDirection può essere una delle seguenti costanti XlDirection. xlDown 'direzione giù xlToRight 'direzione a destra xlToLeft 'direzione a sinistra xlUp 'direzione su Esempio Questo esempio seleziona la prima cella (la B1) della colonna B nell'area contenente la cella B4. Range("B4").End(xlUp).Select se le celle fossero vuote, altrimenti se per esempio la cella B3 fosse occupata con un valore, l'istruzione avrebbe fatto fermare la selezione a questa cella. Quindi si può dire che End Select viene usata per rintracciare la prima cella occupata, data una cella di partenza e con la direzione desiderata. Nel caso che nessuna cella sia occupata, verrà selezionata l'ultima cella del foglio. (verso dx. sx, su o giù). Ed ecco invece l'istruzione usata per incollare dei dati, sfruttando la succitata proprietà End, ma per trovare la prima riga libera dove "scaricare" i dati. Supponiamo quindi di avere un foglio, che chiameremo "Archivio", nel quale vorremo "incollare" dei dati provenienti da un altro foglio. Decideremo che i dati verranno inseriti a partire dalla colonna A. In genere sceglieremo la prima cella del foglio, la A1, per iniziare a formare il nostro archivio. Solo che dovremo usare un accorgimento, per evitare che se nell'istruzione usiamo la cella A1 come inizio di selezione, non ci ritroviamo ad avere l'ultima cella del foglio come cella individuata dell'istruzione Selection.End(xlDown).Select (infatti il comando porterebbe, non trovando celle occupate, alla fine del foglio). L'accorgimento consiste nel tenere le prime due celle A1 e A2 occupate, con un qualsiasi valore, tipo un intestazione colonna, e indicare nel codice a questo punto la cella A1 come cella da selezionare per iniziare il ciclo. Poichè l'istruzione ora si fermerebbe alla cella A2, e noi invece cerchiamo una riga vuota, aggiungiamo l'istruzione "Offset" che sposta la selezione sulla cella sottostante, la A3 (vuota), e qui avviene l'incollaggio. Ripetendo il ciclo, troveremo sempre l'ultima cella e la successiva libera. Questo è il codice: Sub CopiaIncolla2() Worksheets("Arhivio").Select Range("A1").Select 'bisogna fargli trovare 2 celle occupate la A1 e la A2, 'altrimenti l'istruzione seguente porterebbe alla fine del 'foglio generando un errore per via dell'istruzione Offset 'successiva. Selection.End(xlDown).Select ActiveCell.Offset(1, 0).Select 'mi sposto alla successiva che è libera 'incollo i dati. With ActiveCell Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _ :=False, Transpose:=False End With 'esco End Sub
http://ennius.interfree.it/
Pagina 110
MANUALE VBA
X EXCEL
Ancora una procedura per Copiare/Incollare Utilizzo : Copia/Incolla dati in celle NON contigue con destinazione in celle contigue. ovvero: copia dei nuovi dati inseriti nella prima riga libera di un elenco, non tutte le celle, ed incollaggio su una tabella elenco posta su un altro foglio. La procedura si basa sulla necessità di NON copiare tutte le celle di un elenco, ma solo alcune, e di ricostruire un nuovo elenco in altra zona, con i dati inseriti in celle contigue per successivi conteggi. Il codice cerca l'ultima riga dell'elenco nel quale sono stati inseriti i nuovi dati, seleziona le celle che devono essere copiate, si sposta sul foglio di destinazione, nel quale sono state definite le celle iniziali, qui cerca la prima riga libera, e "scarica" (incolla) i dati copiati. La procedura esegue il "copia/Incolla" una cella per volta, selezionando alternativamente il foglio con la cella da copiare e quindi il foglio con la cella dove incollare, ma grazie all'istruzione : Application.ScreenUpdating = False , non si notano saltellamenti. Questa è la routine: Sub copiazza() 'I caratteri in verde, dopo l'apice, sono commenti 'e se vuoi li puoi togliere. Application.ScreenUpdating = False Worksheets("Foglio1").Select Range("A4").Select Selection.End(xlDown).Select Selection.Copy 'copio il nome (1° colonna partendo dalla B) Worksheets("Foglio2").Select ' seleziono il foglio2 Range("B5").Select Selection.End(xlDown).Select ActiveCell.Offset(1, 0).Select 'trovo la prima cella libera 'nella colonna B, la seleziono e incollo Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _ :=False, Transpose:=False '-----------------------------Worksheets("Foglio1").Select 'poi torno sul foglio1 Range("A4").Select Selection.End(xlDown).Select ActiveCell.Offset(0, 1).Select 'copio la data (2° cella 2° colonna dell'elenco) Selection.Copy Worksheets("Foglio2").Select 'torno sul foglio2 Range("B5").Select Selection.End(xlDown).Select 'vado alla cella ultima occupata ActiveCell.Offset(0, 1).Select 'seleziono la colonna accanto 'e incollo Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _ :=False, Transpose:=False '------------------------------Worksheets("Foglio1").Select Range("A4").Select Selection.End(xlDown).Select ActiveCell.Offset(0, 3).Select 'copio il primo valore (4° cella 4°colonna) Selection.Copy Worksheets("Foglio2").Select Range("B5").Select Selection.End(xlDown).Select ActiveCell.Offset(0, 2).Select Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _ :=False, Transpose:=False '-------------------------------http://ennius.interfree.it/
Pagina 111
MANUALE VBA
X EXCEL
Worksheets("Foglio1").Select Range("A4").Select Selection.End(xlDown).Select ActiveCell.Offset(0, 5).Select 'copio il secondo valore (6°cella 6°colonna) Selection.Copy Worksheets("Foglio2").Select Range("B5").Select Selection.End(xlDown).Select ActiveCell.Offset(0, 3).Select Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _ :=False, Transpose:=False '-------------------------------Worksheets("Foglio1").Select Range("A4").Select Selection.End(xlDown).Select ActiveCell.Offset(0, 8).Select 'copio il terzo valore (9° cella 9a colonna) Selection.Copy Worksheets("Foglio2").Select Range("B5").Select Selection.End(xlDown).Select ActiveCell.Offset(0, 4).Select Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _ :=False, Transpose:=False '------------------------------Worksheets("Foglio1").Select Application.CutCopyMode = False 'questo evita i trattini intorno alla/alle 'celle copiate End Sub I riferimenti alle celle di inizio elenco e di inizio destinazione si capiscono meglio se scaricherete il file allegato. la procedura, anche se appare lunga, è semplice, e si ripete tante volte quante sono le celle da copiare. Sotto due immagini, il foglio1 con l'elenco in cui si inseriscono i dati, ed il foglio2 con le destinazioni.
File consultabile e scaricabilie : cartelennius.zip 16 kb http://ennius.interfree.it/
Pagina 112
MANUALE VBA
X EXCEL
Ancora una procedura per Copiare/Incollare (07/01/03) Utilizzo : Copia/Incolla dati in celle NON contigue con destinazione in celle contigue. A differenza del metodo descritto in "Copia/Incolla 3" (vedi), in questa pagina useremo istruzioni differenti (avanzate) per ottenere lo stesso risultato, cioè la copia di dati in colonne non contigue, con incollaggio dei dati copiati in zone contigue. Presento due diversi modelli, che hanno in comune l'utilizzo di nuove istruzioni : UsedRange (proprietà) SpecialCells (Metodo) xlLastCell (Costante) Union (Metodo) Gli esempi che seguiremo saranno: Con un elenco (tabella) di dati, la copia dei dati inseriti nell'ultima riga, in Celle non contigue, e relativo incollaggio dei dati così copiati, in una zona dello stesso foglio o di un altro foglio, ma in Celle contigue, con inserimento nella zona destinazione con ricerca della prima riga libera (progressione). Sempre partendo da un elenco (tabella di dati) posti su più colonne, la copia di Colonne non contigue con incollaggio dei dati così copiati, in una zona dello stesso foglio o di un altro foglio, ma in Colonne contigue. (quindi non solo i dati presenti nell'ultima riga, ma tutti i dati presenti in una o più colonne con incollaggio nella zona di destinazione, con sostituzione in questa zona dei dati già presenti con i nuovi dati (aggiornamento totale). Vediamo intanto le spiegazioni circa le nuove istruzioni: La proprietà UsedRange. In effetti la Guida è un pò avara di chiarimenti, e comunque, per Excel, lo UsedRange è la zona che, sul foglio di lavoro, va dalla cella A1 all'ultima cella del foglio, rappresentata dall'incrocio dell'ultima riga con l'ultima colonna, in pratica tutto il foglio. E' possibile "pilotare" la zona da assegnare all' UsedRange, semplicemente indicando il riferimento ad una cella che serva da inizio zona: se usiamo quindi UsedRange con SpecialCells e la costante xlLastCell, tutto ciò che si troverà sotto e a destra di questa cella, rappresenterà l'UsedRange, purchè contenga dati, anche in celle non contigue. In realtà l'UsedRange, su un foglio vuoto, da solo non potrebbe agire (o meglio, identificherebbe SOLO la cella iniziale scelta) se non definiamo alcuni parametri: fino a quale cella si dovrà intendere la zona coperta da UsedRange, e/o quale tipo di celle comprendere. Useremo quindi il metodo SpecialCells il quale fa riferimento a tutte le celle che risponderanno al "tipo" specificato. Per definire il "tipo" useremo delle "Costanti" specifiche. Eventualmente queste costanti si possono sommare, col risultato di comprendere più "tipi". Vediamo sotto uno specchietto di queste costanti . Tipo Significato xlCellTypeNotes Celle contenenti note xlCellTypeCostants Celle contenenti costanti xlCellTypeFormulas Celle contenenti formule xlCellTypeBlanks Celle vuote xlCellTypeLastCell Ultima cella del foglio di lavoro xlCellTypeVisible Tutte le celle visibili Vediamo un esempio e relative istruzioni. E' possibile assegnare un nome a questa cella, e usare il nome come riferimento nelle istruzioni. Nell'esempio sotto, per meglio capire, useremo la cella F13 come riferimento inizio zona, ed evidenzieremo l'UsedRange, che si fermerà alla cella L22 perchè questa è l'ultima cella che contiene dei dati.
http://ennius.interfree.it/
Pagina 113
MANUALE VBA
X EXCEL
Questa l'istruzione che abbiamo usato: Sub Miazona() Set LastCell = ActiveSheet.UsedRange.SpecialCells(xlLastCell) Range(Range("F13"), LastCell).Select End Sub E' evidente che se invece che selezionare una zona, avessimo voluto pulirne le celle, avremmo adoperato un'istruzione appropriata, tipo Range(Range("F13"), LastCell).ClearContents, oppure avremmo potuto decidere il tipo di font, il grassetto, il colore celle, ecc. ecc. con un comando unico. Due precisazioni: usare una costante specifica su un foglio che non contiene il "tipo" di celle previsto dalla costante scelta, genera un errore. Quindi assicurarsi di usare la o le costanti giuste in funzione del tipo di celle che vorremo comprendere. L'altra è che se usiamo UsedRange assegnando una cella di inizio zona, e non esistono dati nelle celle sottostanti e a destra di questa cella, ma esistono dati sopra, (sia a destra che a sinistra) rispetto alla cella inizio zona, l'UsedRange cercherà la prima cella sopra e la prima cella sopra a destra occupate, e comprenderà le celle in queste zone, non sotto. Esercitatevi su un foglio per meglio capire, anche variando le costanti, con xlBlanks o xlVisible. L'ultima istruzione prevista a inizio foglio, è il metodo Union - Union consente di unire più intervalli discontinui, utile quando si voglia, per esempio, copiare range di celle non contigui. Questo metodo ha come "argomenti", i riferimenti alle celle che esso unisce. Esempio: Dim c1, c2, c3, Intervallo As Range Set c1 = Range("A1") Set c2 = Range("D1") Set c3 = Range("G1") Set Intervallo = Union(c1, c2, c3) Intervallo.Copy Ed ora passiamo agli esempi del Copia/Incolla. Per il primo modello, useremo la tabella già usata per il "Copia/incolla3". Per questo esempio, ipotizzeremo di avere una tabella con dei dati che inseriremo riga per riga, e di cui vorremo copiare solo i dati di alcune colonne (in questo caso evidenziate in giallo), MA di voler copiare solo i dati inseriti nell'ultima riga. (il perchè lo lascio decidere a voi, potrebbe essere che non tutte le righe debbano essere copiate, o un altro motivo, l'importante è vedere quali istruzioni compileremo per questa operazione.) Quindi i dati da copiare si trovano sulla stessa riga, MA in colonne discontinue. I dati poi li vorremo incollare in una zona dello stesso foglio o anche di un altro foglio, e che l'elenco che si formerà con l'incollaggio formi una nuova tabella però con colonne contigue. Questa sotto l'immagine della ipotetica tabella:
http://ennius.interfree.it/
Pagina 114
MANUALE VBA
X EXCEL
e vediamo allora le istruzioni, e relative spiegazioni: Sub incollauno() 'evita saltellamenti (scrolling) a schermo Application.ScreenUpdating = False 'dichiarazioni delle variabili come Range Dim c1, c2, c3, c4, c5, Intervallo As Range With Foglio1 Set c1 = Range("A1").End(xlDown) 'assegnazione alla variabile c1 dell'ultima cella occupata (End(xlDown)) nella colonna A a partire da A1 Set c2 = Range("B1").End(xlDown) 'c.s. per la colonna B Set c3 = Range("D1").End(xlDown) 'c.s. per la colonna D Set c4 = Range("F1").End(xlDown) 'c.s. per la colonna F Set c5 = Range("I1").End(xlDown) 'c.s. per la colonna I End With 'a questo punto sono state identificate e assegnate tutte le celle dell'ultima riga Set Intervallo = Union(c1, c2, c3, c4, c5) 'assegnazione ad Intervallo dell'UNIONE delle 'celle precedentemente assegnate alle variabili. Intervallo.Copy 'copia di Intervallo, cioè delle celle discontinue prec. assegnate Range("L1").Select 'selezione della cella dove incollare i dati copiati (sullo stesso foglio) Selection.End(xlDown).Select 'selezione dell'ultima cella occupata a partire da L1 ActiveCell.Offset(1, 0).Select 'selezione della cella sotto l'ultima occupata (che è vuota) ActiveSheet.Paste ActiveCell 'incollaggio (Paste) di Intervallo nella cella ora attiva. 'Trattandosi di cinque celle, è sufficiente selezionare una prima cella, ed Excel "scaricherà" in sequenza (cioè contigui) sulla stessa riga, i cinque dati memorizzati. 'sotto: elimina il tratteggio intorno alle celle copiate Application.CutCopyMode = False End Sub Ricordo ai lettori che la procedura per incollare, utilizzando End più Offset(1, 0), dovrà trovare le prime due celle di inizio elenco (L1 e L2) occupate o con una intestazione di campo, o qualsiasi altro valore (basta anche un punto). Vediamo ora il secondo esempio di Copia/Incolla. Sempre usando la tabella dell'esempio precedente, questa volta copieremo non più i dati delle celle dell'ultima riga, ma le intere colonne, discontinue (le gialle) qualunque sia la lunghezza raggiunta, e incolleremo i dati sul Foglio2. In questo caso, anzichè cercare nella destinazione, la prima riga libera per scaricare i dati, trattandosi ad ogni copia/incolla di trasferire TUTTE le righe delle colonne interessate, provvederemo alla cancellazione dei dati presenti in tutta la zona di destinazione (utilizzeremo UsedRange) visto che ricostruiremo un nuovo elenco aggiornato. Sul foglio2, per identificare la cella che ci servirà come inizio elenco per incollare i dati, la B6, gli assegneremo un nome, che definiremo come più ci piace (in questo esempio il nome sarà "pippo"). Sotto la tabella che forma l'elenco di destinazione:
http://ennius.interfree.it/
Pagina 115
MANUALE VBA
X EXCEL
E queste le istruzioni, da ATTIVARE con un pulsante, dal Foglio2, e spiegazioni: Sub incolladue() 'evita saltellamenti (scrolling) a schermo Application.ScreenUpdating = False 'dichiarazioni delle variabili come Range Dim c1, c2, c3, c4, c5 Intervallo As Range 'comincia l'assegnazione non più di una singola cella della colonna A ad una variabile, ma di tutte le righe che porteranno dati a partire dalla prima cella (A1) e fino all'ultima cella occupata (End), e queste sono le istruzioni da impostare, per ogni variabile (colonna): With Foglio1.Range("A1") Set c1 = Range(.Cells(1), .End(xlDown)) End With With Foglio1.Range("B1") Set c2 = Range(.Cells(1), .End(xlDown)) End With With Foglio1.Range("D1") Set c3 = Range(.Cells(1), .End(xlDown)) End With With Foglio1.Range("F1") Set c4 = Range(.Cells(1), .End(xlDown)) End With With Foglio1.Range("I1") Set c5 = Range(.Cells(1), .End(xlDown)) End With 'a questo punto sono state identificate e assegnate tutte le colonne da inizio colonna fino 'all'ultima riga occupata Set Intervallo = Union(c1, c2, c3, c4, c5) 'assegnazione ad Intervallo dell'UNIONE delle 'zone precedentemente assegnate alle variabili. 'poi viene pulito il foglio2, a partire dalla cella B6 e con UsedRange, vengono intercettate tutte le celle che porteranno valori. L'utilità di UsedRange in questo caso è evidente: per pulire un elenco non disponendo di riferimenti precisi, con UsedRange saremo sicuri che tutto ciò che sarà tra la cella B6 e la fine del foglio, se occupato da dati, sarà pulito. Set UltimaCella = ActiveSheet.UsedRange.SpecialCells(xlLastCell) Range(Range("pippo"), UltimaCella).Clear 'ora la pagina (Foglio2) è pronta per copiare/incollare i dati, con il metodo Copy http://ennius.interfree.it/
Pagina 116
MANUALE VBA
X EXCEL
applicato all'oggetto Range ' oggetto.Copy [destinazione] Intervallo.Copy ("pippo") 'volendo poi ordinare i dati per nome, useremo la seguente istruzione: With Range("pippo") .Sort Key1:=.Offset(0, 0), Order1:=xlAscending, header:=xlYes End With End Sub
http://ennius.interfree.it/
Pagina 117
MANUALE VBA
X EXCEL
Altri esempi di Copia/Incolla (veloci) Vediamo ancora qualche esempio di copia e incolla, questa volta basati su selezioni libere, o quasi. Si possono usare quando, posto un elenco su un foglio di lavoro, si voglia selezionare una zona variabile per estensione, con destinazione su altre zone dello stesso foglio o di altri fogli. Primo esempio. Definiamo con "Zona" tutta l'area compresa tra una determinata cella, che va identificata nell'istruzione, ed una cella "libera" che verrà selezionata di volta in volta, rendendola quindi Cella Attiva (ActiveCell). Tutte le celle che si troveranno comprese tra la cella fissa e la cella in quel momento attiva, saranno comprese in "Zona". Questa l'istruzione, con A1 come cella fissa ( Cells(1, 1) ): Set Zona = Range(ActiveCell, Cells(1, 1)) Ovviamente sarà possibile scegliersi il nome con il quale impostare sia l'area, sia la cella iniziale fissa. Una precisazione, lavorando con ActiveCell e quindi riferita alla cella attiva SUL FOGLIO DI LAVORO, il pulsante per l'attivazione della macro dovrà risiedere sullo stesso foglio, anche se la destinazione sarà su un foglio "distante". Secondo esempio. Anzichè lavorare con una cella che identifichi l'inizio dell'area, qui useremo una selezione completamente libera, usando appunto Selection. Potremo in questo modo selezionare liberamente qualsiasi area del nostro foglio, perchè tutta l'area selezionata sarà assegnata a "Zona". Set Zona = Selection Ricordo che una selezione può essere fatta anche in celle discontinue o in aree discontinue, usando il tasto CTRL (Control), da tenere premuto mentre eseguiamo le selezioni, PURCHE' le selezioni interessino o delle celle SULLA STESSA RIGA anche in colonne non contigue, o aree che siano della stessa dimensione anche se su colonne discontinue, ma poste sulle stesse righe.
Una volta "reperiti" i dati che vogliamo copiare, useremo il comando Copy per incollare i dati nelle celle di destinazione. Assegneremo un nome alla cella di inizio zona destinazione: questo ci consentirà, stando sul foglio attivo, non solo di incollare i dati sullo stesso foglio, ma di "mirare" a destinazioni poste su altri fogli. Chiameremo quindi "pippo" per esempio la cella L2 sullo stesso foglio, o su un altro, poco importa, e le nostre macro diventeranno: per il primo esempio: Sub Prova() Set Zona = Range(ActiveCell, Cells(1, 1)) Zona.Copy ("pippo") End Sub per il secondo esempio: Sub Prova() Set Zona = Selection Zona.Copy ("pippo") End Sub
http://ennius.interfree.it/
Pagina 118
MANUALE VBA
X EXCEL
Copia / Incolla di più campi con ricerca su un campo specifico. (04/08/03) Ancora un esercizio per copiare/incollare. In questo caso, simuliamo un elenco dati formato da più campi (colonne), e vorremo copiare tutti i dati di una stessa riga, dove uno dei campi corrisponda ad un certo criterio. Useremo un esempio classico: un indirizzario formato da 4 campi: Nominativo, indirizzo, città, telefono; l'esercizio : copiare tutti i dati dove esiste un numero di telefono. Questo esempio è adattabile ad ogni situazione, potrà essere applicato a tabelle dove un valore corrisponde ad un determinato numero, oppure ad una determinata parola, o ancora ad una determinata data, insomma, i casi si sprecano. Intanto vediamo la tabella scelta come esempio ( i dati sono di fantasia ): A B C D 1 Nominativo Indirizzo Città Telefono 2 Abbà Santino Via Crisi 13 Vengiù 0997-774411 3 Accatto Giuseppe Via Tappicchi 23 Citros 4 Barabba Secondo Via Golgota 1 Maraneia 0985-667788 5 Bicchiere Limpido Via Tavole 5 Lavas 0966-996633 6 Catullo Rocco Via Roma 25 Calabellù Ed ora vediamo come operare. Due precisazioni: il formato celle del campo "Telefono" dovrà essere impostato a "Testo" per poter ospitare numeri con lo zero iniziale. In questo stesso campo, se non esiste il numero NON dovrà esserci comunque nessun valore (come zero, un trattino, ecc.) visto che useremo come chiave di selezione tutte le celle nel campo Telefono che saranno vuote ( "" ). Useremo un ciclo For Each...Next che controlli tutte le celle, nel campo "Telefono", e per fornire i riferimenti del Range su cui operare (cioè da quante righe è composto l'elenco), useremo prendere gli estremi nella colonna A (Nominativo) con End, visto che questo campo sicuramente contiene dati, a differenza del campo telefono che può avere celle vuote. Poi con Offset controlliamo se esiste un numero di telefono, in caso positivo, selezioneremo l'intera riga copiandola, ed incollandola in un altro foglio, su cui useremo un ciclo Wend..While per trovare la prima cella libera dove incollare. Vediamo le istruzioni e le spiegazioni (in verde) Sub Aricopia() Application.ScreenUpdating = False 'serve per evitare i saltellamenti a schermo Dim CEL As Object 'dichiariamo CEL come "Oggetto" Set zona = Range(Range("A2"), Range("A2").End(xlDown)) 'con "zona" reperiamo tutto 'il range di celle dalla cella A2 (in A1 c'è "Nominativo") fino all'ultima cella occupata nella 'colonna A For Each CEL In zona 'per ogni CEL( Oggetto cella) nel Range "zona" If CEL.Offset(0, 3) "" Then 'se la cella 3 righe a desta (3) stessa riga (0) rispetto alla CEL in quel momento "spazzolata", è diversa da vuoto (quindi la cella che contiene il 'numero di telefono, lo contiene), allora Range(CEL, CEL.Offset(0, 3)).Select 'si selezionano tutte le celle della stessa riga che 'vanno dalla CEL attiva fino alla cella Telefono Selection.Copy 'si copia in memoria la selezione Worksheets("Foglio2").Select 'quindi ci spostiamo sul foglio destinazione Dim iRow As Integer 'iniziamo il ciclo per la ricerca della prima cella vuota, a partire dalla iRow = 2 'riga due While Cells(iRow, 1).Value "" 'fino a che la cella riga iRow, colonna 1 è occupata iRow = iRow + 1 'si scala di riga incrementando di 1 il numero di riga Wend 'quando si trova una cella libera Cells(iRow, 1).Select 'la selezioniamo ed incolliamo ciò che è stato copiato Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _ :=False, Transpose:=False End If Worksheets("Foglio1").Select 'ritorniamo sul foglio di partenza Next 'e si continua il ciclo sulla cella successiva fino alla fine di "zona" http://ennius.interfree.it/
Pagina 119
MANUALE VBA
X EXCEL
Application.CutCopyMode = False 'elimina il tratteggio intorno alle celle copiate End Sub Spero di essere stato chiaro.
http://ennius.interfree.it/
Pagina 120
MANUALE VBA
X EXCEL
Copia / Incolla di più campi con ricerca su un campo specifico. (04/08/03) Ancora un esercizio per copiare/incollare. In questo caso, simuliamo un elenco dati formato da più campi (colonne), e vorremo copiare tutti i dati di una stessa riga, dove uno dei campi corrisponda ad un certo criterio. Useremo un esempio classico: un indirizzario formato da 4 campi: Nominativo, indirizzo, città, telefono; l'esercizio : copiare tutti i dati dove esiste un numero di telefono. Questo esempio è adattabile ad ogni situazione, potrà essere applicato a tabelle dove un valore corrisponde ad un determinato numero, oppure ad una determinata parola, o ancora ad una determinata data, insomma, i casi si sprecano. Intanto vediamo la tabella scelta come esempio ( i dati sono di fantasia ): A B C D 1 Nominativo Indirizzo Città Telefono 2 Abbà Santino Via Crisi 13 Vengiù 0997-774411 3 Accatto Giuseppe Via Tappicchi 23 Citros 4 Barabba Secondo Via Golgota 1 Maraneia 0985-667788 5 Bicchiere Limpido Via Tavole 5 Lavas 0966-996633 6 Catullo Rocco Via Roma 25 Calabellù Ed ora vediamo come operare. Due precisazioni: il formato celle del campo "Telefono" dovrà essere impostato a "Testo" per poter ospitare numeri con lo zero iniziale. In questo stesso campo, se non esiste il numero NON dovrà esserci comunque nessun valore (come zero, un trattino, ecc.) visto che useremo come chiave di selezione tutte le celle nel campo Telefono che saranno vuote ( "" ). Useremo un ciclo For Each...Next che controlli tutte le celle, nel campo "Telefono", e per fornire i riferimenti del Range su cui operare (cioè da quante righe è composto l'elenco), useremo prendere gli estremi nella colonna A (Nominativo) con End, visto che questo campo sicuramente contiene dati, a differenza del campo telefono che può avere celle vuote. Poi con Offset controlliamo se esiste un numero di telefono, in caso positivo, selezioneremo l'intera riga copiandola, ed incollandola in un altro foglio, su cui useremo un ciclo Wend..While per trovare la prima cella libera dove incollare. Vediamo le istruzioni e le spiegazioni (in verde) Sub Aricopia() Application.ScreenUpdating = False 'serve per evitare i saltellamenti a schermo Dim CEL As Object 'dichiariamo CEL come "Oggetto" Set zona = Range(Range("A2"), Range("A2").End(xlDown)) 'con "zona" reperiamo tutto 'il range di celle dalla cella A2 (in A1 c'è "Nominativo") fino all'ultima cella occupata nella 'colonna A For Each CEL In zona 'per ogni CEL( Oggetto cella) nel Range "zona" If CEL.Offset(0, 3) "" Then 'se la cella 3 righe a desta (3) stessa riga (0) rispetto alla CEL in quel momento "spazzolata", è diversa da vuoto (quindi la cella che contiene il 'numero di telefono, lo contiene), allora Range(CEL, CEL.Offset(0, 3)).Select 'si selezionano tutte le celle della stessa riga che 'vanno dalla CEL attiva fino alla cella Telefono Selection.Copy 'si copia in memoria la selezione Worksheets("Foglio2").Select 'quindi ci spostiamo sul foglio destinazione Dim iRow As Integer 'iniziamo il ciclo per la ricerca della prima cella vuota, a partire dalla iRow = 2 'riga due While Cells(iRow, 1).Value "" 'fino a che la cella riga iRow, colonna 1 è occupata iRow = iRow + 1 'si scala di riga incrementando di 1 il numero di riga Wend 'quando si trova una cella libera Cells(iRow, 1).Select 'la selezioniamo ed incolliamo ciò che è stato copiato Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _ :=False, Transpose:=False End If Worksheets("Foglio1").Select 'ritorniamo sul foglio di partenza Next 'e si continua il ciclo sulla cella successiva fino alla fine di "zona" http://ennius.interfree.it/
Pagina 121
MANUALE VBA
X EXCEL
Application.CutCopyMode = False 'elimina il tratteggio intorno alle celle copiate End Sub Spero di essere stato chiaro.
http://ennius.interfree.it/
Pagina 122
MANUALE VBA
X EXCEL
Copia / Incolla di più campi con ricerca su un campo specifico. (04/08/03) Ancora un esercizio per copiare/incollare. In questo caso, simuliamo un elenco dati formato da più campi (colonne), e vorremo copiare tutti i dati di una stessa riga, dove uno dei campi corrisponda ad un certo criterio. Useremo un esempio classico: un indirizzario formato da 4 campi: Nominativo, indirizzo, città, telefono; l'esercizio : copiare tutti i dati dove esiste un numero di telefono. Questo esempio è adattabile ad ogni situazione, potrà essere applicato a tabelle dove un valore corrisponde ad un determinato numero, oppure ad una determinata parola, o ancora ad una determinata data, insomma, i casi si sprecano. Intanto vediamo la tabella scelta come esempio ( i dati sono di fantasia ): A B C D 1 Nominativo Indirizzo Città Telefono 2 Abbà Santino Via Crisi 13 Vengiù 0997-774411 3 Accatto Giuseppe Via Tappicchi 23 Citros 4 Barabba Secondo Via Golgota 1 Maraneia 0985-667788 5 Bicchiere Limpido Via Tavole 5 Lavas 0966-996633 6 Catullo Rocco Via Roma 25 Calabellù Ed ora vediamo come operare. Due precisazioni: il formato celle del campo "Telefono" dovrà essere impostato a "Testo" per poter ospitare numeri con lo zero iniziale. In questo stesso campo, se non esiste il numero NON dovrà esserci comunque nessun valore (come zero, un trattino, ecc.) visto che useremo come chiave di selezione tutte le celle nel campo Telefono che saranno vuote ( "" ). Useremo un ciclo For Each...Next che controlli tutte le celle, nel campo "Telefono", e per fornire i riferimenti del Range su cui operare (cioè da quante righe è composto l'elenco), useremo prendere gli estremi nella colonna A (Nominativo) con End, visto che questo campo sicuramente contiene dati, a differenza del campo telefono che può avere celle vuote. Poi con Offset controlliamo se esiste un numero di telefono, in caso positivo, selezioneremo l'intera riga copiandola, ed incollandola in un altro foglio, su cui useremo un ciclo Wend..While per trovare la prima cella libera dove incollare. Vediamo le istruzioni e le spiegazioni (in verde) Sub Aricopia() Application.ScreenUpdating = False 'serve per evitare i saltellamenti a schermo Dim CEL As Object 'dichiariamo CEL come "Oggetto" Set zona = Range(Range("A2"), Range("A2").End(xlDown)) 'con "zona" reperiamo tutto 'il range di celle dalla cella A2 (in A1 c'è "Nominativo") fino all'ultima cella occupata nella 'colonna A For Each CEL In zona 'per ogni CEL( Oggetto cella) nel Range "zona" If CEL.Offset(0, 3) "" Then 'se la cella 3 righe a desta (3) stessa riga (0) rispetto alla CEL in quel momento "spazzolata", è diversa da vuoto (quindi la cella che contiene il 'numero di telefono, lo contiene), allora Range(CEL, CEL.Offset(0, 3)).Select 'si selezionano tutte le celle della stessa riga che 'vanno dalla CEL attiva fino alla cella Telefono Selection.Copy 'si copia in memoria la selezione Worksheets("Foglio2").Select 'quindi ci spostiamo sul foglio destinazione Dim iRow As Integer 'iniziamo il ciclo per la ricerca della prima cella vuota, a partire dalla iRow = 2 'riga due While Cells(iRow, 1).Value "" 'fino a che la cella riga iRow, colonna 1 è occupata iRow = iRow + 1 'si scala di riga incrementando di 1 il numero di riga Wend 'quando si trova una cella libera Cells(iRow, 1).Select 'la selezioniamo ed incolliamo ciò che è stato copiato Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _ :=False, Transpose:=False End If Worksheets("Foglio1").Select 'ritorniamo sul foglio di partenza Next 'e si continua il ciclo sulla cella successiva fino alla fine di "zona" http://ennius.interfree.it/
Pagina 123
MANUALE VBA
X EXCEL
Application.CutCopyMode = False 'elimina il tratteggio intorno alle celle copiate End Sub Spero di essere stato chiaro.
http://ennius.interfree.it/
Pagina 124
MANUALE VBA
X EXCEL
Controllo dei costi delle chiamate telefoniche. In pratica: trattasi di ricerca di un dato in una colonna dati, con restituzione in altra zona, del dato trovato e dati a questo correlati. La ricerca avviene con inserimento parziale del valore da cercare. Nella zona destinazione cerchiamo la prima cella libera nella colonna per accodare i dati trovati; un'altro esercizio sul copia/incolla. In seguito ad una richiesta ho realizzato una semplice esercitazione che propongo ad altri "pellegrini". Premetto che si tratta ancora di rintracciare un dato in un elenco di dati e una volta trovato, di copiare i dati correlati in modo da formare un riepilogo che consenta di registrare i relativi costi. Il problema: come identificare dal riepilogo che la Telecom invia con la bolletta, i numeri di telefono che sono stati chiamati dal telefono di casa, visto che nel succitato riepilogo i numeri, in ottemperanza alla legge sulla Privacy, vengono riportati con il numero incompleto seguito da cancelletti (###) per esempio: 33879854###. Ovviamente questo programma proposto, non è un mago, e quindi non potrà mai risalire a rintracciare i numeri mancanti indicati con ###, ma può rintracciare, dato solo il numero senza cancelletti (33879854) quale sia il numero completo presente in un elenco o database che dir si voglia, dove avremo preventivamente inserito tutti i numeri che formano la nostra agenda telefonica. Avremo cura di associare al numero reale anche un Nominativo in modo che, una volta trovato il numero, sia possibile copiare in una tabella all'uopo destinata, i dati relativi al numero (il nome), inserendo a mano il numero degli scatti e relativo costo, per ottenere sia il totale di quanto è costata quella telefonata, sia un totale generale. Le procedure vba seguite, sono state già presentate in questa sezione in diversi articoli, quindi per le spiegazioni provate a consultare i vari copia/incolla e i Database in particolare il "database con spiegazioni", dal quale ho ripetuto il ciclo di ricerca, che trova un dato anche se si fornisce solo una parte di esso. In pratica succede questo: attraverso una InputBox, viene richiesto di inserire il numero da trovare, SENZA cancelletti, vedi foto sotto (le colonne in giallo sono quelle del database, in verde e arancio a zona dove verranno incollati i dati)
Una volta inserito il numero senza cancelletti e premuto Ok, se la ricerca trova un numero in cui figura la sequenza di numeri inserita, avvisa con una domanda se vogliamo registrare i dati relativi al numero esatto e al nome associato. Abbiamo inserito il numero 34897 e infatti viene trovato il 348974562, vedi foto sotto:
Confermando con Si, avremo la copia dei dati Numero/Nome nella zona di destinazione, con ricerca della prima riga libera, ed evidenzazione della cella dove dovremo inserire il numero degli scatti e il costo scatto, vedi foto sotto
http://ennius.interfree.it/
Pagina 125
MANUALE VBA
X EXCEL
Precisazioni: le colonne A, B, D, E sono formattate a "Testo" per consentire l'uso di numeri con lo zero iniziale. Nella colonna H in H3 una semplice moltiplicazione F3*G3 per avere il totale spesa (a seguire nelle celle sottostanti). Sarà possibile poi possibile applicare un filtro ai nomi dei chiamati per avere un parziale dei costi relativi al nome scelto, o ancora "Estrarre" da questo secondo elenco tutti i dati corrispondenti ad un certo nome e formarsi altre tabelle, ecc. ecc. Questa la procedura associata al pulsante "Cerca Numero": Sub cercaNumero() With Worksheets(1).Range("A2:A150") Dim X, messaggio, titolo titolo = "Ricerca Numero Telefonico" messaggio = "Inserisci il Numero che vuoi cercare" X = InputBox(messaggio, titolo) If X = "" Then Exit Sub 'se non scrivo niente nella finestra esco dalla routine Set C = .Find(X, LookIn:=xlValues) ', LookAt:=xlWhole If Not C Is Nothing Then firstAddress = C.Address Do C.Cells.Select Y = C.Value 'prendo il numero di telefono Z = C.Offset(0, 1).Value 'prendo il nome a lato 'compongo il messaggio di avviso e conferma si/no irisposta = MsgBox("Trovato " & Y & " a nome " & Z & ". Vuoi registrare ?", vbYesNo) If irisposta = vbYes Then 'se rispondo si allora GoTo 10 'esco dal ciclo e registro End If Set C = .FindNext(C) Loop While Not C Is Nothing And C.Address firstAddress Else MsgBox "Numero non Trovato" End If 10: 'ho trovato il numero e identifico con "zona" le due celle che voglio copiare Set zona = Range(ActiveCell, ActiveCell.Offset(0, 1)) zona.Copy Range("D1").End(xlDown).Select 'cerco l'ultima cella occupata partendo da D1 ActiveCell.Offset(1, 0).Select 'seleziono la riga libera sotto l'ultima occupata ActiveSheet.Paste Destination:=ActiveCell 'Incollo i dati copiati con zona.Copy ActiveCell.Offset(0, 2).Select 'seleziono la cella corrispondente al N° scatti e scriverò il 'numero degli scatti letti sulla bolletta. End With Application.CutCopyMode = False End Sub File Scaricabile e consultabile :
http://ennius.interfree.it/
CostiTelefono2000.zip
14 Kb
Pagina 126
MANUALE VBA
X EXCEL
Facciamo un orologio sul Foglio di lavoro. (25/05/03) A volte abbiamo la necessità di conoscere che ore sono allorchè lavoriamo sui fogli di Excel. Possiamo usare la funzione di Excel =ADESSO() inserita in una cella, che ci restituisce la data e l'ora di sistema. L'orario non scorre però come in un orologio, ma rimane fisso al momento in cui abbiamo aperto il foglio, e si aggiorna soltanto se inseriamo o modifichiamo dei valori sul foglio di lavoro, e l'aggiornamento comunque non avviene nella scala dei minuti se effettuiamo una variazione in una cella all'interno del minuto dell'orario attualmente presente nella cella con la funzione. Non è proprio ciò che possiamo considerare un : "orologio". Sfruttando la funzione vba "Timer", è però possibile usare una routine "autoinnescante" che, trascorso un determinato periodo di tempo, che decideremo noi, riattivi la funzione =ADESSO() (in inglese: NOW), generando l'aggiornamento dell'orario. Attenzione: la macro qui presentata può provocare effetti indesiderati disabilitando alcune barre dei menù. Prima di deciderne un eventuale utilizzo, provatela su file non importanti. Creiamo quindi una macro che inseriremo in un modulo e potremo attivare "l'orologio" associando la macro ad un pulsante, ma meglio ancora potremo sfruttare un evento che per tutti noi è quasi impossibile non generare: il cambio di selezione di una cella sul foglio di lavoro; in questo modo attiveremo la macro ad ogni operazione che eseguiamo sul foglio di lavoro. Non è necessario riscrivere nel Worksheet_SelectionChange del foglio di lavoro scelto tutte le istruzioni, ma sarà sufficiente richiamare il nome della macro, così: Private Sub Worksheet_SelectionChange(ByVal Target As Range) Oriolo 'nome della macro End Sub Un avvertimento: poichè una volta avviata la macro, questa continua a girare restando sempre attiva, se avremo bisogno di compilare altre istruzioni vba, sarà necessario "stoppare" la macro stessa, selezionando la routine e cliccando il pulsantino quadrato blu che troviamo nel menù dell'editor di visual basic, che corrisponde al comando "Ripristina", oppure dal menù Esegui/Ripristina. Sarà comunque possibile eseguire modifiche sul foglio di lavoro anche con la macro avviata, come pure lanciare altre macro precedentemente compilate; questo perchè nelle istruzioni sotto riportate, usiamo la funzione vba "DoEvents" che sospende l'esecuzione in modo che il sistema operativo possa elaborare altri eventi. Vediamo le istruzioni: nell'esempio uso la cella A1, formato cella = Ora, come cella Orologio: (tenere presente che se avviamo la macro tramite pulsante, (mettiamo dal Foglio1), la stessa, restando attiva, sarà efficace in tutte le celle A1 di tutti i fogli della cartella, basterà spostarsi su un'altro foglio, per veder comparire in A1 l'orologio; lasciare quindi disponibile (non usare per altre istruzioni o dati) la cella che deciderete di usare come orologio); la macro attivata da pulsante si fermerà soltanto se in una cella clicchiamo per inserire una formula, mentre sul foglio di cui sfruttiamo l'evento SelectionChange, selezionando appunto una qualsiasi cella, la riattiviamo: Sub Oriolo() Dim PauseTime, Start, Finish, TotalTime 'dichiarazione variabili impiegate Range("A1").Value = Now 'assegnazione alla cella A1 della funzione Adesso() Range("A1") = Format(Range("A1"), "h.m.ss") 'impostiamo il formato nella cella A1 10: PauseTime = 3 ' Imposta la durata della pausa, espressa in secondi Start = Timer ' Imposta l'ora di inizio e attiva il Timer. Do While Timer < Start + PauseTime 'fintantochè il valore del Timer è inferiore al valore 'dato dalla somma del valore iniziale del Timer (preso con la variabile Start) più il valore 'rappresentato dalla variabile PauseTime DoEvents ' Passa il controllo ad altri processi. Loop ' continua a far girare il Timer fino a che il suo valore è uguale a Start + PauseTime, 'allora passa alla seguente riga: Finish = Timer ' Imposta l'ora di fine della pausa. TotalTime = Finish - Start ' Calcola il tempo totale. 'sotto: ora riportatiamo in A1 il valore di Adesso() ed otteniamo quindi l'aggiornamento. Range("A1").Value = Now Range("A1") = Format(Range("A1"), "h.m.ss") 'impostiamo il formato nella cella A1. 'sotto: poichè a questo punto la routine finirebbe con End Sub, e allora addio http://ennius.interfree.it/
Pagina 127
MANUALE VBA
X EXCEL
orologio, 'con l'istruzione GoTo 10 mandiamo il codice all'indice riga 10 riattivando le istruzioni 'relative al timer: GoTo 10 End Sub Suggerimento: non consiglio di scendere sotto i tre secondi come PauseTime, pena il rischio di non lasciare spazio al sistema di reagire ad altri eventi, meglio sarebbe impostarlo ogni 5 secondi, l'effetto "orologio" resta comunque valido. (routine provata solo sul Excel XP)
http://ennius.interfree.it/
Pagina 128
MANUALE VBA
X EXCEL
Creare un elenco dei nomi dei files contenuti in una cartella. Spesso avrei trovato utile poter avere un elenco di tutti i file contenuti in una cartella, ma Windows non consente una stampa di questo tipo. Questa necessità può scaturire dal voler conservare un elenco dei files presenti in una nostra cartella, sia per verificare eventuali discordanze future, oppure per tenere un elenco di file appartenenti ad una specifica categoria, per esempio alle immagini, e quindi con estensione .Jpg oppure .Bmp, ecc.ecc. Per questo motivo presento una routine che ha il compito di creare un elenco in formato testo (txt) che riporterà il nome dei files di una eventuale estensione, oppure tutti, contenuti all'interno di una cartella o sottocartella del nostro hard-disk. Premetto che mi solo limitato a preimpostare il percorso dell'unità su cui cercare e scrivere, nell'unità C:\ - Volendo potrete modificare le istruzioni in modo che anzichè digitare solo il nome della cartella, possiate digitare un percorso completo, ampliando la possibilità di scrivere o reperire cartelle con relativi files, su qualunque unità del vostro computer. La routine si basa su quattro variabili ottenute con quattro InputBox. Le variabili memorizzano rispettivamente: Il nome della cartella in cui salvare il file di testo che formerà l'elenco Il nome con il quale salvare il file di testo Il nome della cartella di cui vogliamo l'elenco dei files ivi contenuti I nome del file di cui vogliamo registrare il nome, oppure una combinazione di caratteri jolli per avere l'elenco di tutti i file con una certa estensione ( *.xls - opp. *.jpg ecc.ecc.), oppure la combinazione (*.* )per avere l'elenco di tutti i files e sottocartelle presenti. Ho inserito un'istruzione che controlla se esiste la cartella di destinazione, in caso non esista, crea la cartella. Queste le istruzioni : Sub Creaelenco() On Error Resume Next 'chiedo in quale cartella voglio salvare il file di testo Dim mival, mess, tito mess = "" & vbCr & vbCr & _ "Scrivi in quale cartella" & vbCr & _ "salvare il file su C:\ " & vbCr & _ "NON scegliere sottocartelle SE" _ & " inesistenti !" tito = "Inserisci Nome Cartella" mival = InputBox(mess, tito) ' primo vettore che prende il nome della cartella dato tramite 'la inputbox x = "C:\" + mival + "\" 'completo mival anteponendo l'unità C:\ e facendolo seguire dalla 'barra \ separatrice If x = "C:\" + "" + "\" Then Exit Sub 'se non metto il nome nella inputbox e quindi mival è 'vuoto (""), esco dalla routine 'ora chiedo con quale nome salvare il file di testo attraverso una seconda inputbox Dim mytesto, messa, titolo messa = "Inserisci il nome per il file" titolo = "" & vbCr & vbCr & _ "Ora scrivi il nome con cui salvare il file" mytesto = InputBox(titolo, messa) + ".txt" 'con mytesto prendo il nome per salvare il file If mytesto = "" + ".txt" Then Exit Sub 'se mytesto è vuoto, esco dalla routine 'inizia il dimensionamente per l'oggetto FileSystemObject Dim fso, f Set fso = CreateObject("Scripting.FileSystemObject") 'sotto: eseguo il controllo per vedere se in nome contenuto in mival è già una cartella 'esistente If fso.FolderExists(x) Then GoTo 10 'se la cartella esiste, passo a 10, e salto la creazione Dim irisposta As Integer 'pongo la domanda se si vuol creare una nuova cartella nel caso 'non esista la cartella richiesta irisposta = MsgBox("La cartella non esiste, vuoi crearla?", vbYesNo) If irisposta = vbYes Then ' se la risposta sarà SI Set f = fso.CreateFolder(x) 'allora creo la cartella (x è il nome cartella completo di path) Else 'altrimenti, se rispondo NO http://ennius.interfree.it/
Pagina 129
MANUALE VBA
X EXCEL
Exit Sub 'esco dalla routine End If 10: 'sotto: inizia l'istruzione per l'apertura di un file di testo Open x + mytesto For Output As #1 'Crea un nuovo file di testo 'inizia la richiesta della cartella dove leggere i files per ottenere l'elenco loro nomi Dim MyPath, MioFile titolo = "Inserisci Cartella di provenienza" messa = "" & vbCr & vbCr & _ "Ora scrivi di quale cartella vuoi creare" _ & " l'elenco dei files" MyPath = "C:\" + InputBox(titolo, messa) 'MyPath sarà formato dal nome digitato nella inputbox, la terza, con già anteposta l'unità C:\ If MyPath = "C:\" + "" Then Exit Sub 'se MyPath sarà vuoto ("") esco dalla routine 'ora richiedo l'estensione dei files di cui voglio l'elenco messa = "Inserisci Nome file di provenienza" titolo = "" & vbCr & vbCr & _ "Ora scrivi il nome del file o l'estensione." & vbCr & _ " Se vuoi tutti i file della cartella" _ & " puoi scrivere i caratteri jolli *.*" MioFile = MyPath + "\" + InputBox(titolo, messa) 'con la quarta inputbox prendo 'l'estensione del file o tutti i file o un solo file If MioFile = MyPath + "\" + "" Then Exit Sub 'se non fornisco l'estensione, esco dalla 'routine MyName = Dir(MioFile, vbDirectory) 'inizia il ciclo che cerca tutti i file con l'estensione indicata all'interno della cartella voluta Do While MyName "" 'fino a che la cartella è alla fine (vuota) 'Inserisce nel file txt il nomifiles della cartella specificata Print #1, MyName; "" MyName = Dir Loop MyError = CVErr(32767) Close #1 'Chiude il file di testo MsgBox "L'elenco File " & mytesto & " è stato completato" Set fso = Nothing Resume End Sub Come ho detto, è possibile modificare le istruzioni facendo in modo da poter cercare non solo sull'unità C:\, ma in qualunque unità del nostro computer, i file di cui vorremo un elenco, sarà sufficiente eliminare le preimpostazioni che determinano l'assegnazione dell'unità, avendo però l'accortezza di scrivere noi il percorso completo della cartella di provenienza dati. Facendo un esempio solo sulla provenienza, si può modificare la riga: MyPath = "C:\" + InputBox(titolo, messa) con MyPath = InputBox(titolo, messa) e nella finestra (inputbox) che ci porrà la domanda, scriveremo il percorso della cartella di cui fare l'elenco files, completo. per esempio D:\Immagini Spero di essere stato chiaro ma soprattutto di esservi stato d'aiuto. Buon lavoro.
http://ennius.interfree.it/
Pagina 130
MANUALE VBA
X EXCEL
Trasferire dati di TextBox di UserForm a celle del foglio di lavoro Ancora un esempio che mostra come trasferire dei dati introdotti in TextBox presenti su una UserForm, in modo da formare una tabella dati sul foglio di lavoro. E' una condizione operativa che viene usata spesso nei nostri lavori e quindi vediamo come sia possibile usare delle istruzioni che si possono applicare, una volta per tutte, in queste occasioni. Le condizioni : da una UserForm che useremo come maschera di introduzione dati che inseriremo in apposite TextBox, dovremo trasferire questi dati sul foglio di lavoro, partendo da una determinata riga (che sceglieremo noi via codice), e scalando di una riga ad ogni introduzione. I dati saranno inseriti in altrettante celle, sulla stessa riga, per quanto saranno le TextBox. Non ci occuperemo del "Formato dati" nè del "Tipo di dati" che trasferiremo (peraltro già descritti su altri paragrafi di questa sezione), ma di reperire l'"indice riga" per identificare su quale riga copiare i dati, e come identificare le colonne. Questo esempio lo imposto su quattro TexBox, ma potrà essere usato su quante TextBox vorremo. Ultima condizione: vogliamo creare un numero riga progressivo, che ci indichi il numero dei record (righe) presenti nella tabella Private Sub CommandButton1_Click() 'queste istruzioni sotto cercano la prima riga in cui la prima cella (cioè la cella della 'colonna A sia vuota, a partire dalla riga 2 (iRow = 2). La colonna è identificata dal 'numero 1 nell'istruzione While Cells(iRow, 1). Se avessimo voluto creare la tabella a 'partire dalla colonna C, avremmo dovuto scrivere 3, cioè While Cells(iRow, 3) Dim iRow As Integer iRow = 2 'si comincia dalla riga 2 While Cells(iRow, 1).Value "" 'se la cella contiene dati, si passa alla successiva iRow = iRow + 1 Wend 'trovata la riga con la cella vuota, inserisco i dati contenuti nelle textbox, trovando per 'ogni textbox la colonna (iRow 1,2,3,4,5). Poi per incrementare il numero progressivo, gli 'faccio leggere il numero presente nella riga sopra (con Offset), stessa colonna, ed 'aggiungo 1. In totale quindi useremo 5 celle sul foglio di lavoro, la prima ci servirà per il 'numeratore, e le altre quattro per ospitare i dati delle quattro TextBox. Cells(iRow, 1) = Cells(iRow, 1).Offset(-1, 0) + 1 Cells(iRow, 2) = TextBox1 Cells(iRow, 3) = TextBox2 Cells(iRow, 4) = TextBox3 Cells(iRow, 5) = TextBox4 'poi dopo che ho copiato i dati sul foglio devo pulire le textbox per inserire nuovi dati TextBox1 = "" TextBox2 = "" TextBox3 = "" TextBox4 = "" End Sub Con questa routine credo di avere chiarito una procedura standard da seguire quando vorremo inserire dati da una maschera di introduzione dati a celle sul foglio di lavoro. E' possibile ovviamente, lavorando su un foglio, inserire dati su un altro foglio; sarà sufficiente indicare nelle istruzioni su quale foglio "scaricare" i dati, selezionando prima il foglio, e ritornando poi al foglio di partenza . Usando Application.ScreenUpdating=False, eviteremo i saltellamenti a video. Questa la modifica: Private Sub CommandButton1_Click() Application.ScreenUpdating = False Sheets(2).Select 'si seleziona un'altro foglio Dim iRow As Integer iRow = 2 While Cells(iRow, 1).Value "" iRow = iRow + 1 Wend Cells(iRow, 1) = Cells(iRow, 1).Offset(-1, 0) + 1 http://ennius.interfree.it/
Pagina 131
MANUALE VBA
X EXCEL
Cells(iRow, 2) = TextBox1 Cells(iRow, 3) = TextBox2 Cells(iRow, 4) = TextBox3 Cells(iRow, 5) = TextBox4 TextBox1 = "" TextBox2 = "" TextBox3 = "" TextBox4 = "" Sheets(1).Select 'si ritorna al foglio di partenza End Sub Se invece vogliamo vedere come compilare delle istruzioni che sfruttino un ciclo For...Next sugli insiemi, potremo sostituire le righe che identificano le TextBox e le Celle, in questa maniera (scrivo solo le istruzioni da modificare): '...omissis Wend 'da questa riga si sostituiscono le precedenti istruzioni con: 'la prima cella non la inseriamo nel ciclo perchè dispone un'istruzione singola, non comune 'alle altre celle (contatore) Cells(iRow, 1) = Cells(iRow, 1).Offset(-1, 0) + 1 'iniziamo il ciclo dalla seconda cella ( n = 2 To..) a cui assegniamo però la Textbox1, quindi l'indice 'della textbox sarà uguale al valore di n - 1 (n meno uno) For n = 2 To 5 Cells(iRow, n) = UserForm1.Controls("TextBox" & n - 1).Text 'con lo stesso concetto dell'indice per le textbox, e di seguito nello stesso ciclo, facciamo 'pulire le textbox UserForm1.Controls("TextBox" & n - 1) = "" Next Sheets(1).Select 'si ritorna al foglio di partenza End Sub Buon lavoro.
http://ennius.interfree.it/
Pagina 132
MANUALE VBA
X EXCEL
Realizzazione di un Database "SelfMade" In molti mi hanno scritto chiedendomi delucidazioni circa il codice utilizzato nel programma "Gestione database" presente in questa sezione. Non potendoli accontentare, ho realizzato questo programma di cui posso fornire routine e codice, con relative spiegazioni. Premetto che le procedure impiegate, sono utilizzabili in tutti quei casi dove sia necessario gestire elenchi di dati, anche lunghi, come indirizzari, gestione articoli magazzino, elenchi fatture, ecc. ecc. Il programma viene esemplificato comunque per un "indirizzario", ma chiunque potrà adattarlo alle proprie esigenze. Chi è interessato, è pregato di leggersi TUTTE le istruzioni. Il programma si basa su dati inseriti su un foglio di lavoro, su cui dovrà essere costruito lo "scheletro", cioè lo schema che conterrà i dati stessi. Come ogni database che si rispetti, questi dati saranno organizzati in righe e colonne. Le colonne conterranno i "campi" del database con relative "intestazioni di colonna", cioè i nomi dei "campi" (N°, Nominativo, Indirizzo, Città, ecc.ecc.), e le righe saranno i "record", cioè la zona dove fisicamente saranno inseriti i dati, ognuno nel proprio "campo" (colonna) di pertinenza. Molti di voi sanno già cosa si intende per "Database", quindi proseguiamo col vedere come ho impostato il lavoro. Il programma si apre sul Foglio1 dove è stata predisposta la tabella che conterrà i dati. Nell'esempio mi sono limitato ad inserire 6 campi, il N° progressivo dei record, Nominativo, Indirizzo, Città, Telefono, Cellulare, E-mail. I dati potranno essere inseriti sul foglio, direttamente, a mano, ma lo scopo era quello di usare una maschera di introduzione, modifica, ricerca dei dati presenti nell'elenco sul foglio1. Per questo, sul foglio, è presente un pulsante per l'apertura della maschera, una Userform dove sono stati inseriti gli "oggetti" necessari al progetto: 2 OptionButton, per la selezione del metodo di ricerca. 7 TextBox che servono per la gestione della ricerca, e dell'immissione, controllo, modifica dei dati. 7 CommandButton per l'attivazione delle routine necessarie al progetto.
La tabella inizia dalla riga 2 dove sono inseriti i nomi dei campi; questo perchè nelle routine utilizzo il metodo End Select, che partendo dall'alto per trovare la prima riga libera, deve trovare due celle occupate, altrimenti si precipiterebbe a fine pagina se le riga successiva alla prima fosse vuota (quando l'elenco è da iniziare, la prima cella sotto l'intestazione di campo è vuota), per cui nella riga 1, faccio inserire il segno meno ( - ) e questo serve a far trovare le prime due celle occupate (la riga1 col meno, la riga2 col nome del campo). Il formato celle di tutta la tabella è stato impostato a "Testo", in questo modo i numeri di telefono che partono con lo zero, verranno registrati correttamente (Excel si rifiuta di accettare numeri che inizino con zero, a meno che non gli si dica che anzichè un numero, si sta scrivendo del testo). Inoltre l'elenco l'ho impostato per contenere 150 record di dati. Chiunque potrà "allungare" a piacere il range operativo, modificando i riferimenti nelle istruzioni. La colonna A, quella che contiene il N° progressivo del record, viene inizializzata a mano, sfruttando la capacità di Excel di completare le "serie": si scrive in A3 il numero 1, in A4 il numero 2, si selezionano entrambe le caselle, ci si sposta nell'angolo in basso a destra della casella A4, e quando compare il puntatore del http://ennius.interfree.it/
Pagina 133
MANUALE VBA
X EXCEL
mouse fatto come una piccola croce nera, si clicca sinistro e si trascina verso il basso: Excel capisce che si vuole completare una serie, ed in ogni cella seguente le prime due, inserisce i numeri incrementandoli di uno. Vediamo ora le procedure inserite nella Userform, partendo dal pulsante "Inserisci Nuovo". Quando apriamo la UserForm, le Textbox sono tutte vuote. Se vorremo inserire un nuovo nominativo, dovremo scrivere i dati nelle rispettive TextBox, DOPODICHE' cliccheremo sul pulsante. L'istruzione comincia con un controllo sulla TextBox2, quella destinata a contenere il "Nominativo": se la trova vuota, avvisa con un messaggio, riposiziona il focus sella textbox2, ed esce dalla routine senza eseguire il resto delle istruzioni. Se invece la textbox2 conterrà dei dati , viene posta una domanda di conferma registrazione dati (questo per evitare, mentre siamo in modalità "consultazione dati", di premere inavvertitamente il pulsante che senza un controllo di conferma, registrerebbe di nuovo i dati già presenti), se si risponderà Si, inizia la copia dei dati presenti dalle textbox alle celle sul foglio di lavoro. Dopo la copia, avvisa con un messaggio l'avvenuta esecuzione, indi pulisce le textbox. Vediamo nel dettaglio le istruzioni: Range("B1").End(xlDown).Offset(1, 0).Select - questo è il comando che, seleziona la cella B1, si sposta verso il basso (End(xlDown) cercando l'ultima cella occupata, trovata questa, si sposta di una riga sotto, stessa colonna (Offset(1, 0)), e la seleziona (Select) e la rende Attiva; questa cella è ovviamente vuota. ActiveCell.Value = TextBox2 - comincia a copiare il contenuto delle textbox sulla userform con un semplice segno di uguale (=), cioè :la cella sul foglio di lavoro in quel momento attiva, viene resa uguale ai dati contenuti nella textbox2, poi a seguire: ActiveCell.Offset(0, 1).Value = TextBox3 - viene cercata con Offset la prima cella a destra di quella attiva in quel momento, e viene resa uguale al contenuto della textbox successiva, la 3. Si continua proseguendo con lo "scarto" (Offset) di una cella, stessa riga, per ogni textbox interessata. Completate queste istruzioni, appare il messaggio di esecuzione completata, indi TextBox2 = "" - si puliscono le celle predisponendole per nuovi inserimenti. Private Sub CommandButton2_Click() If TextBox2 = "" Then MsgBox "Devi Inserire almeno il nominativo" TextBox2.SetFocus Exit Sub End If 'questa l'istruzione per la domanda di conferma Dim irisposta As Integer irisposta = MsgBox("Confermi la registrazione" _ & " di " & TextBox2.Value & " ?", vbYesNo) If irisposta = vbYes Then Range("B1").Value = "-" Range("B1").End(xlDown).Offset(1, 0).Select ActiveCell.Value = TextBox2 ActiveCell.Offset(0, 1).Value = TextBox3 ActiveCell.Offset(0, 2).Value = TextBox4 ActiveCell.Offset(0, 3).Value = TextBox5 ActiveCell.Offset(0, 4).Value = TextBox6 ActiveCell.Offset(0, 5).Value = TextBox7 MsgBox "Ok Capo, eseguito!" TextBox2 = "" TextBox3 = "" TextBox4 = "" TextBox5 = "" TextBox6 = "" TextBox7 = "" End If End Sub Ad ogni click sul pulsante assegnato, ripeteremo la stessa procedura. Passiamo ad esaminare le istruzioni legate al pulsante "Cerca". Ho inserito due OptionButton (pulsanti di opzione), che ci consentono di scegliere, selezionando uno dei due Optbutton, se vorremo una ricerca basata sull' esatto nome che scriveremo nella textbox sottostante (la textbox1), oppure una ricerca basata su parte del nome che andremo cercando: se scriveremo "ci" verranno trovati tutti quei nomi in http://ennius.interfree.it/
Pagina 134
MANUALE VBA
X EXCEL
cui appare la coppia di lettere "ci", siano posizionate all'inizio o all'interno del nome cercato (per esempio: Ciccillo Gaetano oppure Romboni Placido, ecc.). Ho usato quindi due istruzioni diverse, anche per mostrare che i cicli di ricerca si possono impostare usando diverse modalità di programmazione, ogni procedura legata alla selezione di un opzione. Vediamo quindi la procedura legata al primo pulsante di opzione, quella che ci trova il dato se avremo scritto il nome esatto. Poichè, credo, risulterà nel tempo, difficile ricordarsi se avremo registrato un nominativo usando lettere maiuscole o minuscole, (la ricerca sarebbe CaseSensitive, cioè riconosce le lettere e se non sono scritte uguali non vengono identificate : Ennius è diverso da ennius) ho usato in questo caso la funzione Option Compare Text che ci consente di riconoscere una parola indipendentemente dalle maiuscole/minuscole usate. Solo che questa funzione, NON può lavorare al di fuori di un "Modulo" (vba), cioè esterno alle istruzioni che invece sono posizionate tutte nella UserForm. Per cui è sufficiente inserire un modulo nel progetto, e nella zona "Dichiarazioni" - "Generale" del modulo stesso, inserire la funzione, e subito sotto, posizionare la routine (assegnandoli un nome) su cui agirà la funzione Option Compare Text. Sulla Userform, nell'evento Click del pulsante "Cerca", sarà sufficiente richiamare detta routine semplicemente scrivendo il nome della routine stessa. Ci penserà il codice, ad andarsi a leggere le istruzioni di questa routine, (sotto), in verde sono le spiegazioni. Unica precisazione: trattandosi di istruzioni contenute in un Modulo, per identificare dove si trovano le TextBox, è necessario identificare l'"oggetto" che le contiene, e quindi la riga d'istruzione và così impostata: UserForm1.TextBox2 = CL.Value . (nelle istruzioni contenute nella userform, sarebbe stato sufficiente TextBox2 = CL.Value). Questo è il codice inserito nel Modulo : Option Compare Text ____________________________________________ Sub primo() Dim CL As Object 'dichiarazione del tipo di variabile per CL For Each CL In Range("B3:B152") 'per ogni CL (cella) tra B3 e B152 Dim X As String 'dichiar. di var. per la X X = UserForm1.TextBox1.Value 'la X sarà uguale al dato nella textbox1 che è il 'nominativo da cercare If CL = X Then 'se la cella (CL) è uguale a X CL.Select 'faccio selezionare (fermo il ciclo) questa cella Y = CL.Value 'con Y prelevo il dato contenuto nella cella(CL) 'sotto: carico le textbox della userform con i dati nella cella in quel momento selezionata, e 'nelle celle adiacenti, sulla stessa riga, ma con "scarto" di una rispetto alla selezionata UserForm1.TextBox2 = CL.Value UserForm1.TextBox3 = CL.Offset(0, 1).Value UserForm1.TextBox4 = CL.Offset(0, 2).Value UserForm1.TextBox5 = CL.Offset(0, 3).Value UserForm1.TextBox6 = CL.Offset(0, 4).Value UserForm1.TextBox7 = CL.Offset(0, 5).Value Dim irisposta As Integer 'Imposto la msgbox e relativa domanda irisposta = MsgBox("Trovato " & Y & ". Vuoi fermarti ?", vbYesNo) If irisposta = vbYes Then 'se rispondo SI allora Exit For 'esco dal ciclo End If End If Next CL 'altrimenti proseguo alla successiva cella End Sub Ed ora vediamo tutto il codice inserito nel CommandButton1_Click. Cominciamo con le spiegazioni: intanto viene inserito il controllo se la textbox1, quella che dovrà contenere la parola da cercare, sarà vuota, allora avvisa con un messaggio, pone il focus sella textbox1, ed esce dal ciclo senza continuare l'esecuzione delle istruzioni sottostanti, in caso contenga del testo, avvia l'esecuzione del codice sottostante Private Sub CommandButton1_Click() 'controlla che la textbox1 contenga dati If TextBox1 = "" Then http://ennius.interfree.it/
Pagina 135
MANUALE VBA
X EXCEL
MsgBox "Inserisci Nominativo da cercare" TextBox1.SetFocus Exit Sub End If 'se la textbox1 contiene dati, controlla quale optionbutton è attiva (selezionata).La 'proprietà che attiva la optionbutton è la proprietà Value, che dovrà essere impostata a 'True (di default è impostata a False). In questo esercizio, ho impostato a True questa 'proprietà, per avere un opzione attivata all'avvio della userform. If OptionButton1.Value = True Then 'quindi, se è attiva la optionbutton1 'allora chiama ed esegue la sub "primo" contenuta nel modulo primo End If 'se invece sarà la optionbutton2 ad essere attiva, esegue queste altre istruzioni: If OptionButton2.Value = True Then 'con il Foglio1, nella colonna B da B3 a B150 (zona su cui avviene la ricerca) With Worksheets(1).Range("B3:B150") Dim X As String 'dichiarazione del tipo di variabile assegnata ad X X = TextBox1.Value 'la X sarà uguale al dato nella textbox1 che è il nominativo da 'cercare 'sotto: l'istruzione Set serve ad assegnare a C il riferimento a cui accedere da parte del 'metodo Find (trova); cioè cercherà in C il valore rappresentato dalla X, e lo cercherà 'secondo l'istruzione LookIn=xlValues che consente di cercare dati che contengano il 'valore di X. E' questa istruzione che consente di trovare una parola, digitando anche solo 'una parte di essa. Se si volesse una ricerca esatta allora bisognerebbe completare l'istruzione Find così: Find(X, LookIn:=xlValues, LookAt:=xlWhole) Set C = .Find(X, LookIn:=xlValues) ', LookAt:=xlWhole 'a questo punto C corrisponderà alla prima cella corrispondente al valore cercato, se C corrisponde al valore cercato If Not C Is Nothing Then 'viene memorizzato come primo indirizzo il riferimento alla cella rappresentata da C firstAddress = C.Address 'inizia il ciclo Do....Loop. Le istruzioni Do...Loop consentono di eseguire un blocco di 'istruzioni per un numero indefinito di volte. Le istruzioni vengono ripetute fino a quando 'una condizione è True o fino a quando non diventa True Do 'se la condizione cercata sarà True, cioè se viene rintracciata una cella che corrisponde al 'valore cercato, allora questa cella viene selezionata C.Cells.Select 'ora il contenuto della cella selezionata e di quelle adiacenti (scart) viene riportato nelle textbox della userform, in modo che si possano visualizzare i dati TextBox2 = C.Value TextBox3 = C.Offset(0, 1).Value TextBox4 = C.Offset(0, 2).Value TextBox5 = C.Offset(0, 3).Value TextBox6 = C.Offset(0, 4).Value TextBox7 = C.Offset(0, 5).Value Y = C.Value 'assegno a Y in valore contenuto nella cella selezionata e interrompo il ciclo 'con una msgbox di domanda irisposta = MsgBox("Trovato " & Y & " . Vuoi fermarti ?", vbYesNo) If irisposta = vbYes Then 'se rispondo SI allora GoTo 10 'esco dal ciclo andando a cercare l'istruzione End With End If 'in caso risponda NO, continuo a cercare (FindNext(C)) Set C = .FindNext(C) 'sotto dico: gira (Loop) fintantochè (While) C non viene trovato e le celle sono http://ennius.interfree.it/
Pagina 136
MANUALE VBA
X EXCEL
successive alla prima (identificata con il riferimento firstAddress) Loop While Not C Is Nothing And C.Address firstAddress 'nel caso la ricerca non abbia dato esito, allora (Else) viene dato il messaggio Else MsgBox "Nome non Trovato" End If 10: End With 'finisce il ciclo End If End Sub Queste due routine, quella assegnata all'optionbutton1 e quella assegnata all'optionbutton2, sono simili nell'uso pratico, solo che la prima cercando SOLO valori esatti, può essere utilizzata in tutti quei casi dove si voglia la corrispondenza esatta e basta, per esempio nella ricerca di un codice articolo, o di un numero. In questi casi, se i valori saranno solo numeri, occorrerrà modificare la prima istruzione: intanto, sarà possibile inserirla nella routine stessa attivata dal commandbotton1 (Cerca) e non in un modulo (non ci sarà più la necessità di usare Option Compare Text), e andrà posizionata al posto del richiamo alla Sub primo contenuta appunto nel modulo), e poi, trattandosi di una ricerca Numerica, bisogna che il codice sappia che il valore da cercare non sarà più un Testo, ma un Numero, e adopereremo questa modifica nell'assegnazione della variabile alla X anzichè Dim X As String 'dichiar. di var. per la X X = UserForm1.TextBox1.Value 'la X sarà uguale al dato nella textbox1 che è ec.ecc. si userà Dim X 'dichiar. di var. per la X di tipo Variant X = Val(TextBox1) 'la X sarà uguale al numero nella textbox1 che è il ______________________________________________________ Ora vediamo rapidamente le istruzioni collegate agli altri pulsanti. Condizione essenziale comune a due di queste routine seguenti, (Modificare o Cancellare) è che tutte lavorano SE SI E' SVOLTA PRIMA UNA RICERCA, e quindi la textbox2 conterrà dei dati (nominativo). Quindi le procedure seguenti si potranno attivare SOLO se prima si è chiamato il record sul quale AGIRE, (non avrebbe senso infatti, Modificare o Cancellare qualcosa che non si sia prima evidenziato. Due spiegazioni: Pulsante "Modifica". Dato che avremo già una cella "Attiva" e di cui vedremo i dati direttamente nelle textbox, queste istruzioni si limitano a "riscrivere" nelle stesse celle, i valori che si trovano nelle textbox, compreso quei valori che nel frattempo possiamo aver modificato. Pulsante "Cancella record" . Anche in questo caso vedremo già i dati nelle textbox perchè "trovati" col pulsante "Cerca", e quindi avremo una cella attiva sulla quale intervenire col metodo "Delete". Faccio eliminare l'intera riga, compreso quindi la cella nella colonna A, quella dove esiste il N° riga progressivo, e quindi ho inserito una semplice istruzione, che cancella tutto il range della colonna A, poi assegna il valore 1 alla prima cella (A3) e poi aggiunge 1 alle celle seguenti. Queste le due routine, in sequenza: Private Sub CommandButton3_Click() 'Pulsante "Modifica" If TextBox2 = "" Then MsgBox "Devi Cercare un nominativo per modificarlo" Exit Sub End If ActiveCell.Value = TextBox2 ActiveCell.Offset(0, 1).Value = TextBox3 ActiveCell.Offset(0, 2).Value = TextBox4 ActiveCell.Offset(0, 3).Value = TextBox5 ActiveCell.Offset(0, 4).Value = TextBox6 ActiveCell.Offset(0, 5).Value = TextBox7 MsgBox "Ok! Eseguito!" End Sub Private Sub CommandButton4_Click() 'pulsante "Cancella" If TextBox2 = "" Then Exit Sub 'se la textbox2 è vuota, esce dalla routine 'se la textbox2 contiene un dato perchè ottenuto con la ricerca, allora seleziona la cella 'attiva per confermare l'eliminazione dell'intera riga, facendo prima una domanda http://ennius.interfree.it/
Pagina 137
MANUALE VBA
X EXCEL
ActiveCell.Select Dim irisposta As Integer irisposta = MsgBox("Vuoi cancellare il Nominativo: " & ActiveCell.Value & " ?", _ vbYesNo) If irisposta = vbYes Then ActiveCell.EntireRow.Delete 'poi pulisce la colonna A, seleziona la cella A3, inserisce 1 e incrementa le celle sotto di 1 'fino alla fine del range previsto (A3:A152) Dim CA As Object Range("A3:A152").ClearContents Range("A3").Value = 1 For Each CA In Range("A3:A152") If CA.Offset(1, 0) = "" Then CA.Offset(1, 0) = CA + 1 End If Next End If End Sub I pulsanti per la navigazione tra i record, sfruttano entrambi la selezione di una cella, e con la cella attiva, si muovono verso l'alto o verso il basso sfruttando lo "scarto" (Offset) verso l'alto (-1) o verso il basso (+1). Per renderli funzionanti anche se non si è eseguita nessuna ricerca, faccio selezionare all'apertura della UserForm, la cella B3, quella nella quale si troverà il primo nominativo. Ho inserito due controlli perchè avvisino e si fermino se saremo a inizio o a fine elenco. questa l'istruzione nell'Initialize della UserForm: Private Sub UserForm_Initialize() Worksheets(1).Range("B3").Select End Sub e queste le istruzioni inserite nei pulsanti di navigazione: Private Sub CommandButton5_Click() 'pulsante per "Scorri in Su" If ActiveCell.Offset(-1, 0).Value = "Nominativo" Then MsgBox "Siamo a inizio elenco, impossibile salire oltre" Exit Sub End If ActiveCell.Offset(-1, 0).Select TextBox2 = ActiveCell.Offset(0, 0).Value TextBox3 = ActiveCell.Offset(0, 1).Value TextBox4 = ActiveCell.Offset(0, 2).Value TextBox5 = ActiveCell.Offset(0, 3).Value TextBox6 = ActiveCell.Offset(0, 4).Value TextBox7 = ActiveCell.Offset(0, 5).Value End Sub Private Sub CommandButton6_Click() 'pulsante per "Scorri in Giù" If ActiveCell.Offset(1, 0).Value = "" Then MsgBox "Siamo a fine elenco, impossibile proseguire" Exit Sub End If ActiveCell.Offset(1, 0).Select TextBox2 = ActiveCell.Value TextBox3 = ActiveCell.Offset(0, 1).Value TextBox4 = ActiveCell.Offset(0, 2).Value TextBox5 = ActiveCell.Offset(0, 3).Value TextBox6 = ActiveCell.Offset(0, 4).Value TextBox7 = ActiveCell.Offset(0, 5).Value http://ennius.interfree.it/
Pagina 138
MANUALE VBA
X EXCEL
End Sub File consultabile e scaricabile : MioDB2-2000.zip
http://ennius.interfree.it/
28 Kb
Pagina 139
MANUALE VBA
X EXCEL
Database per Gestione Magazzino e Vendite. Presento un progetto completo (o quasi) per la gestione di un magazzino prodotti, con registrazione della quantità venduta e saldo quotidiano dei ricavi. Adatto a tutte quelle attività dove si voglia una gestione semplice e veloce dove si voglia amministrare il carico/scarico prodotti con visualizzazione del venduto e registrazione delle cifre incassate. Scopo di questo esercizio è di fornire una progetto ampliabile e modificabile a piacere, per adattarlo alle varie necessità. Seguendo l'impostazione data con il primo database presentato in questa sezione (vedi: "Database con spiegazioni") che si fonda essenzialmente sulla selezione di una cella del database, rendendola quindi ActiveCell, e tramite l'utilizzo degli Offset, reperire, dialogare, visualizzare, modificare tutti i dati correlati al dato contenuto nell'ActiveCell. Il concetto è molto semplice, è può essere facilmente assimilato leggendo le spiegazioni fornite nel paragrafo sopra citato. Ovviamente a questo progetto sono state apportare le modifiche necessarie che ora vedremo Sotto un immagine della form per la gestione dei dati:
All'utente viene lasciato il compito di inserire i seguenti dati: codice articolo, descrizione articolo, costo, ricarico %, aliquota iva, e fornitore, mentre i calcoli per la determinazione del costo comprensivo del ricarico, dell'importo iva, del prezzo di vendita, e del valore totale giacenza sono eseguiti dal codice nel momento in cui si esegue un carico o uno scarico, e registrate in automatico sul database, e nel caso dello scarico, anche registrate sul Foglio2, che è il foglio dove si consulta il venduto della giornata. Quando si apre la cartella di lavoro, appare la form, viene selezionata la cella A3 che corrisponde alla prima cella di inizio elenco, e vengono caricate le textbox con i dati dei campi di questa prima riga. Sarà possibile scorrere l'elenco con gli appositi pulsanti, o decidere di eseguire altre operazioni: Inserimento nuovi articoli Poichè la form si presenta sempre con i dati presenti nella prima riga dell'elenco, PRIMA di premere il pulsante "Inserisci Nuovo", (che inserirebbe i dati presenti nel database cercando la prima riga libera dove "scaricare" i dati delle textbox, creando quindi un "doppione" dei dati ivi già presenti) è necessario premere la barra fucsia per pulire i campi, scrivere i dati nelle textbox abilitate (le textbox relative ai calcoli non sono abilitate) e SOLO a questo punto premere il pulsante "Inserisci Nuovo". Sono stanti inseriti gli opportuni controlli per evitare di dimenticare di inserire i dati necessari. I valori da inserire nei campi "Ricarico" e "Aliquota Iva" DEVONO essere valori SENZA il segno di percentuale (%). Provvedono le istruzioni a considerarli come tali.(per esempio, come ricarico, si può inserire 70 MA NON 70%). (vedi foto della barra, sotto)
http://ennius.interfree.it/
Pagina 140
MANUALE VBA
X EXCEL
Carico Una volta che i dati sono stati inseriti nel database, sarà possibile agire sul pulsante "Carico". Apparirà un'altra form, nella quale si viene avvisati che si sta eseguendo un carico sull'articolo in quel momento attivo, si inseriranno i valori e si premerà "Conferma". Il codice provvede a fare tutti i calcoli dovuti, ad aggiornare la form "Magazzino" e il database. Tutti i dati numerici inseriti, vengono trattati come "tipo" di dati Double, quindi "decimali". Questo per consentire di trattare quantità anche con decimali e prezzi in euro.
Scarico La form per lo scarico è la form più densa di istruzioni; intanto controlla che la quantità da scaricare sia "coperta" dalla giacenza: non si può scaricare più di quanto si ha in giacenza. Poi esegue tutti i calcoli per l'aggiornamento dei valori, sia sulla form "Magazzino", sia sul database; provvede inoltre a "raccogliere" i dati dell'articolo scaricato ed a comporli in una zona non visibile del foglio1 , e da qui copia i dati (non tutti) per riportarli nel Foglio2, il foglio dove vengono registrati l' articolo, la quantità venduta, il prezzo unitario di vendita (quattro valori), con ricerca della prima riga libera per incollare i dati.. Sul foglio2 sono presenti due formule, una conteggia il totale venduto per articolo, l'altro inserisce la data del giorno. Una cella porta il totale giornaliero delle vendite. Le celle e le formule sono predisposte per 300 righe: basta assegnare il formato celle e copiare le formule per quante righe in più si vorrà. Sono presenti in questo foglio due pulsanti: con uno si pulisce la zona dei dati del giorno prima (solo i dati, non le formule), e con l'altro pulsante si ritorna sul foglio database e si riattiva la form "Magazzino". Sotto un immagine del foglio2
Un altra istruzione che viene eseguita dalla form "Scarico", è il controllo della quantità giacente dopo lo scarico: se il valore della giacenza sarà zero, viene posta una domanda per l'eliminazione dell'intera riga : in caso di risposta affermativa la riga dell'articolo con giacenza zero verrà eliminata. Un'ultima precisazione: sulla form "Magazzino" , il pulsante "Mostra Vendite" serve per passare a vista (senza la form) sul foglio2 per consultare i dati; il pulsante "Esci e Salva" serve per salvare la cartella e uscire chiudendo anche Excel. Per le spiegazioni generali, vi rimando al paragrafo "Database con spiegazioni", mentre le istruzioni più significative sono spiegate nelle procedure sul file. File consultabile e scaricabile : Gestione Magazzino.zip
http://ennius.interfree.it/
125 Kb
Pagina 141
MANUALE VBA
X EXCEL
Un esercizio completo per la Gestione di un database su Excel Inserisco questo programma, peraltro richiestomi, come esempio di cosa si possa ottenere con una buona programmazione. Premetto che quasi tutto il codice NON è mio, ma estratto da un articolo di un "Maestro": Gianni Giaccaglini, che ha pubblicato diversi libri su Excel ed il Vba, (che invito ad acquistare). E' comunque sintomatico che, aiutandosi con le istruzioni che leggiamo su i libri "guida", si possa cominciare a capirle ed adattarle alle nostre esigenze. Il programma consente la gestione di un database completo, basato su una Form di controllo, che consente di: Introdurre nuovi dati. Modificare i dati esistenti. Navigare tra i record, scalandoli uno per volta, selezionare l'ultimo record inserito o risalire al primo record. Ordinare l'elenco dati in base al Nominativo oppure al Numero progressivo. Eliminare un record. Tutto questo direttamente dalla Form di gestione. E' stato inserito un pulsante per la ricerca di un nominativo che, se presente, viene selezionato sul foglio di lavoro, direttamente nel database. L'esempio riportato nel file allegato, è stato predisposto per l'inserimento fino a 600 nominativi. NON sono stati inseriti né commenti né spiegazioni, e non intendo fornirne. Chi intende scaricare il file, dovrà possedere una certa conoscenza del Vba, e interpretare le istruzioni da solo. Sotto, un immagine del programma:
File consultabile e scaricabile: Mio db2000.zip 29 Kb
http://ennius.interfree.it/
Pagina 142
MANUALE VBA
X EXCEL
UserForm unica per Database su più fogli Presento un esempio di come si possa con un'unica UserForm, gestire più elenchi (database) all'interno della stessa cartella, ma su più fogli. Condizione necessaria richiesta: le tabelle che formano il database dovranno tutte mantenere la stessa "struttura": riferimenti alle celle, numero di campi, riga inizio elenco, ecc.ecc. per ogni foglio. Questo ci consente quindi di usare un'unica form, sulla quale saranno posizionati dei pulsanti per spostarsi tra i vari fogli. Una volta sul foglio voluto, gestiremo i dati presenti su quel foglio, senza avere legami agli altri fogli. Utile quando si voglia, per più utenti, sfruttare database personali, diversi nel contenuto, ma simili nella gestione, oppure quando si vogliano tenere separati per tipologia di appartenenza, nominativi e relativi dati. Nell'esempio preparato, ho usato i fogli per tipologia di nominativi : amici, nemici, parenti, altri. Ma si possono creare elenchi divisi per regione, hobby, interessi, affinità, prefisso telefonico, cap, ideologia, religione e chi più ne ha più ne metta. Basterà predisporre tanti fogli quante sono le diversificazioni. Sarà possibile, per ogni tabella/foglio, cambiare il nome (NON il numero) dei campi, avendo cura di far cambiare le proprietà Caption delle Label che indicano il contenuto delle relative textbox sulla form, allorchè la Form si apre su quel determinato foglio. (basta inserire le istruzioni nell'evento Click del CommandButton che porta a quel foglio) . Insomma, c'è solo da sbizzarrirsi. Sfrutteremo il database MioDB2-2000.xls, presente in questa sezione, paragrafo "Database con spiegazioni", che ben si presta a questa modifica. (Potete consultare la pagina, per accedere alle spiegazioni relative alle istruzioni impiegate). Sono sufficienti poche modifiche, per ottenere questo MultiDB2-2000. Grazie alla impostazione, sia per la ricerca, sia per l'inserimento, o la cancellazione di dati, basata essenzialmente sull'ActiveCell (cella attiva) e sulle celle adiacenti reperite con l'uso di Offset (Scarto), senza usare riferimenti o nomi di Fogli su cui operare (o quasi), è possibile usare il foglio attivo in quel momento, per ottenere la completa gestione dei dati sopra menzionati, all'interno del foglio. Questa un immagine del file:
Vediamo le modifiche rispetto all'altro database: intanto è stato cambiato il nome all'UserForm1 in Indirizzario, quindi sono state modificate tutte le istruzioni che facevano riferimento al nome UserForm1 (nelle chiamate dei pulsanti, all'apertura della cartella di lavoro, e nel modulo1) Al posto dell' istruzione che fa riferimento al Worksheet(1) (che è l'unico foglio su cui lavora l'altro database) è stato usato ActiveSheet, più generico, e si riferisce al foglio attivo in quel momento, adatto quindi ad una multiselezione (uniche due istruzioni, la prima nel commandbutton1, che diventa With ActiveSheet.Range("B3:B150"), e la seconda nel UserForm_Initialize che diventa ActiveSheet.Range("B3").Select ). Per favorire la ricerca dei dati, ho inserito una ComboBox, che "pesca" i dati nel campo "Nominativo". Selezionando un nominativo presente nella lista, trasferiamo lo stesso nella textbox1, che è quella che serve il pulsante "Cerca". Essendo un nominativo "completo", si potrà usare l'opzione di default per una http://ennius.interfree.it/
Pagina 143
MANUALE VBA
X EXCEL
ricerca immediata. Unico accorgimento: dovendo "pescare" i dati che per ogni foglio sono o possono essere diversi, occorre "istruire" la proprietà RowSource della combobox in modo che legga i dati dal foglio in quel momento attivo, quindi ho usato l'evento Click di ogni pulsante che serve a selezionare un foglio, quindi dopo aver selezionato il foglio con Sheets(1).Select , faccio prendere il nuovo riferimento con la seguente istruzione: If ActiveSheet.Name = ("Foglio1") Then Indirizzario.ComboBox1.RowSource = "" 'vuoto la combobox dei precedenti dati Indirizzario.ComboBox1.RowSource = "B3:B152" 'ricarico la combobox con i dati del foglio attivo Per segnalare all'utente su quale foglio ci troviamo nel momento, ho usato delle CkeckBox che porterranno un segno di spunta se il foglio attivo in quel momento è il foglio x, e lo toglieranno agli altri fogli (queste sono per il foglio1, ad ogni foglio cambiano da False a True e viceversa). CheckBox1.Value = True CheckBox2.Value = False CheckBox3.Value = False CheckBox4.Value = False End If Poichè quando si uscirà da Excel, salvando, lo potremo fare da qualunque foglio, ho previsto questa istruzione che riattiva sia la RowSource della ComboBox sia le Chekbox quando riapriremo il file, e ho usato l'evento Activate della UserForm, che faccio, ricordo, aprire all'apertura della cartella di lavoro: Private Sub UserForm_Activate() Indirizzario.ComboBox1.RowSource = "" Indirizzario.ComboBox1.RowSource = "B3:B152" If ActiveSheet.Name = ("Foglio1") Then CheckBox1.Value = True CheckBox2.Value = False CheckBox3.Value = False CheckBox4.Value = False End If If ActiveSheet.Name = ("Foglio2") Then CheckBox1.Value = False CheckBox2.Value = True CheckBox3.Value = False CheckBox4.Value = False End If If ActiveSheet.Name = ("Foglio3") Then CheckBox1.Value = False CheckBox2.Value = False CheckBox3.Value = True CheckBox4.Value = False End If If ActiveSheet.Name = ("Foglio4") Then CheckBox1.Value = False CheckBox2.Value = False CheckBox3.Value = False CheckBox4.Value = True End If Mi sembra di non scordarmi niente, e comunque potete scaricare il file e, se credete, divertirvi. File consultabile e scaricabile : MultiDB2-2000.zip
http://ennius.interfree.it/
42 Kb
Pagina 144
MANUALE VBA
X EXCEL
Realizzare un DataBase senza usare il VBA (o quasi) In seguito ad alcune richieste e per offrire un'alternativa a tutti coloro che intendono realizzare una gestione dei propri dati (elenchi di dati) ma hanno poca o nessuna dimestichezza con il Vba, presento questa soluzione gestibile da chiunque. Si basa sul comando Modulo, attivabile dal menù Dati/Modulo. Un DataBase altro non è che un insieme di dati organizzati in campi e in record: cioè in colonne e righe. La struttura di Excel si presta bene quindi alla realizzazione di database, cioè di elenchi di dati. Ogni database che si rispetti porta le "intestazioni di colonna" (campi) cioè la descrizione di cosa si inserirà nelle righe sottostanti della colonna stessa. Una agenda indirizzi è il classico esempio di in database che conterrà le "intestazioni di colonna" come: numero progressivo, nominativo, indirizzo, città, telefono, ecc.ecc; ognuno di questi sarà un "campo" e dovrà impegnare tante colonne quanti sono i campi, senza lasciare colonne vuote tra un campo e l'altro. Le righe ospiteranno i dati veri e propri relativi al campo di appartenenza.. prendendo come esempio la struttura del "Mio Db2000" del paragrafo precedente, procederemo come segue: selezioniamo la cella A2, (primo campo del nostro elenco) poi ci spostiamo sul menu Dati, clicchiamo su Modulo, ed apparirà la maschera che vediamo nell'immagine seguente:
Se vi appare un messaggio in cui Excel comunica di non riuscire a determinare quale riga contiene le etichette di colonna, proseguite premendo sul pulsante OK. (questo messaggio non apparirà più appena inserito il primo dato). Intanto esaminiamo la maschera: nella barra di intestazione viene riportato in automatico il nome del foglio di lavoro, sulla sinistra ci sono i nomi dei "campi" (le intestazioni di colonna che vediamo sul foglio nella riga 1), a lato si possono leggere i dati contenuti nei rispettivi campi, una barra verticale di scorrimento che serve per una scorsa veloce dei dati presenti nell'elenco; sulla destra notiamo dei pulsanti di chiaro significato, servono per: aggiungere nuovi dati (Nuovo): premendo questo pulsante sarà possibile inserire, nelle apposite caselle che si presenteranno vuote, i nuovi dati: premendo il tasto INVIO (Enter) sulla tastiera, i dati verranno registrati su una riga vuota. Gli altri tasti si commentano da soli: eliminare dati, scorrere le righe alla ricerca di un dato: dato precedente, dato successivo. Il pulsante "Criteri" serve per una ricerca mirata basata su uno dei campi presenti, per esempio per cercare un determinato nominativo. Premuto il pulsante: la maschera assumerà questa immagine:
http://ennius.interfree.it/
Pagina 145
MANUALE VBA
X EXCEL
Ci troveremo in modalità "Criteri": sarà possibile digitare un nome o parte di un nome perchè si attivi la ricerca premendo su uno dei pulsanti "Trova", oppure ritornare alla maschera precedente, premendo il pulsante "Modulo". Se avessimo inserito nel campo Nominativo la parola "pec", e premuto il pulsante "Trova succ." ci saremmo trovati con la maschera che mostra il record trovato, come sotto nella foto:
Ecco quindi un pratico sistema per ottenere una maschera di introduzione, modifica, ricerca dati, senza avere fatto ricorso a nessuna "subdola" pratica Vba. Unica concessione. un pulsante per risparmiarci il "fastidio" di richiamare la maschera dal menu Dati, usando il pulsante stesso collegato ad una semplice macro: Sub Apri() Range("A2").Select ActiveSheet.ShowDataForm End Sub che serve ad attivare la maschera (ShowDataForm). File scaricabile e consultabile: Modulo2000.zip 21Kb
http://ennius.interfree.it/
Pagina 146
MANUALE VBA
http://ennius.interfree.it/
X EXCEL
Pagina 147
MANUALE VBA
X EXCEL
UserForm unica per più Database sulla stessa cartella Ancora una variante realizzata sullo "scheletro" del Multi DB2-2000, dove, a differenza di quest'ultimo che sfrutta la struttura di una tabella di partenza (stesso numero di campi, stessa intestazione dei campi, stesse impostazioni per inizio riga delle tabelle), e che si muove sui fogli grazie alle istruzioni collegate a dei commandbutton, ma che rimane, come base, impostata sulla gestione di più database TUTTI inerenti però solo a indirizzari, in questo caso si vuole diversificare la tipologia di dati da inserire, in modo da avere sempre un'unico file, un'unica userform per introdurre i dati, ma la possibilità di gestire insieme, una Agenda Indirizzi, una Libreria, una Musicoteca, ed una Videoteca. Anche in questo caso, condizione comune ai quattro database, è il mantenimento dello stesso NUMERO di campi. (E' possibile, per i più volenterosi, aumentare il numero di campi desiderati, variando opportunamente le istruzioni, in particolare per la lettura/scrittura dei campi event. aggiunti, tramite l'aggiunta di nuove TextBox (da rendere visibili/non visibili alla bisogna) e dei riferimenti relativi agli Scarti (Offset) dell'ActiveCell aggiunte per i nuovi campi, abbinate ai pulsanti che porteranno alle pagine dove queste modifiche sono state fatte). Per questo nuovo database sono state apportate alcune modifiche : intanto facciamo ridurre ad icona la finestra di Excel, in modo che resti visibile solo la form per la gestione dei dati, e per questo è sufficiente inserire nel WorkBook_Open, questa istruzione: Application.WindowState = xlMinimized poi, per essere sicuri che quando si preme sul pulsante ESCI, vengano salvate tutte le modifiche fatte ai vari fogli, e ripristinata la posizione della Finestra di Excel come in origine, nell'evento Click del Pulsante Esci sono state aggiunte queste istruzioni: For Each cartella In Application.Workbooks cartella.Save Next cartella Application.WindowState = xlMaximized Application.Quit End La proprietà Caption del pulsante ESCI è stata modificata in ESCI & SALVA. In questo modo si esce dalla cartella e si chiude anche Excel. Sono stati rinominati i campi dei vari fogli, in modo da corrispondere al tipo di dati che inseriremo per es. in Libreria, non esiste Nominativo, ma Titolo del libro, non Indirizzo ma Autore, ecc.ecc. Per far sì che anche sulla UserForm. al variare del tipo di database ospitato nel foglio selezionato, vengano aggiornate anche le etichette che indicano il tipo di dati da introdurre o presenti nelle textbox, per ogni pulsante che ci sposta ad un foglio, sono state aggiunte le istruzioni opportune, vediamo un esempio per quando selezioniamo il foglio3 (pulsante "Musica"): Sheets(3).Select If ActiveSheet.Name = ("Foglio3") Then TextBox2 = "" TextBox3 = "" TextBox4 = "" TextBox5 = "" TextBox6 = "" TextBox7 = "" Indirizzario.ComboBox1.RowSource = "" Indirizzario.ComboBox1.RowSource = "B3:B152" CheckBox1.Value = False CheckBox2.Value = False CheckBox3.Value = True CheckBox4.Value = False Label1.Caption = "Cantante" Label2.Caption = "Titolo" Label3.Caption = "Genere" Label4.Caption = "CD-titolo" Label5.Caption = "Anno Pubblic." Label6.Caption = "Prezzo" Label7.Caption = "Scrivi il Cantante da cercare:" Label10.Caption = "MUSICA" OptionButton1.Caption = "Cerca Cantante (esatto)" http://ennius.interfree.it/
Pagina 148
MANUALE VBA
X EXCEL
OptionButton2.Caption = "Cerca Cantante (parziale)" End If Come si vede, vengono reimpostate le proprietà Caption delle Label con le nuove intestazioni dei campi presenti sul foglio3. Vengono altresì pulite tutte le textbox, per evitare di vedere dei dati che appartengono ad un foglio precedente. Queste istruzioni saranno presenti in ogni pulsante di navigazione tra i fogli, ovviamente con le giuste modifiche. Questa un immagine se si è su Agenda:
e questa un immagine di quando saremo su Musica
Come si nota le Label sono aggiornate per i nuovi campi. Altrettanto succederà per gli altri fogli. File consultabile e scaricabile : VariusDB2-2000.zip
http://ennius.interfree.it/
61 Kb
Pagina 149
MANUALE VBA
X EXCEL
Dichiarazione Variabili - accorgimenti Non molti sanno che quando in una istruzione in codice vb (e quindi anche Vba) si usano variabili, eseguendo l'istruzione, si occupa memoria del computer (RAM), e che quando l'istruzione è arrivata alla fine (End Sub), questa memoria non viene "scaricata" dalle variabili che si accumulano e continuano ad occupare memoria. Esiste un'istruzione che si può usare, e che serve a cancellare dalla memoria le variabili prima create. Facciamo un esempio, vediamo una macro in cui si usa una variabile per assegnare un valore che potrà cambiare: Sub Esempio() Dim X 'in assenza del "tipo" di variabile, il codice interpreta la variabile come Variant X = Range("A1").Value 'X è la variabile ...seguono le istruzioni End Sub Bene, è sufficiente inserire prima di "End Sub" questa riga: Set X = Nothing e lo spazio in memoria occupato dalla X sarà cancellato. L'istruzione andrà ripetuta per tutte le variabili caricate nella routine. Un'altro accorgimento, se si lavora con le UserForm, visto che anche l'apertura di una form costringe il computer a "caricare" in memoria tutti i dati che riguardano la Form stessa (compreso gli "oggetti" inseriti (TextBox, CommandButton, ecc.)) sarà quella di usare la stessa istruzione vista sopra, ma per azzerare la form dalla memoria. Se si chiude la form con in classico "UnLoad Me", è opportuno scrivere una riga in più di codice: Set UserForm1 = Nothing prima di End Sub. Oppure uscire con l'istruzione "End" prima di "End Sub", e questa istruzione provvede da sola a chiudere la form ed azzerare la form dalla memoria.
http://ennius.interfree.it/
Pagina 150
MANUALE VBA
X EXCEL
Doppi cicli For .. Next per eseguire ricerche, somme, numero di "presenti", ecc.. Un interessante esercizio, almeno lo spero, indirizzato all'intercettazione di dati, presenti su righe di un elenco, e contemporaneamente, su colonne dello stesso elenco, per restituzione di somme dei dati trovati, oppure di numero di dati simili presenti. L'esercizio si presta ad una molteplicità di utilizzi, variando i termini della ricerca, e del tipo di restituzione del risultato, oppure lo schema di come effettuare la ricerca. Presento quindi due varianti: una per la restituzione del numero di presenze di una variabile (una cella vuota, ma poteva essere su celle contenente il valore "pippo", oppure un numero specifico, o ancora su numeri superiori o inferiori ad un determinato valore, ecc.ecc.) l'altra, per ottenere il totale dei valori presenti in ogni colonna. In entrambi i casi, eseguiremo il controllo su un'intere colonne, iniziando dalla prima colonna richiamata dal ciclo,ottenendo il risultato in una cella della colonna adiacente, poi ci sposteremo sulla colonna successiva a quella su cui abbiamo ottenuto il totale, e così via per quante colonne vorremo. Se controlliamo la colonna A, otterremo il risultato nella colonna B, poi ci sposteremo a controllare la colonna C, otterremo il risultato nella colonna D, si sposteremo a controllare la colonna E, ecc. ecc. Visto che useremo una colonna intera, di volta in volta, e non sapendo quanto potrà essere lungo, in termini di righe il nostro elenco, dovremo ricorrere ad un accorgimento. Poichè nell'esempio simulo la presenza di celle vuote, e non possiamo usare la funzione End, per determinare la lunghezza elenco, useremo un "interruttore" che "stoppi" il ciclo di ricerca sulla colonna, dandoci il risultato e passando al controllo della colonna a lato. Questo "interruttore" potremo inserirlo manualmente in qualsiasi cella si preveda come limite massimo di un elenco, o al limite nell'ultima cella della colona, che è la 65536. L'importante è inserirlo, e come "interruttore" potremo usare qualsiasi valore, numero, testo o data, possibilmente diverso dal tipo di dati che la nostra colonna conterrà. Ma vediamo il primo esempio: Controllare e Contare il numero di celle vuote. Useremo il valore 2 come "interruttore". La routine prevede due cicli di ricerca (il primo per le colonne, con salto di una colonna (Step 2), con all'interno "annidato" un secondo ciclo che cercherà all'interno della colonna i dati voluti. Controlleremo le colonne dalla 1 alla 11 (cioè dalla A alla K) con il ciclo For X = 1 To 11. Sub ContaVuote() 'dichiarazione di una variabile (CL = Cella) come Object Dim CL As Object 'inizializzazione di un contatore cv (celle vuote) cv = 0 'inizio ciclo per spostarsi tra le colonne, saltandone una For X = 1 To 11 Step 2 'assegnazione alla variabile y dell'indirizzo di colonna X (la prima sarà quindi A:A) y = ActiveSheet.Columns(X).Address 'inizio del ciclo di ricerca del valore o cella, cercati per ogni cella della colonna y For Each CL In Range(y) 'in questo caso cerchiamo le celle vuote, se la cella sarà vuota, allora If CL.Value = "" Then 'incrementiamo di una unità il contatore cv = cv + 1 'se invece troviamo una cella col valore uguale a 2 ("interruttore"), allora ElseIf CL.Value = 2 Then 'nella cella a destra (offset) e quindi colonna B, scriviamo il valore del contatore, che a 'questo punto corrisponde al numero di celle vuote trovate. CL.Offset(0, 1).Value = cv 'riazzeriamo il contatore cv = 0 'usciamo da questo ciclo Exit For End If Next 'finito il ciclo sulla colonna appena controllata, continuiamo con Next il primo ciclo sulle 'colonne, che ci porterà alla colonna C, e poi a seguire per quante volte previsto (1 to 11 'Step 2) Next End Sub http://ennius.interfree.it/
Pagina 151
MANUALE VBA
X EXCEL
Se anzichè celle vuote, avessimo voluto controllare quante volte una parola, ad esempio "Rossi", come in un elenco di Clienti, fosse presente, sarebbe bastato cambiare l'istruzione di ricerca, così: If CL.Value = "Rossi" Then e sarebbe stato riportato il numero di quante volte "Rossi" era presente, dall'inizio colonna fino all'interruttore. Secondo esempio: Controllare e Sommare i valori presenti nelle celle per ottenere un totale. Diversificando il criterio di ricerca del secondo ciclo interno, potremo ottenere un totale personalizzato. In questo caso, operando su numeri, come "interruttore" sarà opportuno usare del testo. Nell'esempio mi limito a sommare tutti i valori presenti, colonna per colonna: Sub SommaValori() 'vedi sopra Dim CL As Object 'dichiarazione del tipo di variabile (totale) come Long. Se i numeri fossero con decimali, 'sarebbe necessario usare Double Dim totale As Long 'inizzializzazione di totale con valore = zero totale = 0 ''inizio ciclo per spostarsi tra le colonne, saltandone una For x = 1 To 11 Step 2 'assegnazione alla variabile y dell'indirizzo di colonna X (la prima sarà quindi A:A) y = ActiveSheet.Columns(x).Address 'inizio del ciclo di ricerca del valore, cercati per ogni cella della colonna y For Each CL In Range(y) 'qui iniziamo prima con la ricerca dell'interruttore, e useremo un nome (pippo), nel caso 'venga trovata la cella con questo testo, allora If CL.Value = "pippo" Then 'inseriamo il totale che si sarà formato dagli incrementi di totale, nella cella a lato CL.Offset(0, 1).Value = totale 'azzeriamo totale totale = 0 'usciamo dal ciclo interno Exit For 'se invece il valore di una cella sarà diverso da vuoto (quindi contiene un numero), allora ElseIf CL.Value "" Then 'sommiamo questo valore a totale totale = totale + CL.Value End If 'passiamo alla cella successiva, stessa colonna Next 'terminato il controllo fino alla cella interruttore, passiamo alla colonna successiva Next End Sub Sarà possibile eseguire controlli su ogni colonna, eliminando il comando Step 2 all'inizio del primo ciclo, ed ottenere il risultato, sia nel primo esempio, sia nel secondo, facendo scrivere il risultato NON nella colonna a lato, ma nella stessa colonna dove si effettua la ricerca, spostando con Offset la cella di destinazione: stessa colonna della ricerca, una riga sotto la cella dove stà l'interruttore, esempi: CL.Offset(1, 0).Value = cv (primo esempio) CL.Offset(1, 0).Value = totale (secondo esempio)
http://ennius.interfree.it/
Pagina 152
MANUALE VBA
X EXCEL
Controllare dati doppi con sostituzione del dato duplicato con dati presenti su altra cartella chiusa. (23/03/03) L'esercizio: In due colonne di una tabella (che chiameremo Tab1 per nostra comodità di riferimento) su un foglio di lavoro, (colonna A e B, per esempio) si possono trovare, per ogni riga, due valori eguali nelle due colonne. Vogliamo controllare la colonna B: se il valore presente sarà uguale al valore presente nella stessa riga della colonna A, vogliamo che il valore doppio della colonna B venga sostituito con il valore presente in un'altra tabella (che chiameremo TabOrigine) posta sul Foglio1 di una altra cartella chiusa presente sul nostro HD. Nella cartella chiusa è presente un elenco su più colonne (TabOrigine) : la prima colonna (a sinistra, la A), contiene valori univoci uguali ai valori presenti nella colonna A della Tab1. Vorremo quindi reperire il valore correlato posto nella seconda colonna ( la B ) della TabOrigine, e sostituirlo al valore doppio presente nella colla B della Tab1. Per fare questo sfrutteremo la Funzione CERCA.VERT posta all'interno del nostro ciclo di ricerca dati doppi. Il ciclo eseguirà un controllo dei valori presenti in ogni riga della colonna B della Tab1; nel caso che il valore a lato (colonna A) sia uguale, si effettua la funzione CERCA.VERT e si sostituisce il valore così trovato. Per evitare che Excel, trattandosi di un "collegamento" ad un altro foglio (e chiuso), presenti la finestra di conferma provenienza dati, inseriamo un semplice : Application.DisplayAlerts = False Sotto le immagini delle due tabelle; i nomi e i valori sono d'esempio. Tab1 TabOrigine
Come vediamo in Tab1, esistono valori uguali nelle righe 1, 2. 4, 6, 10. La routine controllerà i valori nelle due colonne, e nel caso siano uguali svolgerà la Funzione CERCA.VERT sulla TabOrigine andando a "pescare" il dato correlato al valore uguale al dato cercato nella colonna B (colore). Faremo inoltre controllare se il valore presente nella colonna B di Tab1 è diverso dal valore della stessa riga colonna A: in questo caso faremo scrivere in B la parola "Sconosciuto". Facciamo anche controllare se le celle della colonna B e rispettiva cella della colonna A saranno vuote: in questo caso passeremo alla riga successiva fino al termine del ciclo. Nel caso di dati doppi nella Tab1, ma il cui valore non venga trovato nella TabOrigine, nella cella colonna B apparirà il messaggio #N/D : valore non disponibile. E questo è il risultato prodotto dalle nostre istruzioni nella Tab1:
http://ennius.interfree.it/
Pagina 153
MANUALE VBA
X EXCEL
e queste le istruzioni. Come si nota, nella Funzione CERCA.VERT (in inglese VLOOKUP), si deve indicare il percorso dove risiede il file con la TabOrigine. Attenzione alla sintassi. Sub TrovaeSostituisci() Dim CL As Object 'sotto: indichiamo al ciclo For Each CL (per ogni cella) su quale Range agire For Each CL In Range("B1:B10") 'se la cella è vuota passa a Next (successivo) If CL.Value = "" Then GoTo 10 'se il valore della cella è uguale al valore della cella a destra (colonna A) If CL.Value = CL.Offset(0, -1).Value Then 'sotto: evita l'apparizione della finestra di conferma Application.DisplayAlerts = False 'allora nella cella (CL) poni la funzione cerca.vert (che inizia la ricerca in TabOrigine a 'partire dalla quinta riga colonna A, fino alla riga 65530 colonna B, e riporta il valore contenuto nella seconda colonna (2) CL.FormulaR1C1 = _ "=VLOOKUP(RC[-1],'C:\Archivio\[Dati.xls]Foglio1'!R5C1:R65530C2,2,FALSE)" 'sotto: se invece il valore nella celle B è diverso dalla cella in A (CL.Offset(0, -1) ElseIf CL.Value CL.Offset(0, -1).Value Then 'allora mi scrivi nella cella B la parola "SCONOSCIUTO" CL.Value = "SCONOSCIUTO" 'sotto: se invece (ancora) le due celle sono vuote, esci dalla routine. ATTENZIONE a 'questa istruzione: lo scopo è quello di far terminare il ciclo se saremo a fine elenco, 'istruzione necessaria specie se abbiamo impostato un Range lungo, e vogliamo appunto 'uscire quando saranno finiti i dati, altrimenti il ciclo prosegue fino alla fine anche se 'trovasse celle vuote, che per il codice vengono viste "uguali". E' NECESSARIO quindi 'NON lasciare righe vuote in A e in B se esistono dati nelle righe successive perchè la 'routine terminerebbe senza controllare i dati sottostanti. ElseIf CL.Value = "" And CL.Offset(0, -1).Value = "" Then Exit Sub End If 10: Next End Sub E' evidente che se i dati da riportare (TabOrigine) non fossero su un'altra cartella, ma sulla stessa cartella aperta dove risiede la Tab1, sarà ancora più semplice e basterà modificare il percorso che mira alla TabOrigine; quindi se questa fosse sulla stessa cartella sul foglio2, basterà scrivere: CL.FormulaR1C1 = "=VLOOKUP(RC[-1],Foglio2!R5C1:R65530C2,2,FALSE)" e non sarebbe più necessaria l'istruzione: Application.DisplayAlerts = False
http://ennius.interfree.it/
Pagina 154
MANUALE VBA
X EXCEL
Estrarre dati da un elenco e formare una tabella di riepilogo dati estratti Quando abbiamo necessità, e capita spesso, di volere un riepilogo di dati presenti in una tabella (elenco), estratti in base ad un nostro criterio di ricerca, possiamo adoperare una routine realizzata in vba. Ottenere una tabella che sia il frutto di un filtraggio di dati esistenti, sicuramente interesserà più di un "pellegrino", per questo propongo una routine che, nella sua semplicità, potrà essere adattata e modificata da chiunque. Facendo il solito esempio, supponiamo di avere un elenco di fatture da cui vorremo estrarre solo quelle relative ad un certo nominativo, e delle fatture, ci interessi avere l'importo di ognuna, per farne anche il totale. Potrebbe essere altresì un elenco di agenti, con a lato il fatturato, e di voler conoscere quanto ha fatturato un determinato agente. Di situazioni similari sono pieni i fogli di excel, quindi procediamo con l'esempio delle fatture. Nell'esempio avremo la colonna A da A1 a A200 dove avremo il nome del cliente o del fornitore, e nella colonna B l'importo di ogni fattura. Sfrutteremo la colonna E per ottenere il nostro riepilogo, ed useremo la cella C1 per scrivere il nome di cui vogliamo eseguire la ricerca. La cella F2 invece ci servirà per ottenere la somma totale degli importi delle fatture estratte(filtrate). Ognuno sarà libero di adattare i propri riferimenti sia alle colonne che contengono l'elenco dati, sia alla colonna dove creare la tabella riassuntiva, come pure alle celle che ospiteranno il criterio di ricerca (C1) o il totale (F2). Alcune considerazioni su come lavora la routine: dato un valore in C1 assegnato alla variabile X, per ogni cella del range A1:A200 verrà svolta la ricerca dello stesso valore (X), se trovato, verrà copiato il valore che è nella cella a destra (Offset), e verrà copiato nella colonna E, dove col metodo End, verrà cercata la prima cella libera e lì, incollato il valore copiato. Ricordo che con End(xlDown).Offset ecc. E' necessario far trovare le prime due celle da inizio selezione (E1) occupate da qualsiasi cosa ( per chiarimenti leggere eventualmente il paragrafo "Copia/Incolla2" in questa sezione). Ad ogni lancio routine, facciamo pulire le celle in E, prima di iniziare il ciclo di ricerca, copia, incolla. Nella cella F2 metteremo la funzione =SOMMA(F3:F203) per avere il totale degli importi filtrati. A posto di scrivere un nome nella cella C1, sarà possibile inserire una casella di riepilogo (combobox) con le proprietà LinkedCell = C1 e ListFillRange = A1:A200. Questo l'esempio:
E queste le istruzioni: Sub riassumi() Dim CL As Object Dim X Application.ScreenUpdating = False Range("E3:E200").ClearContents X = Range("C1").Value For Each CL In Range("A1:A200") If CL = X Then http://ennius.interfree.it/
Pagina 155
MANUALE VBA
X EXCEL
CL.Offset(0, 1).Select Selection.Copy Range("E1").Select Selection.End(xlDown).Select ActiveCell.Offset(1, 0).Select 'incollo i dati. With ActiveCell Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _ :=False, Transpose:=False End With End If Next Application.CutCopyMode = False End Sub
http://ennius.interfree.it/
Pagina 156
MANUALE VBA
X EXCEL
Elenchi zebrati. (11/05/03) Ovvero: come differenziare il colore delle righe di un elenco, alternando una riga colorata ed una no oppure una riga di un colore e un'altra di un'altro colore. Prendendo spunto da una domanda postami sulla Formattazione Condizionale, nasce questo articolo che presento con varie soluzioni, la prima delle quali mi ha sorpreso non poco. Premesso che il buon vecchio Excel ci consente a volte di crearci le nostre istruzioni in molte maniere, per ottenere lo stesso risultato, questa volta, seguendo una procedura che sfruttando il metodo Add, consente di definire una condizione nella creazione di una Formattazione Condizionale tramite vba, è venuto fuori che la sintassi della formula usata per definire la condizione, DEVE ESSERE IMPOSTATA CON FUNZIONI IN ITALIANO. Strano ma vero, e non so il perchè, se uso la normale sintassi in inglese tipica del vba, non si ottiene il risultato voluto. Ma vediamo l'origine del caso : Si vuole alternare il colore delle righe di un elenco, in modo che i dati siano più facilmente leggibili o stampabili, cioè desideriamo creare un "elenco zebrato". Un ragionamento logico è: identifichiamo le righe che corrispondono al numero pari, per esempio, e coloriamo quelle (quindi la 2,4,6,8,,10, ecc). Allora sfruttiamo la Funzione "RESTO" (in inglese=MOD) la quale fornisce come risultato di una divisione, soltanto il resto. Sapendo questo, è facile impostare l'istruzione : se un numero di riga diviso 2 dà come resto zero, allora......infatti tutti i numeri pari diviso 2 danno sempre come resto un bello zero, cioè non ci sono decimali. (stiamo parlando di Resto, non di Quoziente), e quindi nasce la formula, vediamola prima in italiano: =SE(RESTO(RIF.RIGA();2)=0;VERO;FALSO) la stessa in inglese, da usare nel codice: =IF(MOD(ROW(),2)=0,TRUE,FALSE) Impostiamo quindi tutta la procedura: 'Reperiamo la zona di cui vogliamo l'elenco zebrato Range("A1:J20").Select 'cancelliamo nella zona selezionata eventuali formattazioni condizionali presenti, istruzione 'comunque necessaria per poter applicare il metodo Add Selection.FormatConditions.Delete 'impostiamo la format. condiz. tramite la formula Selection.FormatConditions.Add Type:=xlExpression, Formula1:= _ "=IF(MOD(Row(),2)=0,true,false)" 'e a seguito del reperimento delle righe, applichiamo il colore alle righe Selection.FormatConditions(1).Interior.ColorIndex = 35 Orrore, Orrore, il debugger si lamenta immediatamente indicando un errore nella composizione della formula; strano, si controlla, ci sembra scritta correttamente, virgole comprese (in vba i punti e virgola della stessa formula in versione Foglio di lavoro, si devono scrivere come virgole). Vuoi vedere che il debugger protesta proprio per le virgole? Allora le sostituiamo con punti e virgola e proviamo. Questa volta il debugger non dice niente, ma neanche succede niente. Allora tento l'ultima carta, vacca miseria, un controsenso, ma tanto vale...e...così funziona. Giuro che non so perchè. Questa è la macro: Sub Colora() Range("A1:J20").Select Selection.FormatConditions.Delete Selection.FormatConditions.Add Type:=xlExpression, Formula1:= _ "=SE(RESTO(RIF.RIGA();2)=0;VERO;FALSO)" Selection.FormatConditions(1).Interior.ColorIndex = 35 End Sub e questo il risultato:
http://ennius.interfree.it/
Pagina 157
MANUALE VBA
X EXCEL
Esistono comunque altre procedure che si possono attivare per ottenere lo stesso risultato; una procedura che propongo, semplice da capire, e in due versioni: colorare una sola riga, o colorare le righe a colori alterni, inoltre tratteggiando le celle delle righe selezionate per migliorare la vista a video; la prima: Sub Zebrauno() Set zona = Range("A1:F15") 'si imposta la zona su cui agire For Each rw In zona.Rows 'per ogni riga delle righe presenti in zona If rw.Row Mod 2 = 0 Then 'se il numero riga diviso 2 come resto da zero, allora rw.Interior.ColorIndex = 35 'si colora la riga rw.Borders.LineStyle = xlDashDotDot 'si contornano le celle delle righe con il tratteggio End If Next rw 'si passa alla riga successiva End If e questo il risultato:
la procedura a due colori, simile alla precedente: Sub Zebradue Set zona = Range("A1:F15") For Each rw In zona.Rows If rw.Row Mod 2 = 0 Then rw.Interior.ColorIndex = 35 rw.Borders.LineStyle = xlDashDotDot 'xlContinuous Else 'con Else si interviene sulle altre righe (che forniscono un resto diverso da zero) rw.Interior.ColorIndex = 36 'cambia il colore rw.Borders.LineStyle = xlDashDotDot 'xlContinuous End If Next rw End Sub e questo il risultato:
http://ennius.interfree.it/
Pagina 158
MANUALE VBA
X EXCEL
Se poi si volessero togliere le zebrature e ripristinate il foglio all'origine, basterà predisporre queste semplici istruzioni da collegare ad un altro pulsante: per l'elenco ad una sola riga colorata: Sub canctut() Set zona = Range("A1:F15") For Each rw In zona.Rows If rw.Row Mod 2 = 0 Then rw.Interior.ColorIndex = xlNone rw.Borders.LineStyle = xlNone End If Next rw End Sub per l'elenco con entrambe le righe colorate: Sub canctutdue() Set zona = Range("A1:F15") For Each rw In zona.Rows If rw.Row Mod 2 = 0 Then rw.Interior.ColorIndex = xlNone rw.Borders.LineStyle = xlNone Else rw.Interior.ColorIndex = xlNone rw.Borders.LineStyle = xlNone End If Next rw End Sub Se poi preferite lavorare di meno, date in occhiata all'articolo "Elenchi zebrati" presente sul sito, sezione "Primi passi". Buon Lavoro.
http://ennius.interfree.it/
Pagina 159
MANUALE VBA
X EXCEL
Eliminare dati doppi di coppie di nuneri. Ovvero: ricerca ed eliminazione righe se i valori di due celle contigue sono uguali a valori di due altre celle contigue, nelle stesse colonne di un elenco. esempio A B 1 10 20 2 7 32 3 10 20 4 8 15 I valori nelle due celle della riga 3 sono uguali ai valori nelle due celle della riga 1. Andrà eliminata la riga 3. Una problematica peraltro evidenziata da alcune richieste, di cui viene presentata una soluzione, non unica, ma che possiede il grande pregio della eccezionale velocità di esecuzione utilissima in particolare con lunghi e lunghissimi elenchi (50.000 e oltre righe) dove le coppie valori da controllare impegnano notevole tempo se si usano i normali cicli a due variabili. Questa routine è stata realizzata da MICHELE email [email protected] Il sistema escogitato da Michele, in pratica, utilizza una colonna esterna all'elenco (la H) dove "concatena" i valori presenti nelle coppie di celle da esaminare, ottenendo un unico numero non confondibile, ma uguale ad altre "concatenazioni" di stesse coppie di numeri se presenti nelle righe dell'elenco, e crea quindi un elenco di pari lunghezza rispetto all'origine. Poi lavora su questa colonna : identifica il valore della prima cella di questa zona (assegnata alla variabile "MioIntervallo"), e con un ciclo For Each .... Next controlla partendo dalla fine (Step -1) e a salire, se trova un valore uguale alla prima cella dell'elenco; se la trova uguale, viene eliminata l'intera riga. Finito il controllo dei valori della prima cella, si passa alla riga successiva (alla prima (Riga + 1)), ripetendo il controllo dalla fine, a salire. Finito il ciclo dei controlli, seleziona tutta la colonna usata come "MioIntervallo", e cancella i contenuti rimasti. La soluzione di Michele, oltre che intelligente, è veramente veloce: su un Pentium4 1800, su un elenco di 48 coppie di numeri ripetute 10 volte, accodando fino ad ottenere un elenco di 480 righe, ha totalizzato 1 secondo nell'eliminazione di tutte le righe ripetute. Un bel risultato!!! Questa la routine, comprensiva delle istruzioni per il conteggio dei tempi di esecuzione. Sub elidodue() Dim Riga Dim MioIntervallo As Range Dim CL As Object Dim D1, D2 As Date Dim tempoimpiegato As String D1 = Time Application.ScreenUpdating = False Range("A1").End(xlDown).Select R = ActiveCell.Row For Y = 1 To R Cells(Y, 8).Select ActiveCell = Cells(Y, 1).Value & Cells(Y, 2).Value Next Set MioIntervallo = Range(Cells(1, 8), Cells(R, 8)) Riga = 2 For Each CL In MioIntervallo For z = R To Riga Step -1 If CL.Value = Cells(z, 8) Then Sheets(1).Cells(z, 8).EntireRow.Delete Next Riga = Riga + 1 Next Riga = 2 Columns("H:H").Select Selection.ClearContents Range("A1").Select http://ennius.interfree.it/
Pagina 160
MANUALE VBA
X EXCEL
D2 = Time tempoimpiegato = Format(D2 - D1, "hh:mm:ss") MsgBox "Tempo impiegato: " & tempoimpiegato End Sub L'interpretazione delle istruzioni, anche se presuppone una certa conoscenza del codice vba, non è difficile, quindi gli interessati potranno facilmente adattare le istruzioni ai propri riferimenti. Un grazie a Michele, specie da quei "pellegrini" che gradiranno questa sua interpretazione di un problemino comune a molti.
http://ennius.interfree.it/
Pagina 161
MANUALE VBA
X EXCEL
Eliminazione dati doppioni da un elenco Impiego: ricerca ed eliminazione intera riga di dati doppi contenuti in un elenco (tabella) A gentile richiesta (??), pensando che potrà interessere altri visitatori, propongo questa macro realizzata in seguito ad una domanda rivoltami: automatizzare con il Vba, la ricerca in un elenco, di dati doppi, e loro eliminazione insieme ad eventuali dati correlati (quindi eliminazione dell'intera riga). E' un applicazione utile quando si debba avere a che fare con lunghi elenchi di dati, come per esempio una gestione di articoli di un magazzino, dove può capitare di inserire più volte una stessa voce, e dove quindi sia necessario rivedere e correggere l'elenco. Il funzionamento di queste istruzioni è semplice: si basa prima su un ordinamento A-Z dei dati presenti, basato su una chiave di ordinamento, che potrebbe essere il codice articolo (contenuti in questo esempio nella colonna B) con scartamento di eventuali righe vuote (che bloccherebbero il ciclo Do While...Loop) e nella ricerca del valore di ogni cella (a scalare) con i valori delle successive : nel caso vengano riscontrati valori uguali, la riga che contiene il doppione verrà eliminata. Ognuno potrà modificare i Range su cui eseguire il controllo, adattandoli alle proprie esigenze. Questo è il codice da associare ad un pulsante che risiederà nelle stesso foglio su cui si esegue la ricerca : Sub EliminaDoppioni() 'Questo codice ordina i dati nella seconda 'colonna del foglio Dati ed elimina le righe che 'contengono dati duplicati. Application.ScreenUpdating = False Range("B4:B800").Select Selection.Sort Key1:=Range("B4"), Order1:=xlAscending, Header:=xlGuess, _ OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom, _ DataOption1:=xlSortNormal Set currentCell = Worksheets("Dati").Range("B4") Do While Not IsEmpty(currentCell) Set nextCell = currentCell.Offset(1, 0) If nextCell.Value = currentCell.Value Then currentCell.EntireRow.Delete End If Set currentCell = nextCell Loop Range("B4").Select End Sub
http://ennius.interfree.it/
Pagina 162
MANUALE VBA
X EXCEL
Estrarre dati casuali da un elenco o tabella. (29/03/03) Trovo interessante segnalare un esercizio scaturito da una richiesta ricevuta : l'estrazione casuale di nomi, (propri, di città, di animali, di piante, ecc.ecc.), ma vale anche per l'estrazione di numeri. Mentre per i numeri la Funzione ROUND() è specifica, proprio perchè valori numerici, per i nomi (o comunque testo o date) bisogna creare un Indice (e quindi un numero) che identifichi un determinato valore in formato testo o data. Potremmo creare degli Array i quali restituiscono il valore dichiarato in funzione dell'indice rappresentato, ma sceglieremo una strada diversa, che ci consenta di poter modificare non solo il nostro testo come più ci piace, ma di decidere a piacere da quante parole sarà formato il nostro elenco. Useremo quindi o una sola colonna dove scrivere, riga dopo riga, le parole che vorremo randomizzare creando un elenco o tabella, oppure più righe e più colonne per avere una tabella ancora più ampia. In questo modo con il numero di riga e/o di colonna, avremo i nostri numeri da poter randomizzare. Vediamo quindi i passaggi dei due casi. Area posta su una sola colonna, la colonna A che è la prima identificazione dell'area che contiene i dati; con UsedRange identifichiamo l'area qualunque sia la lunghezza. Set zona = ActiveSheet.UsedRange trovare da quante righe è formata l'area così reperita, ed assegnazione del valore numerico ad una variabile ( la x ) x = zona.Rows.Count (*)estrazione di un numero casuale compreso tra 1 ed il valore che la variabile x conterrà; poichè per il vba i numeri partono da zero (e noi non avremo una riga zero, ma partiremo sempre da riga uno), aggiungiamo + 1 al valore casuale estratto (infatti, supponendo di avere 50 righe, in realtà l'intervallo sarebbe da zero compreso a quarantanove compreso, che fanno appunto 50 numeri). Il valore numerico così trovato rappresenterà il numero di riga. Questo valore lo assegnamo ad una variabile (quale) quale = Int(x * Rnd) + 1 ora preleviamo con un'altra variabile ( la Y ) il valore presente nella cella rappresentata dalla riga quale indicando la colonna 1 ( Cells(quale, 1), supposto di avere l'elenco nella colonna A ) che sarà un nome, e formiamo un messaggio che mostrerà il nome così trovato. Y = Cells(quale, 1).Value MsgBox Y Precisazione: se l'elenco fosse invece in un'altra colonna, la C per esempio ( che è la numero 3 ) dovremmo nell'istruzione sopra descritta, indicare con 3 l'indice colonna, così: Y = Cells(quale, 3).Value Per evitare la ripetitività nella sequenza dei valori restituiti dovremo adoperare la funzione Randomize che utilizza numero per inizializzare il generatore di numeri casuali della funzione Rnd assegnandogli un nuovo valore. Se numero viene omesso, il valore restituito dal timer di sistema verrà utilizzato come nuova base. Se Randomize non viene utilizzata, quando la funzione Rnd (senza argomenti) viene chiamata per la prima volta, utilizza come base lo stesso numero. Per le chiamate successive la funzione utilizzerà l'ultimo numero generato. E questa la routine: Sub Acaso() Set zona = ActiveSheet.UsedRange x = zona.Rows.Count Randomize quale = Int(x * Rnd) + 1 Y = Cells(quale, 1).Value MsgBox Y End Sub Se invece la tabella sarà stata creata su più colonne, sempre supponendo di avere l'inizio elenco nella colonna A (la prima) avremo bisogno di contare anche da quante colonne è formata , sempre rintracciando la nostra area con l'UsedRange, con queste modifiche: z = zona.Columns.Count e di creare una casualità anche per le colonne, con: dove = Int(z * Rnd) + 1 e di reperire quindi un indice cella che oltre al numero di riga porterà anche il numero di colonna, con http://ennius.interfree.it/
Pagina 163
MANUALE VBA
X EXCEL
Y = Cells(quale, dove).Value ed alla fine la routine completa in questo caso sarà: Sub Acasodue() Set zona = ActiveSheet.UsedRange x = zona.Rows.Count z = zona.Columns.Count Randomize quale = Int(x * Rnd) + 1 dove = Int(z * Rnd) + 1 Y = Cells(quale, dove).Value MsgBox Y End Sub ATTENZIONE: tutte queste routine si basano su una tabella che inizi dalla riga 1 e dalla colonna A. La variabile Y identifica il numero di riga e di colonna generati dalla randomizzazione, e quindi numeri casuali compresi tra 1 e il numero di righe e di colonne che formeranno la nostra tabella. Poichè l'istruzione Cells (Cells(quale, dove).Value) usata su un foglio di lavoro inizia a contare dalla prima cella (la A1) per trovare la cella Y rappresentata dai valori quale e dove, SE LA TABELLA iniziasse dalla riga 3 e dalla colonna C (anch'essa la numero 3), avremo bisogno di aggiungere questi numeri (3 e 3) al numero indice che serve a generare il numero casuale. Il motivo di ciò è anticipato al punto con l'asterisco rosso (*) più sopra, e quindi le nostre istruzioni verrebbero modificate su queste due righe: quale = Int(x * Rnd) + 4 dove = Int(z * Rnd) + 4 anzichè quale = Int(x * Rnd) + 1 dove = Int(z * Rnd) + 1 Infatti, sempre rifacendo l'esempio delle 50 righe, avremmo bisogno di partire dalla riga 3, e quindi avremo 3 righe, + 1 per correggere l'indice zero, = 4. in questo modo otteniamo che se la randomizzazione del valore 50 (rappresentato da x) generasse zero, avremo 0 + 4 che indica la riga 3, se invece il numero casuale generato fosse 49 (limite superiore del valore 50 visto con inizio da zero), il numero di riga corrispondente sarebbe 53. Lo stesso vale per l'indice colonna se la colonna fosse la C. Spero di essere stato sufficientemente chiaro.
http://ennius.interfree.it/
Pagina 164
MANUALE VBA
X EXCEL
Estrarre dati da un elenco e formare una tabella di riepilogo dati estratti Quando abbiamo necessità, e capita spesso, di volere un riepilogo di dati presenti in una tabella (elenco), estratti in base ad un nostro criterio di ricerca, possiamo adoperare una routine realizzata in vba. Ottenere una tabella che sia il frutto di un filtraggio di dati esistenti, sicuramente interesserà più di un "pellegrino", per questo propongo una routine che, nella sua semplicità, potrà essere adattata e modificata da chiunque. Facendo il solito esempio, supponiamo di avere un elenco di fatture da cui vorremo estrarre solo quelle relative ad un certo nominativo, e delle fatture, ci interessi avere l'importo di ognuna, per farne anche il totale. Potrebbe essere altresì un elenco di agenti, con a lato il fatturato, e di voler conoscere quanto ha fatturato un determinato agente. Di situazioni similari sono pieni i fogli di excel, quindi procediamo con l'esempio delle fatture. Nell'esempio avremo la colonna A da A1 a A200 dove avremo il nome del cliente o del fornitore, e nella colonna B l'importo di ogni fattura. Sfrutteremo la colonna E per ottenere il nostro riepilogo, ed useremo la cella C1 per scrivere il nome di cui vogliamo eseguire la ricerca. La cella F2 invece ci servirà per ottenere la somma totale degli importi delle fatture estratte(filtrate). Ognuno sarà libero di adattare i propri riferimenti sia alle colonne che contengono l'elenco dati, sia alla colonna dove creare la tabella riassuntiva, come pure alle celle che ospiteranno il criterio di ricerca (C1) o il totale (F2). Alcune considerazioni su come lavora la routine: dato un valore in C1 assegnato alla variabile X, per ogni cella del range A1:A200 verrà svolta la ricerca dello stesso valore (X), se trovato, verrà copiato il valore che è nella cella a destra (Offset), e verrà copiato nella colonna E, dove col metodo End, verrà cercata la prima cella libera e lì, incollato il valore copiato. Ricordo che con End(xlDown).Offset ecc. E' necessario far trovare le prime due celle da inizio selezione (E1) occupate da qualsiasi cosa ( per chiarimenti leggere eventualmente il paragrafo "Copia/Incolla2" in questa sezione). Ad ogni lancio routine, facciamo pulire le celle in E, prima di iniziare il ciclo di ricerca, copia, incolla. Nella cella F2 metteremo la funzione =SOMMA(F3:F203) per avere il totale degli importi filtrati. A posto di scrivere un nome nella cella C1, sarà possibile inserire una casella di riepilogo (combobox) con le proprietà LinkedCell = C1 e ListFillRange = A1:A200. Questo l'esempio:
E queste le istruzioni: Sub riassumi() Dim CL As Object Dim X Application.ScreenUpdating = False Range("E3:E200").ClearContents X = Range("C1").Value For Each CL In Range("A1:A200") If CL = X Then http://ennius.interfree.it/
Pagina 165
MANUALE VBA
X EXCEL
CL.Offset(0, 1).Select Selection.Copy Range("E1").Select Selection.End(xlDown).Select ActiveCell.Offset(1, 0).Select 'incollo i dati. With ActiveCell Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _ :=False, Transpose:=False End With End If Next Application.CutCopyMode = False End Sub
http://ennius.interfree.it/
Pagina 166
MANUALE VBA
X EXCEL
Estrarre dati da un elenco e inviare il nuovo elenco in stampa. A seguire dall'esempio della precedente pagina (Estrarre dati da elenco), propongo un esercizio un pò più articolato, e che spesso ci troviamo ad affrontare: disponendo di un database (elenco clienti, elenco fornitori, agenda indirizzi, elenco prodotti, ecc.ecc), vogliamo un riepilogo (o se preferite un "elenco filtrato") in modo da formare un nuovo elenco da mandare in stampa. Premesso che ci sono diversi modi per ottenerlo, (compreso un ordinamento basato su chiave di ricerca che possiamo variare di volta in volta (agendo sulla chiave di ricerca), con selezione anche manuale dell'area che ci interessa e stampare poi questa selezione), questo esercizio è basato sulla ricerca di tutti i nomi che iniziano per una determinata lettera dell'alfabeto. Una InputBox ci chiederà di quale lettera desideriamo eseguire il filtraggio ed estrazione dati , e le istruzioni provvederanno a comporre la nuova tabella con i nomi e i dati correlati che vorremo. Ho diviso le istruzioni in due macro: una per l'estrazione dati ed una per la stampa, ma è possibile unire le istruzioni in unica macro: estrarre i dati e mandarli in stampa in un colpo solo. Attenzione!!: la ricerca è CaseSensitive, cioè è sensibile alle maiuscole/minuscole. Se formiamo il database con nomi di cui la prima lettera è scritta in maiuscolo (per esempio: Bellini) e nella inputbox, per la ricerca scriveremo b (minuscolo) saranno trovati solo i nomi con la b minuscola (e i Bellini no). Ricordatevi quindi di definire un sistema di ricerca che corrisponda al sistema usato per l'archiviazione dati. In questo esempio posizioniamo il database su un foglio (foglio2) mentre il riepilogo lo facciamo sul foglio 1 .Va bene qualunque altro foglio, è solo per lavorare con la ricerca dati da eseguire su un foglio "remoto" in modo da accontentare anche coloro che si chiedono come "pescare" i dati se si trovano non sullo stesso foglio del riepilogo. La foto sotto mostra un elenco a tre campi, (nominativo, indirizzo, città) sul foglio2:
Queste sotto invece mostrano la InputBox con la richiesta di quale lettera immettere (nell'esempio la "b"), e il foglio1 con l'elenco dei dati estratti relativi a tutti i nomi che nel database cominciano con la "b", ed i pulsanti associati alle due macro:
http://ennius.interfree.it/
Pagina 167
MANUALE VBA
X EXCEL
Sotto vediamo la zona che va in stampa. La macro interessata alla stampa, individua, a partire dalla riga 3, e per le tre colonne, l'ultima riga occupata, seleziona l'area così identificata, e la invia alla stampante. Soluzione necessaria visto che non è possibile stabilire a priori quanto sarà lungo l'elenco degli estratti.
Queste le due macro e relative spiegazioni: Sub Riassumi() Dim CL As Object Dim x, messaggio, titolo 'questa sotto evita il saltellamento dei fogli a schermo Application.ScreenUpdating = False 'siamo sul foglio1, si pulisce un'area da A3 a C200. Se gli elenchi estratti fossero più 'lunghi, basterà aumentare il range Range("A3:C200").ClearContents 'si imposta il messaggio e il titolo della inputbox messaggio = "Scrivi l'iniziale dei nomi da estrarre" titolo = "Estrai dati" 'sotto: rendiamo x uguale a ciò che scriveremo nell'inputbox x = InputBox(messaggio, titolo) If x = "" Then Exit Sub 'se non scriviamo niente (x = vuoto) si esce dalla routine 'sotto: per ogni cella (CL) sul foglio2 nel range che va da A1 a A200 For Each CL In Sheets(2).Range("A1:A200") Sheets(2).Select 'riseleziono il foglio2 al rientro del ciclo (Next) 'sotto: se la prima lettera a sinistra del valore che sarà nella cella è uguale alla prima lettera in x, (con 'Left(CL, 1) si confronta la prima lettera del nome che è nelle celle), allora If Left(CL, 1) = Left(x, 1) Then 'sotto: si seleziona sul foglio2 dalla cella trovata alla 3^ cella, stessa riga Sheets(2).Range(CL, CL.Offset(0, 2)).Select Selection.Copy 'si copia la selezione Sheets(1).Select 'ci si sposta sul foglio1 Range("A1").Select 'si seleziona la prima delle celle del nostro elenco di destinazione. (ricordo le prime due celle da far trovare occupare usando l'istruzione End) Selection.End(xlDown).Select 'si cerca l'ultima cella occupata (la seconda) ActiveCell.Offset(1, 0).Select 'si seleziona la cella sotto che è vuota 'incollo i dati (continuando a cercare (sopra) la prima cella libera. http://ennius.interfree.it/
Pagina 168
MANUALE VBA
X EXCEL
With ActiveCell Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _ :=False, Transpose:=False End With End If Next 'continuo il ciclo Sheets(1).Select 'alla fine dell'estrazione ritorno sul foglio1 Range("C1").Select 'seleziono la cella C1 Application.CutCopyMode = False End Sub quella per la stampa: Sub stampa() Worksheets("Foglio1").Select Dim x, y 'dichiarazione di variabili y = Range("A3:C3").Address 'con y prendo i riferimenti dalla colonna A alla C riga 3 x = Range("A3").End(xlDown).Address 'con x prendo il riferimento alla ultima cella 'occupata nella colonna A partendo dalla cella A3 Range(y, x).Select 'seleziono tutta l'area identificata dai riferimenti y x PrintArea = Selection 'dichiaro che l'area di stampa corrisponde alla selezione Selection.PrintOut Copies:=1, Collate:=True 'invio la selezione alla stampante Range("C1").Select End Sub 20/02/03. L'amico Michele ( [email protected] ) mi suggerisce di fornire la soluzione alla limitazione maiuscole/minuscole che limita l'uso della routine di ricerca, con l'impiego delle funzioni LCase e UCase in modo che qualunque sia il formato della lettera scritta nell'inputbox, la ricerca venga comunque effettuata. Ritengo giusto il suggerimento e a questo punto aggiungo anche le istruzioni per l'ordinamento alfabetico con chiave di ordinamento basato sul nome, in modo che l'elenco degli estratti si presenti per ordine alfabetico. Questa la routine modificata, aggiungo le spiegazioni solo alle nuove istruzioni : Sub riassumiordina() Dim CL As Object Dim x, messaggio, titolo Application.ScreenUpdating = False Range("A3:C200").ClearContents messaggio = "Scrivi l'iniziale dei nomi da estrarre" titolo = "Estrai dati" x = InputBox(messaggio, titolo) If x = "" Then Exit Sub W = LCase(x) 'con LCase si assimila la variante x come lettera minuscola Z = UCase(x) 'con UCase si assimila la variante x come lettera maiuscola For Each CL In Sheets(2).Range("A1:A200") Sheets(2).Select 'sotto: se la prima lettera del nome trovato è minuscola o maiuscola.... If Left(CL, 1) = Left(W, 1) Or Left(CL, 1) = Left(Z, 1) Then Sheets(2).Range(CL, CL.Offset(0, 2)).Select Selection.Copy Sheets(1).Select Range("A1").Select Selection.End(xlDown).Select ActiveCell.Offset(1, 0).Select http://ennius.interfree.it/
Pagina 169
MANUALE VBA
X EXCEL
'incollo i dati. With ActiveCell Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _ :=False, Transpose:=False End With End If Next Sheets(1).Select 'ritornati sul foglio1 si seleziona l'elenco estratti e si ordina alfabeticamente y = Range("A3:C3").Address x = Range("A3").End(xlDown).Address Range(y, x).Select Selection.Sort Key1:=Range("A3"), Order1:=xlAscending, Header:=xlGuess, _ OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom, _ DataOption1:=xlSortNormal 'volendo mandare subito in stampa l'elenco, senza usare il secondo pulsante, e visto che 'l'elenco è già selezionato, possiamo aggiungere anche le istruzioni per la stampa: PrintArea = Selection 'dichiaro che l'area di stampa corrisponde alla selezione Selection.PrintOut Copies:=1, Collate:=True 'invio la selezione alla stampante Range("C1").Select Application.CutCopyMode = False End Sub File scaricabile e consultabile (prima routine) : Estraidati2000.zip 14 Kb
http://ennius.interfree.it/
Pagina 170
MANUALE VBA
X EXCEL
Evitare Ripetizioni (nell'inserimento di dati in una tabella) (14/01/03) Praticamente si tratta di un "Controllo inserimento dati". Un'altra dimostrazione che tramite Vba, si possono comporre istruzioni che mirino allo stesso scopo, variando completamente il modo di controllo. Questa volta ci occuperemo della trasposizione in codice di una funzione del foglio di lavoro : la funzione =CONTA.SE Questa funzione, l'abbiamo già vista impiegare in " Usare Convalida (2)" , nella sezione "formule". L' istruzione controlla se le celle di un determinato Range contengono lo stesso valore che si troverà in una cella (che useremo come vettore per la ricerca), se tale vettore sarà trovato sarà pari a 1, e la formula riscontra VERO, altrimenti restituisce FALSO. L'istruzione andrebbe compilata così e verrebbe posta in una cella, per esempio la C1: =CONTA.SE(A1:A100;B1)=1 e dice praticamente: controlla se il valore che è in B1 è presente nel range che va da A1 a A100, se lo trovi scrivi (in C1) VERO, altrimenti scrivi FALSO. Questo potrebbe essere un modo alternativo per il controllo dell'inserimento dati, peraltro ottenibile usando la "Convalida Dati" di Excel. In genere questi controlli si eseguono quando si debbano introdurre dati "univoci", come un numero di fattura, oppure un codice articolo. Volendo quindi applicare in Vba questa funzione, faremo un esempio: supponiamo di avere una colonna dove inseriremo il numero di fattura, e lo posizioniamo nella colonna A, a partire dalla prima cella. Seguendo la falsariga della funzione sopracitata, avremo bisogno di una cella che funzioni da vettore, e di una cella dove "alloggiare" la formula stessa. Visto che ci apprestiamo ad usare del codice, perchè non usare una finestra di introduzione dati, una InputBox, che renderà un pò più "professionale" il nostro lavoro? Avremo bisogno anche di "convertire" il risultato della funzione (VERO, FALSO) in un istruzione che ci consenta di annullare la scrittura se il numero fattura che vorremo inserire esiste già, mentre vorremo che il numero stesso venga scritto, se non presente, e venga scritto nella riga successiva all'ultimo numero presente nel range previsto. Ecco quindi una routine che ci consente di fare quanto detto (in verde sono le spiegazioni): Sub introduzionedati() 'dichiarazione delle variabili per la InputBox Dim messaggio, titoto, Valore titolo = "Introduzione dati" 'ciò che vedremo nella barra del titolo dell'inpputbox messaggio = "Introduci il numero" 'il testo che appare come istruzione da seguire Valore = InputBox(messaggio, titolo) 'Valore sarà uguale al dato inserito nella casella di 'testo della inputbox. If Valore = "" Then Exit Sub 'se non scriveremo niente, si esce dalla routine 'anzichè una cella visibile, assegno la cella B60000 a contenere il valore che fungerà da 'vettore (valore), cioè il secondo argomento della funzione Cerca.Se (CountIf) Range("B60000") = Valore 'assegno a X la cella A60000 (una cella sicuramente non visibile), che sarà la cella dove 'avremo il risultato della funzione (come per la C1 della formula iniziale) Set X = Range("A60000") 'pongo in A60000 (X) la formula =CONTA.SE X.Formula = "=COUNTIF(A1:A100,B60000)=1" 'inizio il ciclo di controllo sulla X, cioè la cella A60000 If X.Value = True Then 'se il valore sara uguale a vero, cioè sarà stato trovato il numero 'nell'elenco previsto A1:A100 MsgBox "Valore Già presente" 'si viene avvisati da un messaggio X = "" 'viene pulita la cella A60000 Range("B60000") = "" 'viene pulita la cella B60000 Exit Sub 'si esce dalla routine Else 'invece (in caso contrario, cioè se il numero non è presente nell'elenco) Range("A1").End(xlDown).Select 'seleziono l'ultima cella occupata a partire dalla A1 ActiveCell.Offset(1, 0).Select 'seleziono la cella sotto che è vuota ActiveCell = Valore 'nella cella attiva copio il vettore (valore) scritto nella inputbox X = "" 'poi viene pulita la cella A60000 Range("B60000") = "" 'poi viene pulita la cella B60000 End If End Sub http://ennius.interfree.it/
Pagina 171
MANUALE VBA
X EXCEL
E questa è la variante con la descrizione nel messaggio della InputBox dell'ultimo numero presente, in modo che si sappia comunque qual'è l'ultimo numero inserito (non ripeto le spiegazioni già fornite nella routine sopra): Sub introducicontrolla() Dim Z Z = Range("A1").End(xlDown).Value 'a Z viene assegnato il valore presente nell'ultima 'cella occupata partendo dalla A1 Dim messaggio, titoto, Valore titolo = "Introduzione dati" messaggio = "Introduci il numero, l'ultimo usato è il N° " & Z & "" 'ho aggiunto nel 'messaggio l'ultimo numero presente (Z) Valore = InputBox(messaggio, titolo) If Valore = "" Then Exit Sub Range("B60000") = Valore Set X = Range("A60000") X.Formula = "=COUNTIF(A1:A100,B60000)=1" If X.Value = True Then MsgBox "Valore Già presente" X = "" Range("B60000") = "" Exit Sub Else Range("a1").End(xlDown).Select ActiveCell.Offset(1, 0).Select ActiveCell = Valore X = "" Range("B60000") = "" End If End Sub Usare la Funzione Timer Esempio di Utilizzo : Fare lampeggiare celle per richiamare attenzione. Può necessitare di volere richiamare l'attenzione dell'operatore sul verificarsi di un determinato evento. Oltre al sistema di avvisare tramite un suono (Beep), può essere di maggiore efficacia usare dei colori che si attivino e lampeggino, come se fosse una luce che si accende e si spenge richiedendo attenzione. Questo effetto come un "Lampeggiatore" lo potremo attivare usando la funzione Timer, con la quale è possibile stabilire il tempo di durata assegnando un valore che rappresenta il numero di secondi di "accensione". Come al solito presento un esempio, in cui faccio lampeggiare due Range di celle, alternativamente, di rosso e di giallo. Ho scelto un Range esteso per ottenere un maggior richiamo visivo. Ognuno potrà definire le zone che vorrà. La routine si basa sull'ipotesi che, quando in una determinata cella (ho considerato la E1) compare un certo valore (ho usato "Pippo"), si deve attivare la routine e quindi il lampeggiare. Va da sè che ognuno potrà variare le condizioni di attivazione secondo le proprie necessità. Il ciclo si ripete secondo l'istruzione For x = 1 To 5, cioè per 5 volte, desiderando cicli diversi, basterà variare il 5 con altro numero. Quando la routine termina il numero di cicli previsti, faccio apparire un messaggio (io ho usato "Attenzione!!!") che blocca l'uscita e lascia le celle con i colori assegnati dall'ultimo ciclo. Solo premendo Ok sulla finestra del messagio, si sblocca il codice che prosegue l'istruzione, riportando le celle all'origine (senza colore), ed esce dalla routine. Questo è il codice da provare associando la macro ad un pulsante: Sub Lampeggia() If Range("E1").Value = "Pippo" Then Dim PauseTime, Start, Finish For x = 1 To 5 'inizia il ciclo e lo ripete per 5 volte PauseTime = 0.5 ' Imposta la durata in secondi. ho messo 1/2 secondo Start = Timer ' Imposta l'ora di inizio. http://ennius.interfree.it/
Pagina 172
MANUALE VBA
X EXCEL
Do While Timer < Start + PauseTime DoEvents ' Passa il controllo ad altri processi. Range("A1:D7").Cells.Interior.ColorIndex = 3 'colora il range di celle di rosso Range("A12:D21").Cells.Interior.ColorIndex = 6 'colora il range di celle di giallo Loop Finish = Timer ' Imposta l'ora di fine della pausa. PauseTime = 0.5 ' Imposta la durata. Start = Timer ' Imposta l'ora di inizio. Do While Timer < Start + PauseTime DoEvents ' Passa il controllo ad altri processi. Range("A1:D7").Cells.Interior.ColorIndex = 6 Range("A12:D21").Cells.Interior.ColorIndex = 3 Loop Finish = Timer ' Imposta l'ora di fine della pausa. Next x 'finisce il ciclo, appare un messaggio 'che blocca i colori MsgBox "ATTENZIONE!!!!" 'premuto ok sul messaggio, vengono eliminati i colori Range("A1:D7").Cells.Interior.ColorIndex = xlNone Range("A12:D21").Cells.Interior.ColorIndex = xlNone 'finisce End End If End Sub
http://ennius.interfree.it/
Pagina 173
MANUALE VBA
X EXCEL
Far lampeggiare (blinking) i Fonts (i caratteri). (09/06/03) Un modo di evidenziare determinate situazioni che si verificano sul foglio di lavoro, potrebbe essere affrontato con l'esercizio che sto presentando : intervenire sui fonts alternando colori diversi dei fonts in modo da creare l'effetto "lampeggio", che richiamerebbe inevitabilmente l'attenzione di chi sta lavorando. Classici esempi di utilizzo potrebbero essere controlli su elenchi di numeri (se il valore che si avrà in una cella o range di celle, non sia maggiore o minore di determinati valori, oppure se in una somma totale si raggiunge o si supera un certo valore, oppure ancora se compare un determinato numero, ecc.), controlli su testo o ancora su date, insomma, le condizioni da poter valutare sono talmente tante che ognuno adatterà i concetti alle proprie esigenze. Intanto vediamo le condizioni necessarie per usare le routine: L' area su cui intervenire - potremo assegnarla su una singola cella, oppure su più celle anche non contigue, oppure su un Range di celle, o ancora su tutto il foglio di lavoro. L'evento per attivare - potremo affidarci ad un pulsante che lanci la macro, oppure scegliere un automatismo come l'evento WorkSheet_Change per richiamare la macro ad ogni cambiamento nella celle/celle o area predefinita. Interruzione della macro - in questo caso realizzeremo una routine che ci consenta manualmente, tramite un pulsante, di interrompere l'effetto "lampeggio" . Le istruzioni per ottenere il "lampeggiare" si basano sul metodo OnTime di cui riporto la descrizione reperibile sulla guida in linea del VBE: Metodo OnTime Programma una routine affinché venga eseguita a una determinata ora futura, vale a dire a una determinata ora del giorno o dopo un determinato periodo. Sintassi : espressione.OnTime(EarliestTime, Procedure, LatestTime, Schedule) Osservazioni Utilizzare Now + TimeValue(time) per una programmazione in un'ora successiva all'ora corrente. un esempio che esegue la macro my_Procedure 15 secondi dopo l'ora corrente: Application.OnTime Now + TimeValue("00:00:15"), "my_Procedure" il concetto è semplice: OnTime basa la partenza sull'ora attuale (Now che corrisponde ad ADESSO()) alla quale aggiunge un tempo che decideremo noi, in questo esempio 15 secondi, dopodichè lancia la macro (il cui nome è racchiuso tra doppi apici e preceduta da una virgola). Impostando il tempo ad 1 secondo, otterremo l'effetto "lampeggio". Ma modificheremo opportunamente queste istruzioni per farle rispondere alle nostre esigenze, non vogliamo infatti lanciare nessuna macro tramite OnTime, bensì inizializzarla. Vediamo le istruzioni, in verde i soliti commenti: Usando un Modulo per ospitare le macro, inseriamo nella sezione "Generale - Dichiarazioni" del modulo la variabile NextTime come Date, comune ad entrambe le macro. Dim NextTime As Date __________________________________________________________________ Sub Lamp() 'sotto : impostiamo l'area sulla quale intervenire settando la variabile "zona" Set zona = Range("E1:G20") 'un'area presa ad esempio 'impostiamo la variabile NextTime che sarà uguale a Now più un secondo NextTime = Now + TimeValue("00:00:01") 'sotto: Con i caratteri nelle celle nell'area scelta (zona) With zona.Cells.Font 'se il colore dei fonts è nero, lo colori rosso, altrimenti lo colori nero, è questo che crea 'l'effetto "lampeggio" ogni secondo If .ColorIndex = 1 Then .ColorIndex = 3 Else .ColorIndex = 1 End With 'fine Con.... 'sotto: trascorso il primo secondo, si rilancia la stessa macro "Lamp" sfruttando ancora il 'metodo OnTime Application.OnTime NextTime, "Lamp" End Sub ora vediamo la routine per interrompere l'istruzione appena vista. Anche in questo caso si ricorre al metodo OnTime, ma sfruttando l'argomento Schedule che, impostato a False, consente di "cancellare" una (la) routine impostata precedentemente (Lamp). Oltre all'interruzione della routine, abbiamo http://ennius.interfree.it/
Pagina 174
MANUALE VBA
X EXCEL
bisogno di inserire un'istruzione che ripristini il colore dei fonts a nero, nel caso che si sia interrotta la routine mentre i fonts erano colorati in rosso. Queste le istruzioni: Sub Stoppa() Set zona = Range("E1:G20") 'sotto: si richiama col metodo OnTime la macro "Lamp", ma con "schedule" uguale a 'False, la interrompiamo, cancellandola Application.OnTime NextTime, "Lamp", schedule:=False 'si ripristina il colore nero nei fonts zona.Cells.Font.ColorIndex = xlAutomatic End Sub Quindi, viste le due routine, che potranno essere entrambe attivate da pulsante (la macro "Stoppa" dovrà essere comunque sempre attivata manualmente), prendiamo in esame come automatizzare l'avvio della macro "Lamp". Come già detto, diverse sono le necessità di essere avvisati, con il lampeggio, per cui prendiamo in esame un evento solo il Worksheet_Change, ma con due diverse istruzioni: Lampeggiare dei fonts su tutta l'area al verificarsi anche in una sola cella di una determinata condizione. Lampeggiare dei fonts solo su due celle nell'area al verificarsi di una determinata condizione, uguale e anche diversa per ognuna delle due celle. Prima ipotesi: se in una qualsiasi cella della zona scelta, sarà immesso un valore superiore a 3500 Private Sub Worksheet_Change(ByVal Target As Range) 'è necessario creare un ciclo For Next per controllare i valori di tutte le celle dell'area Dim C As Object Set zona = Range("E1:G20") 'impostiamo la zona For Each C In zona 'per ogni cella in zona If C.Value > 3500 Then 'se il valore in una cella è maggiore di 3500 Lamp 'si chiama la macro Lamp End If Next End Sub E' pacifico che se i criteri per l'attivazione fossero più di uno, dovremo modificare le istruzioni If con l'aggiunta degli operatori And oppure Or, o con l'inserimento di altre condizioni tramite ElseIf. Seconda ipotesi: se in due celle ben definite si verificherà una certa condizione: attiveremo la macro Lamp se nella cella F2 oppure nella cella F7 ci sarà un valore superiore a 3500 Private Sub Worksheet_Change(ByVal Target As Range) Set X = Range("F2") Set Y = Range("F7") If X > 3500 Or Y > 3500 Then Lamp End If End Sub Se invece si volesse che in entrambe le celle si verifichi che un valore superi 3500 per attivare il lampeggio, basterà modificare la riga: If X > 3500 Or Y > 3500 Then con If X > 3500 And Y > 3500 Then Per interrompere il lampeggio cliccheremo sul pulsante associato alla macro "Stoppa".
http://ennius.interfree.it/
Pagina 175
MANUALE VBA
X EXCEL
Uso del Filtro personalizzato in vba, con le date. Ancora un esercizio basato sull'applicazione del filtro personalizzato, basato sulla ricerca di dati compresi tra una data ed un altra, il tutto realizzato in vba. Supponiamo di voler filtrare tutti i record compresi tra due date, il 10/01/03 e il 15/02/03. Applicheremo quindi il filtro sul campo "data", selezioneremo "personalizzate" nel menù del campo, e si aprirà la finestra per la selezione dei "criteri di ricerca". Ovviamente sceglieremo il primo criterio "uguale o maggiore" del 10/01/03, e come secondo criterio "minore o uguale" a 15/02/03. Se ci aiutiamo usando il "Registratore di macro", vedremo che nelle istruzioni compilate da Excel, alle voci "Criteria1" e "Criteria2", viene usata la seguente sintassi: omissis...AutoFilter Field:=10, Criteria1:=">=10/01/2003", Operator:= _ xlAnd, Criteria2:=" 99 Then If Num0 > 199 Then LL$ = N$(Num1) LL$ = LL$ + "cento" End If If Num2 > 1 Then LL$ = LL$ + M$(Num2) If Num3 > 0 Then If Num3 = 1 Or Num3 = 8 Then LL$ = Left$(LL$, Len(LL$) - 1) LL$ = LL$ + N$(Num3) End If End If If Num2 < 2 Then LL$ = LL$ + N$(Num3 + Num2 * 10) End If Return Fine: End Sub Un plauso al bravo Marco; ora aspettiamo la versione per i decimali. La versione per gli Euro con i decimali è arrivata: La routine è inserita nell'evento Change del Foglio di lavoro, ma può essere inserita anche in un modulo e attivabile tramite l'associazione ad un pulsante. (Dovrà essere assegnato un nome alla macro, per esempio Sub NumeriLettere() ) Private Sub Worksheet_Change(ByVal Target As Range) ' Converte un numero da cifre a lettere (con decimali) Application.ScreenUpdating = False Dim N$(100), M$(100) http://ennius.interfree.it/
Pagina 235
MANUALE VBA
X EXCEL
Range("B6").Select 'cella col numero da convertire NumTot = ActiveCell.Value Num = Int(NumTot) Dec = NumTot - Num Decim$ = Format(Dec, ".00") Decim$ = " / " + Right$(Decim$, Len(Decim$) - 1) N$(0) = "" N$(1) = "uno" N$(2) = "due" N$(3) = "tre" N$(4) = "quattro" N$(5) = "cinque" N$(6) = "sei" N$(7) = "sette" N$(8) = "otto" N$(9) = "nove" N$(10) = "dieci" N$(11) = "undici" N$(12) = "dodoci" N$(13) = "tredici" N$(14) = "quattordici" N$(15) = "quindici" N$(16) = "sedici" N$(17) = "diciassette" N$(18) = "diciotto" N$(19) = "diciannove" M$(0) = "" M$(2) = "venti" M$(3) = "trenta" M$(4) = "quaranta" M$(5) = "cinquanta" M$(6) = "sessanta" M$(7) = "settanta" M$(8) = "ottanta" M$(9) = "novanta" M$(10) = "Cento" NN$ = LTrim$(Str$(Num)) If Len(NN$) > 6 Then Milioni$ = Left$(NN$, Len(NN$) - 6) NN$ = Right$(NN$, 6) End If If Len(NN$) > 3 Then Migliaia$ = Left$(NN$, Len(NN$) - 3) NN$ = Right$(NN$, 3) End If GoSub Ciclo LLL$ = LL$ NN$ = Migliaia$ If Migliaia$ = "1" Then LLL$ = "mille" + LLL$ Else GoSub Ciclo If Len(LL$) > o Then LLL$ = LL$ + "mila" + LLL$ End If http://ennius.interfree.it/
Pagina 236
MANUALE VBA
X EXCEL
NN$ = Milioni$ If Milioni$ = "1" Then LLL$ = "unmilione" + LLL$ Else GoSub Ciclo If Len(LL$) > o Then LLL$ = LL$ + "milioni" + LLL$ End If 'ActiveCell.Offset(0, 1).Range("A1").Select LLL$ = LLL$ + Decim$ Range("B7").Select 'cella che riporta la conversione il lettere ActiveCell.Value = LLL$ 'ActiveCell.Offset(1, -1).Range("A1").Select Exit Sub 'GoTo Fine Ciclo: LL$ = "" Num0 = Val(NN$) If Len(NN$) = 2 Then NN$ = "0" + NN$ If Len(NN$) = 1 Then NN$ = "00" + NN$ Num3 = Val(Right$(NN$, 1)) Num2 = Val(Mid$(NN$, 2, 1)) Num1 = Val(Left$(NN$, 1)) If Num0 > 99 Then If Num0 > 199 Then LL$ = N$(Num1) LL$ = LL$ + "cento" End If If Num2 > 1 Then LL$ = LL$ + M$(Num2) If Num3 > 0 Then If Num3 = 1 Or Num3 = 8 Then LL$ = Left$(LL$, Len(LL$) - 1) LL$ = LL$ + N$(Num3) End If End If If Num2 < 2 Then LL$ = LL$ + N$(Num3 + Num2 * 10) End If Return Fine: End Sub Ancora grazie a Marco.
http://ennius.interfree.it/
Pagina 237
MANUALE VBA
X EXCEL
Numeri Romani (in automatico.) A volte abbiamo la necessità di ottenere, in una cella, numeri in formato romano (con le lettere). Oltre alla funzione specifica =ROMANO(rif_Cella) da inserire in una cella del foglio di lavoro, è possibile ovviamente ottenere anche via codice lo stesso risultato. Ricordo che un istruzione via codice rimane, non può essere cancellata come invece può accadere con una formula inserita in una cella. Ecco quà una routine veloce, magari da inserire nell'evento SelectionChange del foglio di lavoro anzichè associata ad un pulsante, per averla attivata ad ogni cambio di selezione cella, sul foglio di lavoro. Esemplificando, porremo in A1 la cella contenente il numero da convertire, ed in C1 vorremo il numero romano; se in A1 avremo il numero "24", in C1 avremo "XXIV" . Questo è il codice: Private Sub Worksheet_SelectionChange(ByVal Target As Range) 'per avere numeri romani Range("C1").Formula = "=ROMAN(A1)" End Sub
http://ennius.interfree.it/
Pagina 238
MANUALE VBA
X EXCEL
La routine converti numeri in lettere trasformata in FUNZIONE: NUMINEURO(). Utilizzo: Conversione numeri in lettere, quando la conversione interessa più celle . Aggiornata al 30/06/03 con l'aggiunta dell'argomento Arrotonda. Questa procedura è utile nel caso si voglia utilizzare la conversione su più celle, e magari con riferimenti a celle distanti. A differenza delle routine del precedente paragrafo, dove è necessario inserire nel codice i riferimenti alle celle, sia quella che porta il numero, sia quella dove deve apparire la conversione in lettere del numero, questa funzione, una volta inserita la funzione in un modulo, consente di essere richiamata in qualunque cella, semplicemente dichiarando il nome della funzione ed il riferimento alla cella da convertire. Se per esempio, vogliamo che in F2 appaia il numero in lettere che è nella cella A1, in F2 scriveremo =Numineuro(A1;2) 'arrotondata a due decimali ed avremo in F2 la conversione. La conversione converte numeri interi o decimali e restituisce sempre il numero scritto in lettere con due decimali. (es: centoventisette/00). Ora è previsto l'arrotondamento a due decimali secondo il concetto: se il terzo decimale è 5 o maggiore, il secondo decimale viene arrotondato al decimale superiore (per eccesso), da 0 a 4, arrotondamento per difetto. Esempio: in A1 abbiamo 127,5478 con la funzione =Numineuro(A1;2) in un'altra cella, otteniamo: Centoventisette/55 (il secondo decimale arrotondato per eccesso) Questo è il codice, da copiare e incollare in un modulo: Function NumInEuro(NumTot As Currency, Arrotonda As Integer) Application.ScreenUpdating = False Dim N$(100), M$(100) NumTot = Round(NumTot, Arrotonda) Num = Int(NumTot) Dec = NumTot - Num Decim$ = Format(Dec, "." + String(Arrotonda, "0")) Decim$ = " / " + Right$(Decim$, Len(Decim$) - 1) If Arrotonda = 0 Then Decim$ = " / 00" N$(0) = "" N$(1) = "uno" N$(2) = "due" N$(3) = "tre" N$(4) = "quattro" N$(5) = "cinque" N$(6) = "sei" N$(7) = "sette" N$(8) = "otto" N$(9) = "nove" N$(10) = "dieci" N$(11) = "undici" N$(12) = "dodoci" N$(13) = "tredici" N$(14) = "quattordici" N$(15) = "quindici" N$(16) = "sedici" N$(17) = "diciassette" N$(18) = "diciotto" N$(19) = "diciannove" M$(0) = "" M$(2) = "venti" M$(3) = "trenta" M$(4) = "quaranta" M$(5) = "cinquanta" M$(6) = "sessanta" M$(7) = "settanta" M$(8) = "ottanta" http://ennius.interfree.it/
Pagina 239
MANUALE VBA
X EXCEL
M$(9) = "novanta" M$(10) = "Cento" NN$ = LTrim$(Str$(Num)) If Len(NN$) > 6 Then Milioni$ = Left$(NN$, Len(NN$) - 6) NN$ = Right$(NN$, 6) Else Milioni$ = "" End If If Len(NN$) > 3 Then Migliaia$ = Left$(NN$, Len(NN$) - 3) NN$ = Right$(NN$, 3) Else Migliaia$ = "" End If GoSub Ciclo LLL$ = LL$ NN$ = Migliaia$ If Migliaia$ = "1" Then LLL$ = "mille" + LLL$ Else GoSub Ciclo If Len(LL$) > o Then LLL$ = LL$ + "mila" + LLL$ End If NN$ = Milioni$ If Milioni$ = "1" Then LLL$ = "unmilione" + LLL$ Else GoSub Ciclo If Len(LL$) > o Then LLL$ = LL$ + "milioni" + LLL$ End If NumInEuro = LLL$ + Decim$ Exit Function Ciclo: LL$ = "" Num0 = Val(NN$) If Len(NN$) = 2 Then NN$ = "0" + NN$ If Len(NN$) = 1 Then NN$ = "00" + NN$ Num3 = Val(Right$(NN$, 1)) Num2 = Val(Mid$(NN$, 2, 1)) Num1 = Val(Left$(NN$, 1)) If Num0 > 99 Then If Num0 > 199 Then LL$ = N$(Num1) LL$ = LL$ + "cento" End If If Num2 > 1 Then LL$ = LL$ + M$(Num2) If Num3 > 0 Then If Num3 = 1 Or Num3 = 8 Then LL$ = Left$(LL$, Len(LL$) - 1) LL$ = LL$ + N$(Num3) http://ennius.interfree.it/
Pagina 240
MANUALE VBA
X EXCEL
End If End If If Num2 < 2 Then LL$ = LL$ + N$(Num3 + Num2 * 10) End If Return End Function Procedura realizzata da Marco Nocciolini.
http://ennius.interfree.it/
Pagina 241
MANUALE VBA
X EXCEL
Cicli su "Controlli". (ovvero sugli "Oggetti" posti su Userform) Quando lavoriamo sulle UserForm, possiamo inserirvi degli "Oggetti" presi dalla finestrina degli strumenti che appare quando, nell'editor di visual basic, inseriamo o selezioniamo una UserForm. Questi oggetti: una TextBox, un CommandButton, una ComboBox, ecc. ecc., non sono altro che "Controlli" ActiveX. Ogni Controllo viene identificato dal vba in funzione della Classe di appartenenza. Più Oggetti appartenenti alla stessa Classe vengono visti come "insiemi" omogenei, per esempio le TextBox ; tantè che se inseriamo più TextBox sulla nostra UserForm, per identificare l'oggetto, il vba provvede ad assegnare un numero progressivo ad ogni oggetto della stessa Classe inserito (avremo quindi: TextBox1, TextBox2, ecc.ecc.). La numerazione progressiva dovrebbe semplificare la programmazione quando si debbano trattare più oggetti omogenei, per esempio con cicli For Each...Next oppure For I = 1 To X ..Next, in riferimento a ciò che vorremmo fosse compiuto su ogni oggetto dell'insieme. A differenza di quanto avviene in Visual Basic, dove è possibile costruire una "Matrice di Controlli", che ci consentirebbe di assegnare un indice progressivo ad uno stesso "controllo" ripetuto (Text1(0), Text1(1), Text1(2), ecc.), favorendo quindi il riconoscimento di quale indice è interessato progressivamente dal ciclo For ...Next, in vba è necessario ricorrere all'insieme CONTROLS (Controlli) tramite un indice, un numero o un nome. Visto che tutte le TextBox hanno in comune almeno la parola Text, è possibile impostare una routine per intervenire sulle loro proprietà, per esempio per renderle visibili o invisibili: For Each cnt In Controls If Left(cnt.Name, 4) = "Text" Then cnt.Visible = False '(oppure True) End If Next Cioè : per ogni controllo (cnt) in Controlli (cioè gli "oggetti" presenti sull'UserForm), se le prime 4 lettere del nome del controllo sono uguali a Text, allora rendiamo invisibile il controllo. Vediamo ora un esempio su come riempire, sfruttando l'insieme Controls, una serie di TextBox con i valori prelevati da celle del foglio di lavoro. Supponiamo di avere dieci TextBox, nelle quali vogliamo che vengano riportati i dati presenti in altrettante dieci celle, sulla stessa riga, e di cui sia resa attiva la prima cella a sinistra, dovremmo scrivere le istruzioni, una per ogni textbox, in questa maniera: TextBox1 = ActiveCell.Offset(0, 1).Value TextBox2 = ActiveCell.Offset(0, 2).Value TextBox3 = ActiveCell.Offset(0, 3).Value TextBox4 = ActiveCell.Offset(0, 4).Value TextBox5 = ActiveCell.Offset(0, 5).Value ecc.ecc. fino alla 10. Sfruttando invece i Controls dell'oggetto UserForm, è possibile ridurre le istruzioni a poche righe, con un ciclo For ..Next, vediamo come: For n = 1 To 10 '(n sarà incrementato di 1 da 1 a 10 ad ogni ciclo (10 sono sia le textbox, sia le celle)) UserForm1.Controls("TextBox" & n).Text = ActiveCell.Offset(0, n).Value Next ed avremo ottenuto il nostro risultato con solo tre righe di istruzioni. La variabile n cambia ad ogni ciclo, sia per indicare in progressione il nome del controllo TextBox, sia per identificare la colonna della cella. Buon lavoro.
http://ennius.interfree.it/
Pagina 242
MANUALE VBA
X EXCEL
Usare l'Operatore LIKE (19/07/03) Sappiamo tutti, almeno spero, cosa sono gli "Operatori di confronto", eventualmente date un occhiata all'articolo "operatori numerici" nella sezione "primi passi". Comunque servono a confrontare due valori (testi, numeri o date). In genere i confronti si fanno per valutare la corrispondenza esatta tra due o più valori, oppure se un valore può essere maggiore o minore rispetto ad un altro; ma quando abbiamo bisogno di confrontare "approssimativamente" due valori, gli operatori normalmente usati non bastano più. Un esempio potrebbe essere quello di cercare, in un elenco, tutti valori la cui lettera iniziale corrisponde ad "a". Sappiamo che una parola come "atlante", che pure comincia con "a", non verrebbe trovata in quanto in quanto i metodi di ricerca si basano su un valore preciso fornito come chiave di ricerca, e la stringa "a" ovviamente è diversa dalla stringa "atlante". In questi casi ci viene in aiuto l'operatore LIKE, che vuol dire appunto "simile a...", "come" o " che "assomiglia a..". Perchè Like ci possa aiutare in una ricerca, va a sua volta aiutato, (indicando di seguito ad un valore chiave per esempio la lettera "c"), un carattere speciale definito sintatticamente "criterio", e che può essere rappresentato da uno dei seguenti caratteri Jolly : ? - (punto di domanda) che fa corrispondere qualsiasi carattere singolo. * - (asterisco) che fa corrispondere zero o più caratteri. # - (cancelletto) che fa corrispondere qualsiasi cifra singola (0-9). I caratteri Jolly ci consentono quindi di sostituirsi ad elenchi di caratteri o intervalli di caratteri in qualsiasi combinazione. Un'istruzione quindi che ci consentirebbe di trovare "atlante" digitando solo la lettera "a", diventerebbe: If CL.Value Like "a*" Then dove l'asterisco posto dopo la lettera "a" consentirebbe di riconoscere "a tlante" perchè l'asterisco sostituisce l'intervallo di caratteri "tlante". In parole povere, l'istruzione sopra dice: se il valore della variabile CL comincia con "a" con tutto quello che ci sia dopo, Then ecc. ecc. Ma come e dove usare Like ? Essenzialmente per eseguire ricerche di dati, di cui si voglia inserire una sola lettera iniziale, o un numero come chiave di ricerca, e si vogliano trovare tutti i valori che iniziano con quella lettera (o gruppo di lettere), e quel numero (o gruppi di numeri). Avremo quindi che con la stringa di ricerca c* - verranno trovati tutti i valori che iniziano per "c" : catullo, circo, caramba, cucurnia, ceffo, ecc. ca* - verranno trovati tutti i valori che iniziano per "ca" : catullo, caramba car* - verranno trovati tutti i valori che iniziano per "car" : caramba lo stesso dicasi per i numeri, o per le date (più avanti troverete una precisazione a questo proposito) 1* - verranno trovati tutti i valori che iniziano per "1" : 10, 125, 1543, 10/07/03 ecc. 10* - verranno trovati tutti i valori che iniziano per "10" : 10, 10/07/03 154* -verranno trovati tutti i valori che iniziano per "154" : 1543 Abbiamo quindi imparato un metodo flessibile che ci consente un'ampia possibilità di variare le ricerche. Vediamo ora qualche esempio di applicazione, con relative istruzioni e commenti (in verde). Iniziamo con chiarire come impostare la chiave di ricerca: inserendo la chiave di ricerca direttamente nelle istruzioni: es. Like "a*" ; sistema poco flessibile perchè ci costringerebbe alla modifica dell'istruzione nel codice se volessimo trovare altri valori che inizino con altre lettere. usare una cella del foglio di lavoro, nella quale di volta in volta scrivere il valore che serva come chiave di ricerca; es. usando la cella C1, la nostra istruzione diventerebbe Like Range("C1") & "*". Come si nota usiamo un concatenatore di stringa (la &) per restituire, uniti, il valore che scriveremo in C1 seguito dall'asterisco (carattere Jolly) usare una textbox se usiamo una userform, nella quale scriveremo il valore chiave di ricerca (come sopra) e in questo caso l'istruzione sarà : Like TextBox1 & "*" usare una InputBox. in questo caso dovremo assegnare ad una variabile il valore restituito dalla InputBox, e richiamare questa variabile+asterisco dopo Like. Sarà sufficiente compilare: Myvar = InputBox("Inserisci il valore da cercare") ......Like CStr(Myvar) & "*" Ora passiamo a vedere in quale contesto usare queste istruzioni. Molte sono le situazioni in cui si può presentare la necessità di svolgere una ricerca, qui sceglieremo l'esempio di cercare tutti i valori la cui iniziale corrisponderà ad una determinata lettera, e con i dati così trovati, riempire una ListBox. Sotto vediamo l'immagine dell'esercizio: la colonna A è la zona dove risiedono i dati, la cella C1 è la cella che usiamo come vettore per l'iniziale che serve a trovare nella colonna A tutti i valori che cominciano con una lettera, in questo caso la lettera "c", e la ListBox1 con i nomi trovati http://ennius.interfree.it/
Pagina 243
MANUALE VBA
X EXCEL
e questa la routine usata: Sub carica1() Dim CL As Object ActiveSheet.ListBox1.Clear 'predisponiamo la pulizia della listbox per la nuova ricerca For Each CL In Range("A1:A20") 'inizia il ciclo di ricerca: per ogni CL (cella presente 'nel Range A1:A20 (tanto per fare l'esempio mi fermo alla riga 20) 'sotto: se il valore che presente nella cella in quel momento scorsa, è uguale al valore che 'è in C1*, cioè basta che inizi con "c" (in questo caso), allora If CL.Value Like Range("C1") & "*" Then 'carichiamo con AddItem quel valore nella ListBox1 ActiveSheet.ListBox1.AddItem CL.Value End If Next End Sub Basterà quindi variare la lettera in C1 per ottenere, se presenti nell'elenco, i nomi che iniziano per quella lettera, nella ListBox. Un'altro aspetto interessante di questo metodo di estrazione dati, perchè di questo si tratta, è che se nell'elenco dati esistono celle vuote, queste non figureranno mai nella ListBox. Qualcuno potrebbe obiettare: ma come fare per ottenere i nomi trovati, messi in ordine alfabetico nella ListBox? E ancora: come fare perchè eventuali dati doppi vengano riportati una sola volta? A quest'ultima domanda rispondo: leggete l'articolo "ComboBox . ordinare la lista", dove si parla della routine per l'eliminazione dei doppioni. Alla prima domanda rispondo. applicare un'ordinamento all'elenco a cui si attingono i dati da ricercare: i dati trovati saranno già in ordine alfabetico. creare in una colonna fuori vista, un riepilogo dei dati estratti, applicare a questo secondo elenco un'ordinamento ascendente, reperire quindi il riferimento alla cella iniziale e con End il riferimento alla cella finale, e fornire questi riferimenti alla proprietà ListFillRange della ListBox (anzichè usare AddItem) in modo che la ListBox ci mostri l'elenco in ordine alfabetico. Vediamo un'immagine basata su quest'ultima ipotesi, dove, per comodità visive, il secondo elenco lo ospito nella colonna D, a partire dalla cella D4. A questo elenco è stato già applicato l'ordinamento, a destra la Listbox caricata usando la sua proprietà ListFillRange :
http://ennius.interfree.it/
Pagina 244
MANUALE VBA
X EXCEL
E questa la routine, le istruzioni sono un pò più complesse rispetto alla routine carica1, ma viene svolto un lavoro diverso : Sub carica2() 'iniziamo pulendo la ListBox1 predisponendo alla nuova ricerca, e NON usiamo Clear, 'ma impostiamo il ListFillRange a vuoto ("") ActiveSheet.ListBox1.ListFillRange = "" Dim CL As Object 'dichiariamo CL come "oggetto" (usato nel ciclo For Each..Next) Dim iRow As Integer 'dichiariamo la variabile iRow come Integer (è la variabile con la 'quale impostiamo da quale riga iniziare il riepilogo dei dati trovati (il secondo elenco) iRow = 3 'rendiamo iRow uguale a 3, cioè il riepilogo inizierà dalla riga 3 Range("D:D").ClearContents 'Puliamo tutta la colonna destinata al riepilogo, e la puliamo 'tutta in quanto non sappiamo quanto lungo era il riepilogo precedente. 'sotto: ATTENZIONE : istruzione necessaria per ripristinare tutta la colonna destinata al 'riepilogo, impostando il formato celle a "Generale". Operazione necessaria nel caso in cui 'si cerchino occasionalmente anche valori tipo data (es. 10/07/03), quando il dato 'cercato viene trovato, se è una data, Excel nel copiare la data nel riepilogo, modifica 'anche il formato della cella che ospiterà la data a formato cella data. Questa 'impostazione rimane anche per le nuove ricerche, e se nella nuova ricerca venissero 'cercati numeri anzichè date, il numero che finisse dove prima era stata ospitata una data, 'sarebbe errato e inservibile. Range("D:D").NumberFormat = "General" 'sotto: si imposta il valore della cella, ora riga 3, 4 colonna, cioè la D, a "riepilogo" come 'se fosse un'intestazione di campo Cells(iRow, 4).Value = "riepilogo" 'sotto: si inizia il ciclo di ricerca nel Range A1:A20, ognuno metterà i suoi riferimenti For Each CL In Range("A1:A20") 'sotto: se il valore che è presente nella cella in quel momento scorsa, è uguale al valore 'che è in C1*, cioè basta che inizi con "a" (in questo caso), allora If CL.Value Like Range("C1") & "*" Then 'inizia il ciclo interno per cercare una cella libera, colonna D (4) dove scrivere il valore 'trovato sopra (CL.Value) Do While Cells(iRow, 4).Value "" 'fino a che la cella è occupata iRow = iRow + 1 'si passa a controllare la cella sottostante (+1) e si continua con Loop Cells(iRow, 4) = CL.Value 'quando si verifica che una cella è vuota, gli si passa il valore 'rappresentato da Cl.Value Exit Do 'e si esce dal ciclo Do While..Loop Loop 'altrimenti si continua a cercare una cella vuota ritornando a Do While http://ennius.interfree.it/
Pagina 245
MANUALE VBA
X EXCEL
End If Next ' con Next si scorrono, una ad una le celle del Range A1:A20 'a questo punto, finita la ricerca e caricato il "riepilogo", abbiamo bisogno di reperire gli 'Address, cioè gli indirizzi delle celle che delimitano il "riepilogo" lista = Cells(4, 4).Address 'con "lista" prendiamo l'indirizzo della cella riga 4, colonna 4 '(D4) nera = Cells(4, 4).End(xlDown).Address 'con "nera" reperiamo l'ultima cella occupata 'della colonna D. 'sotto: applichiamo l'ordinamento ai range i cui estremi sono stati trovati con le due 'variabili Range(lista & ":" & nera).Sort Key1:=Range("D4"), Order1:=xlAscending, Header:= _ xlGuess, OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom 'e alla fine carichiamo la ListFillRange della ListBox1 con l'assegnazione dei range di 'pertinenza ActiveSheet.ListBox1.ListFillRange = (lista & ":" & nera) End Sub Sotto riporto, senza spiegazioni (sono uguali), la routine che si avvale di una InputBox per reperire il valore da cercare: Sub caricainput() ActiveSheet.ListBox1.ListFillRange = "" Dim CL As Object Dim iRow As Integer iRow = 3 cosa = InputBox("Scrivi cosa cerchi") 'con "cosa" prendiamo il valore da cercare Range("D:D").ClearContents Range("D:D").NumberFormat = "General" Cells(iRow, 4).Value = "riepilogo" For Each CL In Range("A1:A20") If CL.Value Like CStr(cosa) & "*" Then 'e usiamo la variabile stringa "cosa" al posto 'dell'indicazione di una cella Do While Cells(iRow, 4).Value "" iRow = iRow + 1 Cells(iRow, 4) = CL.Value Exit Do Loop End If Next lista = Cells(4, 4).Address nera = Cells(4, 4).End(xlDown).Address Range(lista & ":" & nera).Sort Key1:=Range("D4"), Order1:=xlAscending, Header:= _ xlGuess, OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom ActiveSheet.ListBox1.ListFillRange = (lista & ":" & nera) End Sub Unica precisazione: il carattere Jolly * (asterisco), può essere usato come valore iniziale da cercare, seguito da una o più lettere, e quindi ancora un asterisco. In questo modo possiamo trovare valori al cui interno compaia la lettera o le lettere digitate tra gli asterischi. Esempio: se in C1 o nella InputBox scriviamo *lo*, verranno trovati tutti i nomi al cui interno compare la coppia "lo", cioè : bellotti, cellosi, luppolo . Buon lavoro.
http://ennius.interfree.it/
Pagina 246
MANUALE VBA
X EXCEL
Ordinamento di un elenco dati in automatico. Oltre all'ordinamento di un elenco dati ottenibile dai due pulsantini A-Z o Z-A posti su una barra dei menù, può essere utile usare il codice Vba per ottenerlo in automatico. La tenuta di dati ordinati per ordine alfabetico (ad esempio dei nominativi o un elenco articoli), oppure per ordine di grandezza (in un elenco numerico), o ancora un elenco di date. Con la seguente procedura, basterà infatti aggiungere un nuovo dato per vederlo subito dopo ordinato al posto giusto in elenco. La procedura la inseriremo nell'evento WorkSheet_SelectionChange del foglio che contiene l'elenco: Private Sub Worksheet_SelectionChange(ByVal Target As Range) With Range("A:A") .Sort Key1:=Range("A1"), Order1:=xlAscending, Header:=xlGuess, _ OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom, _ DataOption1:=xlSortNormal End With End Sub L'esempio è stato fatto presupponendo un elenco contenuto nella colonna A, e che i dati iniziassero nella cella A1. Basterà sostituire i riferimenti per adattarlo al proprio elenco. Se anzichè volere un ordinamento crescente volessimo un ordinamento decrescente, basterà sostituire il comando " Order1:=xlAscending " con " Order1:=xlDescending " . Volendo, invece, disporre manualmente il nostro elenco, potremo utilizzare questa routine , associata ad un pulsante posizionato sul foglio di lavoro: Sub OrdineCrescente() With Range("A:A") .Sort Key1:=Range("A1"), Order1:=xlAscending, Header:=xlGuess, _ OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom, _ DataOption1:=xlSortNormal End With End Sub e l'altra, decrescente: Sub OrdineDecrescente() With Range("A:A") .Sort Key1:=Range("A1"), Order1:=xlDescending, Header:=xlGuess, _ OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom, _ DataOption1:=xlSortNormal End With End Sub Quando invece vogliamo ordinare un elenco formato da più colonne, per esempio un indirizzario dove oltre al nome registriamo, nelle colonne adiacenti, l'indirizzo, la città, ed altri dati, avremo bisogno, se scegliamo di avere i dati in ordine alfabetico del nominativo, che anche i dati associati al nominativo si spostino insieme al nome. Volendo usare ancora l'ordinamento automatico, dovremo allora modificare le istruzioni. Sfrutteremo sempre l'evento SelectionChange del foglio di lavoro, ma dovremo inserire delle coordinate precise per indicare TUTTA l' area interessata all'ordinamento, compreso quindi non solo la colonna che contiene i nomi che vogliamo in ordine alfabetico, ma anche le colonne dove sono i dati correlati (indirizzo, città, telefono, ecc.). Potremo comprendere anche delle righe vuote che serviranno poi per l'aggiunta di nuovi dati. Supponiamo quindi di usare la zona che va dalla prima cella (A1) e. per quattro colonne (A,B,C,D) fino alla riga 200, l'istruzione sarà: Private Sub Worksheet_SelectionChange(ByVal Target As Range) With Range("A1:D200") 'Area che comprende tutti i dati .Sort Key1:=Range("A1"), Order1:=xlAscending, Header:=xlGuess, _ OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom, _ DataOption1:=xlSortNormal End With 'la spiegazione di questa parte di codice la dò sotto Dim CL As Object For Each CL In Range("A1:A200") If CL.Value "" And CL.Offset(0, 1).Value = "" Then CL.Offset(0, 1).Select http://ennius.interfree.it/
Pagina 247
MANUALE VBA
X EXCEL
End If Next End Sub Poichè l'inserimento di un nominativo, nella colonna A, ha bisogno di essere confermato premendo il tasto INVIO o con la pressione su una delle frecce per spostarsi, e questo genera l'evento SelectionChange, avremo che il nome appena immesso verrà immediatamente ordinato, con conseguente spostamento nell'elenco stesso: per non correre a cercare dove è finito (specie con lunghi elenchi), ho aggiunto la seconda istruzione che cercherà (nella colonna dei nominativi, la A) il primo valore con la cella adiacente vuota, e la selezionerà, così che si possano inserire gli altri dati mancanti.
http://ennius.interfree.it/
Pagina 248
MANUALE VBA
X EXCEL
Ordinamento scadenzario con raggruppamento clienti e somma degli importi delle loro fatture. Fra i diversi metodi che Excel ci offre per ottenere da un elenco, per esempio uno scadenzario, il raggruppamento dei dati relativi ad una chiave prestabilita (per esempio il Codice Cliente o il nominativo Cliente), ottenibili con un filtro, e relativi sub totali del campo "Importo fattura", sempre attraverso le risorse di Excel (menù Dati/Subtotali), presento una soluzione realizzata in vba che non richiede l'uso del filtro. Vediamo un esempio, nella foto sotto vediamo una tipica tabella, che potrebbe essere anche un "Riepilogo Fatture emesse" anzichè uno scadenzario, il concetto non cambia:
Come si nota nell'elenco, i dati non sono raggruppati per Cliente, ma mescolati. Avremo bisogno quindi di eseguire un "ordinamento" in modo da riunire tutti i clienti con lo stesso nome oppure con lo stesso codice cliente, noi scegliamo la chiave "codice cliente". poichè sappiamo da quanti campi (colonne) è formato il nostro elenco, ma non possiamo sapere quante righe occuperà, useremo la funzione End per reperire i "riferimenti" delle ultime celle: sia quella verso il basso (End(xlDown) a partire dalla cella di inizio elenco (la A2), sia quella verso destra (End(xlToRight)) sempre a partire dalla stessa A2. In questo modo selezioneremo tutta l'area dati che ci interessa, e su quell'area applicheremo l'ordinamento usando come chiave di ricerca il codice cliente (SortKey1:= Range("A2")), e queste sono le istruzioni, che assoceremo ad un pulsante: Sub Ordinadati() giu = Range("A2").End(xlDown).Address dx = Range("A2").End(xlToRight).Address Range(giu & ":" & dx).Select Selection.Sort Key1:=Range("A2"), Order1:=xlAscending, Header:=xlGuess, _ OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom, _ DataOption1:=xlSortNormal End Sub e questo sarà l'effetto dopo l'ordinamento; come di vede, sono stati riuniti tutti i clienti che hanno lo stesso codice uguale:
A questo punto cosa dovremo ottenere: la separazione tra un cliente e l'altro con l'aggiunta di due righe, nella prima delle quali inseriremo la parola "totale" nella colonna B l'inserimento nella colonna E, sempre nella prima delle due righe aggiunte, di una formula che cerchi, a salire, tutti gli importi relativi alle fatture di quel cliente, e sommi gli importi in modo da ottenere un totale cliente. Per ottenere questo risultato: http://ennius.interfree.it/
Pagina 249
MANUALE VBA
X EXCEL
useremo una InputBox nella quale inseriremo il codice cliente di cui vogliamo il totale. useremo in ciclo di ricerca di questo valore in modo che venga selezionata l'ultima cella che sia uguale al codice indicato. trovata questa cella, selezioniamo la riga immediatamente sotto, e con la cella in quel momento attiva, diciamo di inserire 2 nuove righe. Operazione necessaria visto che l'inserimento riga in Excel avviene sopra la cella (meglio riga) in quel momento selezionata, non sotto. inseriamo nella prima delle due righe (colonna A) un trattino, nella colonna B la parola "totale" e nella colonna E la funzione SOMMA. questo il codice impiegato: Sub cerca() With Worksheets(1).Range("a1:a20") 'range sui cui si opera 'istruzioni per l'imputbox che ti chiede il 'codice cliente Dim code, mess, titolo mess = "Inserisci il Codice Cliente :" titolo = "Inserimento Codice" code = InputBox(mess, titolo) '"code" ora è uguale al codice digitato 'inizio il ciclo di ricerca nel range scelto (A1:A20), del valore che 'è stato assegnato a "code" Set c = .Find(code, LookIn:=xlValues) If Not c Is Nothing Then firstAddress = c.Address Do 'viene individuato l'ultimo "code" trovato e seleziono 'la cella sotto per poter aggiungere due righe c.Offset(1, 0).Select Set c = .FindNext(c) Loop While Not c Is Nothing And c.Address firstAddress End If End With 'aggiungo due righe ActiveCell.EntireRow.Insert Shift:=xlDown ActiveCell.EntireRow.Insert Shift:=xlDown 'inizia l'inserimento nella prima cella di una lineetta (colonna A) 'poi inserisce "totale" nella cella accanto, ActiveCell = "-" ActiveCell.Offset(0, 1) = "totale" 'poi controlla la colonna importi, (la E) e ne prende i riferimenti a salire: X sarà uguale al 'riferimento cella attiva + 4, cioè la colonna E, ma la cella nella riga immediatamente 'sopra (-1). Poichè si può verificare il caso che esista un cliente con una sola fattura, 'sarebbe inutile sommare, ed allora eseguiamo un controllo sulla seconda riga a salire, colonna E, Se questa cella conterrà dati (diversa da vuota), allora si ricercano tutte le 'righe a salire con End, per prendere l'ultimo riferimento If ActiveCell.Offset(-2, 4) "" Then X = ActiveCell.Offset(-1, 4).Address 'poi preso il riferimento dell'ultima cella con End(xlUp) contenete valori Y sarà uguale al riferimento cella attiva + 4, cioè la colonna E ma alla prima (la più in alto) cella iniziale. 'Ricordo che l'istruzione End cerca l'ultima cella occupata nella direzione indicata dalla 'costante, a partire dalla cella iniziale indicata (in questo caso: ActiveCell) e si ferma 'appena trova una cella vuota. Y = ActiveCell.Offset(-1, 4).End(xlUp).Address 'indi applica la funzione somma ActiveCell.Offset(0, 4).Formula = "=SUM(" & X & ":" & Y & ")" 'nel caso previsto che il cliente sia presente con una sola fattura, invece rendiamo il totale 'uguale all'importo della fattura Else http://ennius.interfree.it/
Pagina 250
MANUALE VBA
X EXCEL
ActiveCell.Offset(0, 4).Value = ActiveCell.Offset(-1, 4).Value End If End Sub Una nota: perchè la somma si applichi in maniera corretta, sarà necessario procedere rispettando il codice progressivo: se per esempio, si cercasse di ottenere il totale del codice 3402 senza prima avere ottenuto i totali e quindi la separazione con due righe vuote del codice 3401, il codice 3402 verrebbe separato si da quello successivo, ma la somma coinvolgerebbe anche i valori relativi al codice 3401 non essendo separati. Questa comunque l'immagine del risultato dell'istruzione sopra:
Buon Lavoro.
http://ennius.interfree.it/
Pagina 251
MANUALE VBA
X EXCEL
ProgressBar. (23/07/03 - Aggiornamento 10/02/04) Alcune richieste, anche passate, avevano come argomento, la realizzazione di una "barra di avanzamento" (ProgressBar) che avvisasse l'utente che una macro era in esecuzione. Infatti a volte si può lamentare una lentezza nell'esecuzione di procedure, tale da far pensare che il computer non stia lavorando, Una barra di avanzamento consentirebbe, con la sua progressione, di "visualizzare" che in effetti "qualcosa" sta succedendo. Premesso che alcune routine sono mal costruite, ed impiegano più tempo del dovuto a terminare, premesso che alcuni computer sono dotati di scarse risorse Hardware (poca RAM, dischi lenti, processori obsoleti) esistono tuttavia lentezze causate dalla gran quantità di dati che devono essere elaborati, da qui l'esigenza di avere un "segnale" che tutto sta procedendo. Bisogna capire come prima cosa, che qualsiasi ProgressBar per funzionare, deve poter essere collegata ad una variabile che sia un vettore del numero di iterazioni che si stanno eseguendo. Solo con questa impostazione sarà possibile verificare in tempo reale (o quasi ) l'avanzamento dell'esecuzione di una macro, e le istruzioni per l'avvio della ProgressBar, andranno posizionate opportunamente per evitare che lo scorrimento della barra di avanzamento non parta, o parta dopo, o addirittura interferisca con l'esecuzione stessa (della procedura) rallentandola ulteriormente. La ProgressBar più semplice e veloce da ottenere (ATTENZIONE: questa soluzione NON vale per la versione 97 di Excel), è quella di usare la "BARRA DI STATO". Vediamo come ottenerla: dal menù Visualizza, selezionare la voce "Barra di stato", vedi foto:
e in fondo alla pagina, comparirà una barra in più, con all'estrema sinistra, la parola "Pronto" :
Questa operazione può comunque essere evitata, perchè la "Barra di stato" può venire chiamata da un'istruzione che integreremo nella nostra macro, e che imposterà anche il testo che apparirà al posto di "Pronto", indicando peraltro il valore che indica lo "stato di avanzamento", ottenendo l'effetto di tranquillizzare chi aspetta che la macro termini. Nella foto sotto vediamo un'istantanea dei numeri che scorrono, indicando l'avanzamento:
Vediamo quindi come sfruttare le istruzioni, presentando un esempio nel quale, riempiremo tutte le celle che vanno dalla cella A1 alla J500, con numeri casuali. Questa la macro, che potrete copiare e provare sul vostro computer : Sub Test() http://ennius.interfree.it/
Pagina 252
MANUALE VBA
X EXCEL
[A1:J500].ClearContents 'nel range previsto come esempio, puliamo le celle Dim r 'dimensioniamo la variabile "r" per indicare le righe Dim c 'dimensioniamo la variabile "c" per indicare le colonne For r = 1 To 500 'indi si inizializza il ciclo che conterà le righe da 1 fino a 500 For c = 1 To 10 ' e le colonne fino alla colonna 10 (la J) Randomize 'Randomize serve a reinizializzare il numero iniziale casuale, diverso dal 'primo ottenuto con Rnd, successivo Cells(r, c) = Int(Rnd * 150) 'poi assegniamo un numero casuale compreso tra zero e 150 'alla cella, riga r, colonna c, in quel momento reperita con i cicli For..Next sopra Cells(r, c).Select 'selezioniamo la cella in quel momento "spazzolata" solo per creare un 'effetto visivo delle celle che vengono di volta in volta individuata. In questo modo si può 'controllare l'efficacia dell'avanzamento routine Counter = r * c 'con la variabile "counter" (riga per colonna) otteniamo il numero di celle 'in ogni momento "contate" dai cicli For..Next e rappresenta il valore che verrà mostrato 'nella "StatusBar", indicando quindi la progressione delle operazioni. DoEvents 'istruzione necessaria perchè lascia l'esecuzione delle istruzioni "libere" di 'proseguire, senza "bloccarne" l'esecuzione. 'sotto: si attiva la "barra di stato" e si imposta il testo che vedremo, seguito dal valore 'rappresentato da "COUNTER". Application.StatusBar = "Procedura in esecuzione : " & Counter 'indi si procede con Next a scorrere prima le righe, e a seguire le colonne Next c Next r [A1].Select 'a fine ciclo si seleziona la cella A1 per ritornare a inizio foglio Application.StatusBar = False 'questa istruzione cancella il testo dalla barra di stato End Sub se non si fosse utilizzata l'ultima istruzione (Application.StatusBar = False), ma questa: Application.StatusBar = "Procedura completata : " & Counter questo sarebbe ciò che vedremo nella barra di stato:
il tutto crea un buon effetto di macro in esecuzione e termine. Bisogna fare attenzione al posizionamento delle istruzioni per l'avvio della StatusBar, si corre il rischio che per vedere una progressione di avanzamento, si rallenti in maniera notevole l'esecuzione della macro peggiorando il risultato finale in termini di tempo impiegato. Ognuno dovrà valutare le proprie necessità in funzione delle proprie istruzioni. Non chiedetemi aiuto in questo caso, ognuno dovrà capire come usare l'esempio proposto, e risolvere da solo. Un modo meno problematico e comunque di effetto, è quello di usare una UserForm con una Label, dove sfrutteremo la Caption della Label per gestire due messaggi: Inizio Procedura, e Fine Procedura. Se avviamo l'UserForm ad inizio macro, usando DoEvents, lasceremo la prosecuzione delle istruzioni fino alla fine, e indipendentemente dal tempo impiegato, otterremo di avvisare l'utente che la procedura è in esecuzione, e alla fine che è terminata, senza interferire con i tempi già lunghi richiesti dalle vostre procedure. Vediamo come lavorare: Impostare la proprietà ShowModal della UserForm a False. Questo consente di spostare il focus sul foglio lasciandolo libero di aggiornarsi. Condizione necessaria per l'aggiornamento o lo scorrimento del foglio, dipende ovviamente dalle istruzioni contenute nelle vostre procedure. Questa impostazione crea un problema su Excel 97 che si rifiuta, dando errore, di mostrare una UserForm con ShowModal impostato a False. Usiamo l'esempio dei numeri casuali usato sopra, ma con le istruzioni per inizializzare la UserForm: Sub Test3() [A1:J500].ClearContents 'sotto: attiviamo la userform e nella Label facciamo apparire la scritta "PROCEDURA IN 'CORSO", usando la proprietà Caption della Label (etichetta) UserForm1.Show http://ennius.interfree.it/
Pagina 253
MANUALE VBA
X EXCEL
UserForm1.Label1.Caption = "PROCEDURA IN CORSO" DoEvents ' Non ci scordiamo di trasferire il controllo ad altri processi, quelli sotto: Dim r Dim c For r = 1 To 500 For c = 1 To 10 Randomize Cells(r, c) = Int(Rnd * 150) Cells(r, c).Select Counter = r * c 'DoEvents Next c Next r [A1].Select 'alla fine della procedura, modifichiamo il testo nella Label sull'userform UserForm1.Label1.Caption = "FINE PROCEDURA" End Sub L'efficacia è garantita, questa l'immagine iniziale all'avvio della macro:
e questa l'immagine a fine procedura.
Un'altra ProgressBar molto più efficace come effetto visivo, che comporta un pò più di lavoro per ottenerla, la propongo nell'esempio sotto. Premetto che l'idea non è mia, ma trovata in Internet, e adattata da me per l'occasione. Viene sfruttata anche in questo caso una UserForm, ma l'effetto visivo dello stato di avanzamento simula il funzionamento di una reale ProgressBar. Anche questa procedura non è attuabile con la versione 97 di Excel o precedenti. Ma vediamo cosa serve: una UserForm la cui proprietà ShowModal va impostata a False. una Cornice (Frame) (da inserire nella Userform), la cui proprietà SpecialEffect va impostata a 2fmSpecialEffectSunken per creare l'effetto incavato. un'etichetta (Label) (da inserire all'interno del Frame) la cui proprietà SpecialEffect va impostata a 1fmSpecialEffectRaised per creare l'effetto rilievo. ed ora vediamo la routine presentata sopra per generare numeri casuali, modificata con le istruzioni necessarie: Sub TestBar() [A1:J500].ClearContents Dim r Dim c RowMax = 500 'assegniamo il valore 500 al numero massimo di righe(con RowMax) ColMax = 10 'assegniamo il valore 10 al numero massimo di colonne (con ColMax) For r = 1 To 500 'inizializziamo i cicli come spiegato nella Sub Test For c = 1 To 10 Randomize Cells(r, c) = Int(Rnd * 150) Cells(r, c).Select Dim Counter As Integer 'dichiariamo la variabile Counter come Integer http://ennius.interfree.it/
Pagina 254
MANUALE VBA
X EXCEL
Counter = Counter + 1 'incrementiamo di 1 il counter ad ogni cella scorsa Next c 'si passa alla cella della colonna successiva Percent = Counter / (RowMax * ColMax) 'con Percent prendiamo il valore della 'percentuale delle celle scorse, dividendo il valore di counter per 5000 UserForm1.Show 'chiamiamo la Userform With UserForm1 ' con la Userform impostiamo il formato (0%) della proprietà Caption del 'Frame del valore rappresentato da Percent .Frame1.Caption = Format(Percent, "0%") .Label1.Width = Percent * (.Frame1.Width - 10) ' questa è l'istruzione che imposta la 'lunghezza della Label, data appunto dal valore Percent moltiplicato la Lunghezza del 'Frame meno 10 pt. E' questo che provoca l'effetto avanzamento nella label, il cui fondo è 'stato impostato a rosso End With 'fine con DoEvents 'istruzione importante che passa il controllo ad altri processi, passando con 'Next r alla riga successiva per ripetere tutto il ciclo Next r [A1].Select End Sub Due immagini relative alla Sub TestBar
Chiaramente la velocità dell'avanzamento è proporzionale al tempo di esecuzione delle istruzioni. 10/02/04 - Aggiornamento. Quando invece non sia possibile stabilire il numero di iterazioni legate all'esecuzione di una procedura, per cui risulta problematico determinare il vettore del valore di incremento di una ProgressBar, ma si voglia comunque avvisare l'utente che una procedura, a volte molto lunga, è in corso, possiamo usare una cella del foglio di lavoro (magari determinandola a priori, o meglio lasciando una cella libera per questo scopo), dove far apparire la scritta : "Procedura in esecuzione". Evitiamo in questo modo di "trafficare" con UserForm, e saremo comunque avvisati che il computer sta lavorando. http://ennius.interfree.it/
Pagina 255
MANUALE VBA
X EXCEL
L'idea, presentata dall'amico Falini Eliano ( [email protected] ), è semplice, facilmente attuabile, e di sicura efficacia. Si pongono ad inizio e fine della procedura di cui vorremo avvisare, due semplici blocchi di istruzioni, il primo attiverà la cella che avremo destinato allo scopo, scrivendo una frase che ci avvisi, e magari evidenziamo di giallo la cella stessa, e ne evidenziamo i bordi; il secondo blocco invece ripristina la cella, eliminando la scritta, il colore e togliendo i bordi. Quando attiveremo la nostra procedura, verremo avvisati che la procedura è in esecuzione, e quando sarà terminata sparirà l'avviso e la formattazione della cella ritornerà normale. Simuliamo la Cella B1 come quella da noi destinata, e vediamo i due blocchi di istruzioni: Sub TuaMacro() With [B1] 'blocco iniziale nuove istruzioni .Value = "Procedura in esecuzione :" 'messaggio che appare in B1 .Interior.ColorIndex = 6 'si colora la cella di giallo .Borders.LineStyle = xlContinuous 'si impostano i bordi alla cella End With .....seguono le istruzioni da eseguire With [b1] 'blocco finale nuove istruzioni .Value = "" 'si pulisce la cella B1 .Interior.ColorIndex = xlNone 'si toglie il colore giallo .Borders.LineStyle = xlNone 'si tolgono i bordi End With End Sub Ovviamente potrete decidere se usare il grassetto per la cella, ed eventualmete un colore per il font. Un grazie ad Eliano.
http://ennius.interfree.it/
Pagina 256
MANUALE VBA
X EXCEL
La proprietà End. (01/07/03) Ovvero : come reperire l'ultima cella occupata in una colonna (o in una riga). Continuano a pervenire domande su come individuare la fine di un elenco di dati, quando si lavora con elenchi la cui lunghezza è variabile, e quindi non si conosce il riferimento alla cella finale. Su sito esistono moltissime routine che usano End per reperire i range su cui agire, ma probabilmente è opportuno evidenziare l'argomento con una pagina dedicata. Vediamo cosa dice la guida in linea: Proprietà End (da non confondere con l'Istruzione End, che è tutta un'altra cosa) Restituisce un oggetto Range che rappresenta la cella alla fine dell'area contenente l'intervallo di origine. la sintassi: espressione.End(Direction) dove: espressione Argomento necessario. Un'espressione che restituisce un oggetto nell'elenco. Direction Argomento necessario di tipo XlDirection. Specifica la direzione dello spostamento. XlDirection è una delle seguenti costanti: xlDown - verso il basso xlToRight - verso destra xlToLeft - verso sinistra xlUp - verso l'alto Ora vediamo di tradurre in "pellegrinese" queste istruzioni, aiutandoci con esempi. Supponiamo di avere nella colonna B dalla cella B1 fino alla cella B10 un elenco di dati: un'istruzione così compilata: Range("B1").End(xlDown).Select identificherebbe la cella finale dell'elenco, la B10, e la selezionerebbe. Come si nota, noi abbiamo quindi indicato nell'istruzione la cella iniziale dell'elenco (la B1), e l'istruzione, grazie all'uso di End con la direzione verso il basso (xlDown), trova la fine dell'ultima cella occupata dell'elenco. Che cosa vuol dire questo: che quando non sappiamo quanto sarà lungo un elenco, ma vogliamo identificare l'ultima cella occupata, è sufficiente indicare la cella iniziale più End per trovare senza fallo la cella finale. Come condizione determinante per il corretto reperimento della cella finale occupata, dovremo disporre di un elenco SENZA celle vuote nel mezzo. Se nell'elenco ipotizzato prima (B1:B10) avessimo avuto la cella B8 vuota, senza dati, l'esecuzione dell'istruzione avrebbe identificato la B7 come ultima cella occupata, trascurando di proseguire nonostante la B9 e la B10 contengano dati. Chiaro? Uno degli utilizzi classici della proprietà End (dell'oggetto Range) è la identificazione di un'area dati quando non sappiamo quanto è ampia l'area stessa, quando aggiungendo o togliendo righe di dati (o colonne), non possiamo determinarne a priori i riferimenti. Parlo dei riferimenti alle celle che contengono tutta l'area dati, di cui abitualmente usiamo indicare la cella iniziale e la cella finale che comprendono l'area. Se abbiamo un'area che va dalla prima cella della colonna A alla 22esima cella della colonna G noi usiamo questa sintassi: Range("A1:G22").Select - indichiamo appunto gli estremi dell'area per selezionarla. Ma quando non conosciamo quale sarà la cella finale e vorremo usare End per reperirla, dovremo usare una sintassi diversa, legata a Range non come "oggetto" del foglio di lavoro, ma come "Insieme Range" (cioè un insieme di celle). In quest'ottica, "l'Insieme Range" possiede delle proprietà, due delle quali sono lo stesso Range (come proprietà dell'Insieme Range), e la proprietà Cells. Servono entrambi per definire le celle, e possiamo usare indifferentemente l'uno o l'altro purchè non mescolati insieme. Vediamo prima l'utilizzo della prorietà Range: Range("A1", Range("G1").End(xlDown)).Select intanto vediamo che la differenza maggiore rispetto alla precedente istruzione è che non usiamo i due punti (:) come separatori tra le celle che delimitano l'area, ma una virgola; questo perchè la proprietà Range necessita di argomenti (per definire le celle), e come tali devono essere separati dalla virgola. Comunque vediamo che per identificare il secondo argomento (la cella finale), abbiamo usato End riferito alla cella iniziale della colonna G, la G1: in questo modo verrà cercata l'ultima cella occupata nella colonna G, e sarà questa cella a fornire il secondo riferimento. Se invece volessimo usare Cells, di cui ricordo brevemente i concetti: Proprietà Cells Utilizzare Cells(row, column) dove row è l'indice di riga e column è l'indice di colonna, per restituire una singola cella. http://ennius.interfree.it/
Pagina 257
MANUALE VBA
X EXCEL
dovremmo usare questa istruzione: Range(Cells(1, 1), Cells(1, 7).End(xlDown)).Select dove il primo argomento va letto come cella riga 1 colonna 1 (quindi A1), e il secondo argomento come cella riga 1 colonna 7 (e quindi G1). Anche in questo caso gli argomenti sono separati da virgola, e al secondo argomento è stata "affibbiata" End per reperire l'indice riga dell'ultima cella nella stessa colonna (la 7) che conterrà i dati. Impostando opportunamente le direzioni, potremo cercare sia celle in righe che in colonne, sia verso il basso sia verso l'alto, che a destra o a sinistra, dipenderà da come avremo impostato le nostre tabelle dati. Ciò che poi faremo con le aree identificate e selezionate non riguarda questa pagina, ma propongo un esempio, peraltro riscontrabile scaricando il file "TraduttoreFunzioni2000.zip" (vedi articolo "Traduttore Funzioni"), dove, dovendo prevedere l'inserimento di dati, nell'ordinamento alfabetico dei dati, viene usata la Proprietà End per reperire tutta l'area da ordinare. In questo caso, dovendo ordinare secondo una chiave specifica, e volendo che i dati correlati alla chiave, sulle stesse righe, si muovano insieme, vengono usate delle variabili (x e y) alle quali assegnare gli indirizzi (o riferimenti) delle celle che comprendono l'area. Alla prima variabile assegniamo il range che va dalla cella A1 alla C1, prendendo gli indirizzi (Address) ; le tre colonne che comprendono l'area dati: y = Range("A1:C1").Address
con la seconda variabile reperiamo, tramite End sulla prima colonna, l'indirizzo (Address) dell'ultima cella occupata: x = Range("A1").End(xlDown).Address poi selezioniamo tutta l'area usando i riferimenti (indirizzi o Address) ora "presenti" nelle due variabili : Range(y, x).Select Se andiamo a controllare (con "imposta punto di interruzione" e successive pressioni sul tasto F8 possiamo scorrere le istruzioni riga per riga), vediamo infatti che in Range(y, x) , la y corrisponde a "$A$1:$C$1"
e la x corrisponde a "$A$267"
e quindi l'area selezionata sarà:
http://ennius.interfree.it/
Pagina 258
MANUALE VBA
X EXCEL
E queste le istruzioni basate come chiave d'ordinamento sulla colonna A: Sub ordinaitaliano() Worksheets("Foglio2").Select Dim x, y y = Range("A1:C1").Address x = Range("A1").End(xlDown).Address Range(y, x).Select Selection.Sort Key1:=Range("A1"), Order1:=xlAscending, Header:=xlGuess, _ OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom End Sub Se invece, anche per le colonne non sapessimo quale sarà l'ultima che ci consenta di definire un riferimento preciso, potremo usare End dando come direzione xlToRight (verso destra) riferito alla cella iniziale (A1), e basterà sostituire questa riga nella routine appena vista, per ottenere la nostra selezione: y = Range("A1", Range("A1").End(xlToRight)).Address oppure, usando la proprietà Cells y = Range(Cells(1, 1), Cells(1, 1).End(xlToRight)).Address cioè, in entrambi i casi, stiamo dicendo : y è uguale al Range che parte dalla cella A1 fino all'ultima cella occupata partendo dalla A1, andando verso destra (quindi stessa riga). Credo di avere fatto un pò più di luce sull'argomento.
http://ennius.interfree.it/
Pagina 259
MANUALE VBA
X EXCEL
La proprietà Offset (relativa all'oggetto Range). Scarto, scartamento o spostamento, in Vba si chiama Offset .La sua sintassi è : ActiveCell.Offset(0, 1).Select oppure Range("Tuorange").Offset(0, 3).Value Un foglio di lavoro è composto da righe e colonne, e le celle vengono identificate dal loro "riferimento", cioè dalla lettera di colonna e dal numero di riga all'incrocio dei quali si trova una cella. Quindi la cella che si trova nella terza riga all'incrocio con la seconda colonna avrà il riferimento B3. (come in una "battaglia navale"). Si usa mettere nel riferimento, per prima la lettera che identifica la colonna, seguita senza spazi dal numero di riga. E' con questo sistema, quello dei riferimenti, che Excel ci consente di lavorare con i dati che saranno contenuti nelle celle. Usiamo infatti dei riferimenti quando compiliamo le nostre formule o le nostre macro, in questo modo "miriamo" con precisione alle celle che ci interessano. Esistono tuttavia situazioni nelle quali, pur "mirando" ad una cella, avremo bisogno di dati che si trovano non nella cella "mirata", ma in altre celle del foglio di lavoro, in genere contenenti dati correlati al dato contenuto nella cella "mirata". Possiamo fare un esempio: considerando di avere una tabella con un elenco di nomi (posti nelle righe della colonna A), per ogni nome: l'indirizzo (nella colonna B), la città (nella colonna C) il numero di telefono (nella colonna D). E di volere, dato un determinato nome, il suo numero di telefono. Cosa faremo? cercheremo con una procedura, il nome voluto, lo selezioneremo, e con l'uso di Offset andremo a pescare il numero di telefono che si trova sulla stessa riga, ma tre celle a destra rispetto alla cella in quel momento selezionata, per cui l'istruzione risulterebbe così: ActiveCell.Offset(0, 3).Value Ciò che faremo poi col numero di telefono così trovato non riguarda questa pagina, ma vediamo invece di capire l'istruzione Offset sopra esemplificata : abbiamo stabilito una cella di base (ActiveCell) e abbiamo detto ad Excel di volere il valore contenuto nella cella che si trova sulla stessa riga ma tre celle a destra rispetto alla cella selezionata. E questo l'abbiamo detto usando dei numeri : in una istruzione Offset, il primo numero identifica sempre la riga, il secondo numero identifica la colonna, rispetto ad una cella di riscontro. Abbiamo usato quindi lo Scarto. Le direzioni di Scostamento rispetto alla cella base, sono : a destra (positivo), a sinistra (negativo), sopra (negativo) o sotto (positivo). Utilizzando quindi un numero (positivo o negativo) per indicare la riga e un numero (negativo o positivo) per indicare la colonna, rispetto alla cella base, "pescheremo" il dato nella cella che ci interessa. Sotto, uno specchietto per capire come usare i riferimenti con Offset. Impostata la cella D7 come cella base, se volessimo "pescare" i dati nella cella F8, useremmo Offset(1, 2) (una riga sotto, due colonne a destra) rispetto a D7. (per i riferimenti positivi non occorre usare il +, per quelli negativi necessariamente andrà usato il - ) Con lo 0 (zero) si indica : nessun scarto, cioè stessa riga op. stessa colonna.
http://ennius.interfree.it/
Pagina 260
MANUALE VBA
X EXCEL
Proteggere e/o Limitare la durata nell'uso di una cartella Excel. In generale con "protezione" possiamo intervenire per proteggere un foglio di lavoro o tutta la cartella. Le modalità per l'utilizzo di queste "protezioni" sono note e comunque facilmente leggibili e apprendibili dai vari menù. Abbiamo poi la possibilità di proteggere anche il codice in vba (macro) attraverso l'editor di visual basic, scegliendo dal menù dell'editor, "Strumenti/Proprietà di VBA Project... e nella finestra che appare, scegliere "protezione", spuntando la casella "proteggi progetto dalla visualizzazione", e assegnando una password. Fatto questo è necessario salvare e chiudere la cartella : alla riapertura, se vorremo accedere al codice, dovremo digitare la password. Tutti questi metodi sono comunque inservibili se ci vogliono forzare le protezioni : esistono in commercio dei programmi che hanno il preciso compito di individuare e fornirci le password usate a protezione di fogli, cartelle e progetto vba, rendendo quindi inutili i nostri sforzi per proteggerci il lavoro. Per fortuna la maggior parte degli utenti di Excel rientra nell'utenza standard (non fraintendetemi), cioè di coloro che, per uso casareccio o di ufficio, usano excel senza malizia e senza sentirsi degli pseudoHacker. Le protezioni, per questi "onesti cittadini", servono e funzionano egregiamente. E' possibile peraltro munire i nostri lavori di "protezioni" personalizzate, oltre a quelle standard fornite da Excel, protezioni impostate tramite istruzioni in codice vba, che possono essere molto articolate e prevedere anche "scadenze" o "durate". Sarebbe in questo modo possibile distribuire dei lavori in versione "Trial" che se, non confermati, scadranno e non sarà possibile utilizzarli. Si può scegliere una scadenza a data oppure per numero di volte. Vediamo entrambi i casi, facendo prima alcune considerazioni. Non esiste una protezione inviolabile per gli "smanettoni", esistono solo protezioni valide per "pellegrini" "non smanettoni" (la maggioranza, per fortuna.) Excel quando apre un file che contiene macro, mostra sempre una finestra che chiede se vogliamo "Attivare" o "Disattivare" le macro (questo solo se NON abbiamo scelto in "Protezione/Macro" "Bassa protezione", in questo caso i file vengono aperti senza nessuna richiesta e con l'attivazione delle macro). Disattivare le macro ci consente di poter scorrere i fogli, di scoprire i fogli eventualmente nascosti, di girare in lungo e in largo, compreso l'accesso al codice, di poter modificare o eliminare istruzioni e/o dati presenti nei fogli (quei dati che vedremo più avanti, e che servirebbero a limitare l'uso del nostro programma). Al tempo stesso però, non sarà possibile attivare le istruzioni di nessuna macro e quindi non potremo gestire il lavoro se impostato con delle macro. Ci spostiamo tra i fogli perchè possiamo selezionare le "linguette" dei fogli (le WorksTab). Se le "linguette" non fossero visibili non potremo mai usarle e quindi spostarci. Esiste un istruzione, CHE SI ATTIVA anche se disabilitiamo le macro, e che consente appunto di non far vedere le "linguette", e senza linguette, non si va da nessuna parte, neanche quindi sul foglio dove avremo inserito le nostre istruzioni "segrete". Per poterci spostare tra i fogli, useremo dei pulsanti, associati a delle macro. Ma se disabilitiamo le macro, non ci potremo spostare, giusto? E quindi continuiamo a proteggerci. Sarà poi sufficiente NON prevedere un pulsante che porti al nostro foglio "segreto", e chiunque, anche abilitando le macro, non potrà mai accedervi. A questo punto, sarà sconsigliato "nascondere" il foglio "segreto", proprio perchè dal menù "Formato/Foglio" sarebbe possibile usare "Scopri foglio" che è un'azione che selezionerebbe il foglio così scoperto, mettendolo in primo piano, mostrando a tutti ciò che vogliamo invece tenere nascosto. Ricapitoliamo le operazioni da fare: munire l'accesso al codice vba di una password disabilitare le "linguette" dei fogli predisporre i pulsanti per spostarci sui fogli permessi (per attivare la protezione del codice e disabilitare le linguette occorre salvare, chiudere e riaprire il foglio, resteranno poi sempre attivi) vediamo le istruzioni da compilare: Nell'evento WorkBook_Open(), per disabilitare le linguette su tutti i fogli: ActiveWindow.DisplayWorkbookTabs = False In un Modulo nell'editor di vb, creare le macro per spostarsi tra i fogli: Sub apriuno() Sheets(1).Select End Sub Sub apridue() Sheets(2).Select End Sub preparare tante macro per quanti saranno i fogli su cui navigare, TRANNE quella che porterebbe al foglio che vorremo come "segreto". In ogni foglio predisporremo tanti pulsanti per quanti saranno i fogli, http://ennius.interfree.it/
Pagina 261
MANUALE VBA
X EXCEL
in modo che da qualunque foglio si possa andare su qualunque altro foglio. Ogni pulsante sarà associato alla macro di pertinenza. A questo punto ci troveremo in questa condizione: se abilitiamo le macro, andremo solo sui fogli di cui esistono i pulsanti, se NON abilitiamo le macro, non andremo da nessuna parte e le linguette non saranno comunque visibili. Per accedere al codice avremo bisogno di una password : la protezione è in atto. Ed ora vediamo i due sistemi per la "Prova a Scadenza" del nostro lavoro. Prova a tempo. Si basa sulla registrazione della data di primo accesso, e dopo un periodo stabilito (normalmente 15 o 30 giorni), non sarà più possibile l'uso del lavoro. Useremo il nostro foglio "nascosto" per queste registrazioni, e predisporremo le opportune istruzioni, sempre sfruttando l'evento WorkBook_Open(). Supponiamo di usare il foglio3. Avremo bisogno di un contatore che abiliti l'attivazione della registrazione della data di prima apertura (A1 e A2), metteremo in C1 la data che otterremo con la funzione =OGGI(), faremo copiare via codice solo la data e non la funzione, in D1, e in E1 metteremo la data + il tempo in giorni destinato al periodo di prova. (Abbiamo bisogno che questa operazione sia fatta una volta sola, per questo usiamo un contatore), avremo poi bisogno di un'istruzione che controlli se la data odierna (in C1) sarà maggiore della data posta in E1 (limite massimo di prova) o minore della data posta in D1 (limite di inizio prova) che faremo invece aggiornare ad ogni chiusura cartella. Se qualcuno, da pannello di controllo, regolasse la data a un certo giorno antecedente, provocherebbe la chiusura della cartella così come avverrà se superata la data di termine prova. Vediamo le istruzioni e le spiegazioni: Foglio3, cella A1 inserire zero (0) - cella C1 inserire =OGGI() Private Sub Workbook_Open() 'si disabilitano le linguette ActiveWindow.DisplayWorkbookTabs = True 'si inizializza il contatore incrementando A2 di 1 rispetto a A1 (che è zero) Sheets(3).Range("A2") = Sheets(3).Range("A1") + 1 'si rende A1 uguale a A2 (ora A1 è uguale a 1) Sheets(3).Range("A1") = Sheets(3).Range("A2") 'si controlla se A1 è uguale a 1 (e quindi solo in questo caso si eseguono queste istruz.) If Sheets(3).Range("A1").Value = 1 Then 'allora si copia la data di C1 (oggi) in D1 Sheets(3).Range("D1") = Sheets(3).Range("C1") 'e si incrementa la data C1 del valore scelto (15) in E1 Sheets(3).Range("E1") = Sheets(3).Range("C1") + 15 End If 'ora inizia il controllo sulle date: se la data di oggi è maggiore di quella scritta in E1 If Sheets(3).Range("C1").Value > Sheets(3).Range("E1") Then 'allora si chiude l'applicazione e quindi anche Excel Application.Quit End If 'se la data di oggi è inferiore alla data registrata in D1 (che corrisponde alla prima esecuzione del programma (nel caso che qualche "astuto" retroceda la data in pannello di controllo)(legata anche all'istruzione posta in WorkBook_BeforeClose) If Sheets(3).Range("C1").Value < Sheets(3).Range("D1") Then 'allora si chiude l'applicazione e quindi anche Excel Application.Quit End If End Sub E queste le istruzioni in chiusura della cartella: Private Sub Workbook_BeforeClose(Cancel As Boolean) 'questo controllo aggiorna la data che si troverà in D1, e che serve di controllo alla routine precedente, riducendo i giorni (all'indietro) di prova: se la data di oggi è superiore alla data presente in D1, D1 viene aggiornato con la data di oggi. Se il http://ennius.interfree.it/
Pagina 262
MANUALE VBA
X EXCEL
solito "astuto" metterà indietro la data, scatta la chiusura dell'applicazione. If Sheets(3).Range("C1") > Sheets(3).Range("D1") Then Sheets(3).Range("D1") = Sheets(3).Range("C1") End If 'questa istruzione garantisce che in uscita venga comunque salvata la cartella con le registrazioni sulle date e sul contatore. ThisWorkbook.Save End Sub Volendo è possibile munire l'istruzione di un messaggio che avvisa quanti giorni mancano alla scadenza. (usando la fuzione DateDiff) Prova a numero di aperture. Si basa sul conteggio di quante volte viene aperta la cartella del nostro lavoro. Risulta più semplice da impostare rispetto alla precedente (questione di opinioni) e anche qui useremo un contatore che inizializzeremo partendo da zero (A1), e decideremo il numero di aperture consentite. Come sopra sfruttiamo il foglio3 con le celle A1 e A2, la durata sarà di 15 aperture. (non ripeto le istruzioni già fornite nella precedente routine) Private Sub Workbook_Open() ActiveWindow.DisplayWorkbookTabs = False Worksheets(3).Range("A2") = Worksheets(3).Range("A1") + 1 Worksheets(3).Range("A1") = Worksheets(3).Range("A2") 'sotto: con X prendiamo il numero di volte rimaste, con una semplice sottrazione X = 15 - Worksheets(3).Range("A2").Value If Worksheets(3).Range("A2").Value >= 15 Then Application.Quit Else 'avvisiamo con un messaggio che riporta il numero di prove restanti MsgBox "Hai ancora " & X & " prove da fare" End If End Sub e questa l'istruzione in chiusura cartella : Private Sub Workbook_BeforeClose(Cancel As Boolean) ThisWorkbook.Save End Sub Alcuni consigli: si possono inserire quanti fogli vorremo, per usare un foglio come "segreto"; non è detto che le celle usate per le protezioni siano necessariamente sul foglio3, potrebbero stare sul dodicesimo di venti fogli, per esempio. Non scordarsi dei pulsanti per la selezione dei fogli.
http://ennius.interfree.it/
Pagina 263
MANUALE VBA
X EXCEL
Calcolare provvigioni a scalare con il Vba. (04/04/03) Ancora un esercizio con un calcolo provvigionale, con utilizzo di una Funzione Utente oppure di una routine. A differenza del paragrafo precedente "Provvigioni differenziate" dove le provvigioni si modificavano in funzione dell'importo della fattura e dello sconto concesso al cliente, restando computate per intero con l'aliquota provvigionale prevista (in toto), questa volta ci occupiamo di provvigioni a scalare in funzione dello scaglione di importo fattura (al di là dello sconto concesso, che considereremo comunque a parte). Ma riassumiamo le diversità: nell'esempio precedente, se variava l'importo fattura (5000=8% - 25000=4% - oltre 25000=2,5%) si assegnava la provvigione prevista su tutto l'importo fattura. per cui la provvigione spettante su una fattura di € 10000, sarebbe stata del 4% e cioè di 400 €. (ripeto, senza tenere conto dello sconto cliente che comunque avrebbe poi contribuito a variare l'aliquota in questo caso del 4%). In questo esercizio invece useremo il concetto che troviamo anche nella sezione "Formule", paragrafo "tabella calcolo IRPEF", dove le aliquote vengono applicate secondo il sistema fiscale: fino ad una certa cifra, una certa aliquota, da questa cifra fino ad un'altra, un'altra aliquota sulla cifra eccedente la prima cifra, e così via. Tutti conosciamo il sistema di tassazione attualmente in vigore. Applicheremo quindi una provvigione dell'8% fino a 5000, del 4% sulla cifra tra 5000 e 25000, e del 2,5% sulle cifre oltre 25000. Sommeremo poi le tre provvigioni, nel caso presenti, in modo da ottenere un totale provvigioni da assegnare all'agente. Per rendere il tutto il più simile all'esercizio precedente, anche qui valuteremo lo sconto concesso al cliente, in modo da ridurre la provvigione assegnata allo scaglione di pertinenza. Non ripeto queste condizioni che sono leggibili nel paragrafo su citato. Per meglio presentare questo esercizio, presento due soluzioni, entrambe portano allo stesso risultato. La prima è una macro, la seconda l'ho impostata come Funzione, in modo da poterla usare direttamente sul foglio di lavoro. Vediamo la macro: nelle istruzioni esemplifico identificando l'origine del campo importo fattura e sconto concesso, ipotizzando che i due valori si trovino il primo in A1 (importo) e il secondo in B1(sconto). Uso ancora due variabili per assegnare le due costanti relative agli scaglioni che determinano l'assegnazione delle aliquote provvigionali, base (per lo scaglione fino a 5000) e basedue (per lo scaglione da 5000 25000) e oltre. L'identificazione degli scaglioni di pertinenza delle provvigioni le identifico con dei cicli If..Then...Else. Una volta assegnato alle variabili b - c - d gli importi degli scaglioni, uso Select Case per diversificare le due condizioni degli sconti ( fino a 5% o maggiore di 5%). All'interno di questi due casi, eseguo i conteggi di assegnazione delle provvigioni. Il risultato lo riporto in una messagebox, ma può essere tranquillamente assegnata una cella dove restituire il risultato. Sub ProvvScalare() 'assegnazione alle variabili sconto e importo dei valori contenuti nelle due celle sconto = Range("B1").Value importo = Range("A1").Value 'assegnazione alle variab. base e basedue delle costanti dei valori degli scaglioni base = 5000 basedue = 25000 'controllo se base (5000) è maggiore o uguale a importo (Range("A1")) If base >= importo Then 'nel qual caso assegno alla variab. minimo lo stesso valore di importo minimo = importo Else 'in caso contrario rendo la variab. minimo uguale a base (5000) minimo = base End If 'inizio l'assegnazione alle variab, b - c - d dei valori che corrisponderanno agli importi 'relativi ai vari scaglioni b = minimo If basedue >= importo Then c = importo - b Else c = basedue - b End If d = importo - (c + b) http://ennius.interfree.it/
Pagina 264
MANUALE VBA
X EXCEL
'si controlla che esistano gli importi relativi ai tre scaglioni, altrimenti si assegna zero alla 'variabile dello scaglione. Se il valore fosse nullo (non esistesse), si genererebbe un errore 'nelle istruzioni successive dove si eseguono i calcoli su questi valori. If b < 0 Then b = 0 If c < 0 Then c = 0 If d < 0 Then d = 0 'inizio il controllo sugli sconti, usando Select Case, il primo caso è che lo sconto sia 5 o 'inferiore a 5 Select Case sconto Case Is 5 y = 8 - ((sconto - 5) / 3) z = 4 - ((sconto - 5) / 3) w = 2.5 - ((sconto - 5) / 3) If y < 1.5 Then y = 1.5 If z < 1.5 Then z = 1.5 If w < 1.5 Then w = 1.5 If cifra = 5001 And cifra = 25001 Then X = w End Select Provv = (X * cifra) / 100 End Function Gli argomenti cifra e sconto non saranno altro che il riferimento alla cella che contiene l'importo fattura il primo, e una cella che destineremo a contenere il valore dello sconto concesso, il secondo. Questa http://ennius.interfree.it/
Pagina 267
MANUALE VBA
X EXCEL
funzione potrà essere inserita in una cella, esempio la C1, visto che nel problema su esposto si citava la A1 e la B1, e sarà : =Provv(A1;B1) ed in C1 avremo il risultato della formula. Se l'elenco fosse su più righe, sarà sufficiente usare il "trascinamento" per copiare la formula dalla C1 nelle celle sottostanti, ed il bravo Excel provvederà ad aggiornare i riferimenti alle celle di pertinenza. Se poi si volesse, nel caso di elenchi lunghi, affidarsi ad una macro per inserire la funzione nelle celle previste, sarà possibile richiamare la funzione assegnandogli gli argomenti, come faremmo con qualsiasi altra funzione. Questo un esempio: elenco importo fatture nella colonna A, nella B gli sconti concessi, a partire dalla riga 2. Nella colonna C vorremo il conteggio provvigioni. la macro: Sub provvigioni() Dim CL As Object For Each CL In Range("C2:G200") 'definizione con "importo" dell'importo fattura che si trova stessa riga, 2 colonne a sinistra '(la A), rispetto a CL, primo argomento della funzione Provv importo = CL.Offset(0, -2).Value 'definizione con "scon" del valore dello sconto che si trova stessa riga, 1 colonne a sinistra '(la A), rispetto a CL, secondo argomento della funzione Provv scon = CL.Offset(0, -1).Value 'se la cella CL è vuota, allora If CL = "" Then 'nella cella CL si mette il risultato della funzione: la provvigione spettante CL = Provv(importo, scon) End If Next End Sub non ho inserito un controllo se l'importo fattura o lo sconto non sono presenti, perchè in caso manchino dati il risultato della funzione sarà zero. Due precisazioni: nel campo "sconto" (colonna B) dovrà essere inserito lo sconto in formato numero e NON percentuale ( 5 e non 5%); i campi importo fattura (colonna A) e provvigioni (colonna C) dovranno avere il formato celle a numero con 2 decimali.
http://ennius.interfree.it/
Pagina 268
MANUALE VBA
X EXCEL
Utilizzare dell'istruzione Clear. Anche se semplice, a fronte di alcune domande rivoltemi, vorrei suggerire come usare l'istruzione "CLEAR", cioè : "pulisci" o se preferite, "vuota". Una prima precisazione và fatta nel distinguo tra "Clear" e "ClearContents" . Clear "pulisce" la/le celle da ogni contenuto, numeri, lettere ecc., ma elimina anche formule presenti nella/e celle, oltre a formato bordi, colore font, colore cella, praticamente tutto. Sub Cancella() Range("A2:C23").Clear End Sub ClearContents invece "pulisce" la/le celle solo dei valori e delle eventuali formule, lasciando inalterate le altre eventuali impostazioni. Sub Cancella() Range("A2:C23").ClearContents End Sub E' possibile usare uno dei due comandi anche per pulire intere righe, o intere colonne, oppure celle non contigue, "sparse" sul foglio, con le seguenti rispettive istruzioni: pulire, ad esempio, tutta la riga 8: Rows("8:8").ClearContents pulire, ad esempio, tutta la colonna C : Columns("C:C").ClearContents Pulire, ad esempio, le celle B1, F4, A10,G3 Range("B1,F4,A10,G3").ClearContents
http://ennius.interfree.it/
Pagina 269
MANUALE VBA
X EXCEL
Registrare dei dati in un archivio dati. In questa esercitazione ci riallacciamo a quanto contenuto nel paragrafo precedente dove si cercano dei totali tra due date, in un archivio dati. Questa volta ci occupiamo di come trasferire i dati provenienti da una zona di registrazione, nell'archivio. In questo esempio, simuliamo di avere un range di celle dove a fine giornata inseriremo il totale incassato, diviso per prodotto. Che il totale si inserisca manualmente oppure sia il risultato di una somma, non cambia niente: ci troveremo sempre con una riga di celle dove avremo dei dati, divisi per colonna, ed è questa riga con i totali giornalieri divisi per prodotto che noi vorremo registrare in un archivio (una tabella) che contenga i totali per data, in modo da poter successivamente consultare questo archivio, per indagini sul totale incassi relativi a periodi di date. Il funzionamento delle procedure è semplice: è un Copia/Incolla con ricerca della destinazione basata su una chiave di ricerca: la data del giorno. Useremo una cella dove inseriremo la funzione =OGGI() per aggiornare automaticamente la data del giorno all'apertura del foglio di lavoro (nell'esempio la D1). Disporremo la zona di raccolta dei dati da registrare (nell'esempio le celle che vanno da B3 a F3 comprese), alla quale assegneremo un Nome (dal menu Inserisci/Nome/definisci) (in questo esempio : Importo) così nelle istruzioni useremo il nome anzichè la definizione del range interessato. Disporremo sullo stesso foglio (ma potremo farlo anche su un'altro foglio) una tabella che sarà l'archivio in cui registrare i totali giornalieri. In questa tabella useremo la prima colonna a sinistra (nell'esempio la colonna A) per predisporre le date di registrazione. Lo potremo fare agevolmente, basterà scrivere le prime due date con cui inizia il nostro archivio, selezionarle entrambi e, usando il "trascinamento", scorrere verso il basso: Excel, capendo che intendiamo ottenere una "serie", provvederà a inserire tutte le date in progressione, fino a quando decidiamo di fermarci. Sotto vediamo l'effetto "trascinamento" dove veniamo avvisati del valore (data) raggiunto alla fine della selezione (NB: il formato celle dovrà essere impostato a Data, con lo stesso formato usato per la cella D1):
Per le istruzioni useremo una Funzione Utente per la ricerca della riga corrispondente alla data odierna (nella colonna A dell'archivio) ed una procedura che, una volta trovata la riga, selezioni la cella immediatamente a destra della cella con la data trovata, e provveda a Copiare (prelevare è più giusto) i dati dalla zona che abbiamo chiamata Importo (Range B3:F3) e la Incolli (inserisca è più giusto) nella cella in quel momento attiva. Assegneremo ad un pulsante la macro creata, ed il gioco è fatto: a fine giornata premeremo il pulsante e i dati verranno archiviati alla data corrispondente. Sotto un'immagine dell'esempio preparato:
E queste le istruzioni, per prima la funzione per la ricerca: Function cercadata(Intervallo, valore) http://ennius.interfree.it/
Pagina 270
MANUALE VBA
X EXCEL
For Each c In Intervallo If c.Value = valore Then Y = c.Row Exit For End If Next c cercadata = Y End Function e quindi la procedura (nell'esempio mi sono fermato alla riga 134) assegnata al pulsante "Registra Incasso" Sub registra() Dim oggi As Date 'dichiarazione del "tipo" di dati della cella D1 assegnato a "oggi" oggi = Range("D1").Value 'la variabile "oggi" è uguale alla data in D1 Set rango = Range("A8:A134") 'si assegna a "rango" la zona in cui avviene la ricerca cerca = cercadata(rango, oggi) '"cerca" è il risultato della Funzione "cercadata", che 'cerca in "rango" il valore "oggi" e restituisce il numero di riga nell'archivio, corrispondente 'alla data odierna (rango e oggi sono i due argomenti della funzione cercadata) Set Zona = Range("Importo") 'assegniamo a Zona il Range B3:F3 Rows(cerca).Cells(1, 2).Select 'selezioniamo la seconda cella della riga (cerca) Zona.Copy ActiveCell 'copiamo il contenuto di Zona nella cella attiva End Sub Ricordo che che quando si vuole incollare il contenuto di più celle (anche non contigue, MA presenti sulla stessa riga) è sufficiente selezionare per la destinazione una SOLA cella iniziale, sarà Excel a provvedere che l'incollaggio avvenga in tante celle CONTIGUE per quante sono le celle di provenienza.
http://ennius.interfree.it/
Pagina 271
MANUALE VBA
X EXCEL
Registrare Prenotazioni su un Foglio di lavoro. (04/05/03) Uno fra i lavori che vorremmo realizzare con Excel, vista la sua naturale impostazione in righe e colonne che formano tante celle, è certamente quello di crearci un "Planning", cioè un foglio dove poter registrare prenotazioni di Camere d'albergo, o anche di appartamenti nei Bed and Breakfast, ma potrebbe essere anche un Planning per turni di lavoro, per appuntamenti, per.... per.... Tanti sono i motivi. Normalmente ci si affida ad un planning Mensile, e facendo l'esempio sulle camere, per ogni camera, ma potrebbe essere per ogni dipendente nel caso dei turni di lavoro, ecc.ecc. Presento un esempio impostato sulla necessità di controllare visivamente i giorni di prenotazione di una camera, in un Planning annuale. Come controllo, coloreremo le celle corrispondenti alle prenotazioni, di un colore in contrasto col resto del foglio. Vediamo l'esempio del Planning:
Come vedete, è stata predisposta una griglia, che per quanto riguarda le celle interessate ai giorni e ai mesi, partono dalla colonna C (quindi la numero 3), e dalla riga 5. Questi riferimenti sono importanti perchè ci serviranno nelle istruzioni, da quale riga e quale colonna iniziare a contare. Prima di passare ad illustrare le procedure adoperate, premetto un'altra necessità che sorge nel determinare, quanti giorni possiede ogni mese, visto che non tutti i mesi sono di 31 giorni. Per questo ho realizzato una Funzione Utente, che verrà poi richiamata nelle routine. Questa la funzione, che restituisce il numero di giorni in funzione del mese espresso come numero (1=gen, 2=feb, ecc): nella sezione Generale - Dichiarazioni di un Modulo, inseriamo questa istruzione, per "aiutare" la funzione ad essere visibile anche al di fuori del modulo stesso: Option Explicit ___________________________________________________________________ Function giorni(mese) 'questa funzione serve a definire quanti giorni ha un mese in modo che nel calcolo di 'registrazione sul foglio si possa determinare automaticamente il fine mese. Per 'febbraio ho considerato fisso 28 giorni. Nel bisestile lo farete manualmente di colorare il 'giorno 29. Select Case mese Case 1, 3, 5, 7, 8, 10, 12 giorni = 31 Case 4, 6, 9, 11 giorni = 30 Case 2 giorni = 28 End Select End Function Ho poi usato una UserForm che richiamo col CommandButton sul foglio, e nella quale inseriremo le due date: di inizio e di fine prenotazione. Le date le inseriremo in due TextBox, e per comodità visiva ho usato altre 4 textbox nelle quali svolgeremo delle trasformazioni. Premesso che per queste operazioni si può usare delle variabili anzichè delle textbox, io ho usato queste perchè più facilmente rintracciabili nelle istruzioni e rendono, credo, più comprensibile il tutto. Sulla UserForm ho inserito due altri pulsanti: quello per lo sviluppo delle routine che coloreranno le celle, ed uno per ripristinare il colore di fondo, "ripulendo" quindi la tabella. Vediamo la UserForm: http://ennius.interfree.it/
Pagina 272
MANUALE VBA
X EXCEL
e questo sarà il risultato:
Per le Textbox indicate dalle frecce, imposteremo poi la loro proprietà "Visible" a False. Nelle due textbox indicate in blu, estraggo il giorno presente nella textbox a sinistra, e nelle due indicate in rosso, estraggo il mese (in numero). Le routine sono state inserite nell'evento Exit della textbox (la 2) relativa alla data di partenza, in modo che, spostandoci col Focus sulla textbox (la 7) della camera N°, si attivi l'evento. Queste le istruzioni: Private Sub TextBox2_Exit(ByVal Cancel As MSForms.ReturnBoolean) TextBox3 = Day(CDate(TextBox1)) 'restituisce il giorno della data di arrivo TextBox5 = Month(TextBox1) 'e il mese espresso in numero TextBox4 = Day(CDate(TextBox2)) 'restituisce il giorno della data di partenza TextBox6 = Month(TextBox2) 'e il mese espresso in numero End Sub Come già detto, è possibile usare delle varianti anzichè delle textbox, esempio: giornoarrivo = Day(CDate(TextBox1)) 'restituisce il giorno della data di arrivo mesearrivo = Month(TextBox1) 'e il mese espresso in numero basterà poi usare i nomi delle varianti (giornoarrivo, mesearrivo) nelle istruzioni al posto del riferimento alle textbox. Sempre nella Userform vediamo la TextBox7 dove appare il numero della camera. Questo numero dovrà corrispondere anche al nome assegnato al foglio di lavoro che identificherà appunto il planning relativo a quella camera, inoltre il numero camera ci servirà anche per controllare se siamo sul foglio giusto quando premeremo il pulsante "Pulisci Foglio Attivo". Infatti nell'evento Click del commandbutton facciamo controllare se il nome del foglio corrisponde al numero camera; in caso positivo si ripristina il colore di tutte le celle (in questo caso in grigio), altrimenti veniamo avvisati con un messaggio. Questa la routine: Private Sub CommandButton2_Click() 'queste istruz. controllano se siamo sul foglio che corrisponde al numero scritto nella 'textbox7, e in caso positivo colorano tutte le celle dei giorni in grigio If ActiveSheet.Name = TextBox7.Value Then http://ennius.interfree.it/
Pagina 273
MANUALE VBA
X EXCEL
With Range("C5:AG16") .Cells.Interior.ColorIndex = 15 End With Else MsgBox "Non siamo sul Foglio giusto" End If End Sub Anzichè usare una textbox nella quale digitare il numero ( in lettere ) potremmo usare una ComboBox che "peschi" con il suo RowSource un elenco con i numeri delle camere, e basterà poi selezionare il numero voluto nella ComboBox. In questo caso va modificata l'istruzione di confronto in questo modo: If ActiveSheet.Name = ComboBox1.Text Then Passiamo ora ad esaminare le istruzioni collegate all'evento Click del pulsante "Registra sul foglio". Private Sub CommandButton3_Click() 'sotto: leggo il nome del foglio che corrisponde alla textbox7 e lo seleziono Worksheets("" & TextBox7.Value & "").Select 'sotto: inizia il controllo delle date per colorare le celle. Si possono verificare 3 condizioni: '1) le due date corrispondono allo stesso mese es: 10/03/03 - 25/03/03 '2) le due date accavallano un mese es: 20/03/03 - 05/04/03 '3) le due date accavallano più di un mese es: 20/04/03 - 05/07/03 'Per stabilire le tre condizioni sopra citate, usiamo un cico If... Then..ElseIf; nella prima 'condizione verifichiamo se i valori (numeri) che rappresentano i mesi, e che si trovano 'nelle textbox 5 e 6 sono uguali (cioè se le date appartengono allo stesso mese), se la 'condizione è vera, allora eseguiamo le istruzioni: If TextBox5.Value = TextBox6.Value Then 'assegniamo alla variabile r il valore che è nella textbox5 e che serve a conoscere quale 'riga identificare, cioè nell'esempio la riga 4 (aprile) AGGIUNGENDO il numero di 'quante righe ci sono prima che inizi la tabella, che sono 4 (gennaio inizia dalla riga 5), 'quindi 4+ 4 = 8 (infatti nella tabella il mese Aprile si trova sulla riga 8) r = TextBox5.Value + 4 'seleziona la riga del mese 'ora assegniamo alla variabile c il valore che si trova nella textbox3 (cioè 15, che è il 'giorno) e che serve a identificare la colonna del giorno di inizio. Poichè anche in questo 'caso come per le righe, le colonne dei giorni iniziano dalla C cioè la colonna 3, 'aggiungiamo 2 al valore della textbox3 che sarà 15+2=17 (infatti nella tabella il giorno 15 'è nella colonna Q che è la diciassettesima da sinistra) c = Val(TextBox3) + 2 f = Val(TextBox4) + 2 'a questo punto sappiamo con r in quale riga assegnare il colore, e con c ed f abbiamo i 'giorni, e quindi usiamo queste variabili per identificare il range di celle da colorare. Range(Cells(r, c), Cells(r, f)).Interior.ColorIndex = 40 'sotto abbiamo la seconda condizione, cioè se le date accavallano un mese, esempio dal 15/04/03 al 05/05/03 (che è l'esempio citato nell'immagine della userform); quindi 'controlleremo se la textbox6 sarà uguale al valore della textbox5 +1, in caso positivo, 'seguono le istruzioni che assomigliano a quelle sopra, solo che dovremo identificare due 'righe (quelle dei due mesi che appartengono alle due date), in più abbiamo bisogno di 'sapere quando finisce il mese appartenente alla data di ingresso, e per fare questo usiamo 'la funzione vista inizio pagina, che dato il numero del mese, restituisce di quanti giorni è 'formato, e ci servirà per determinare quale colonna usare per colorare il periodo dalla 'data di inizio, alla fine del mese. Mentre per il mese successivo sapremo che inizia dalla 'colonna 3, mentre i giorni li prenderemo dalla Data della partenza. ElseIf TextBox6.Value = Val(TextBox5) + 1 Then r = TextBox5.Value + 4 'identifica la riga del mese c = Val(TextBox3) + 2 'identifica colonna del giorno di arrivo f = giorni(TextBox5) + 2 ' identifica tramite la funzione "giorni" la fine del mese e quindi, http://ennius.interfree.it/
Pagina 274
MANUALE VBA
X EXCEL
'riga sotto, colora le celle del range cosi reperito (data arrivo-fine mese) Range(Cells(r, c), Cells(r, f)).Interior.ColorIndex = 40 'sotto:ora si reperiscono riga (cioè mese), colonna iniziale(la 3) e giorno della Data di 'partenza e si colorano le celle rr = TextBox6.Value + 4 cc = 3 ff = TextBox4.Value + 2 Range(Cells(rr, cc), Cells(rr, ff)).Interior.ColorIndex = 40 'terza condizione: questa condizione deve prevedere un numero imprecisato di mesi che 'possano intercorrere tra le due date; infatti possiamo avere due date 02/04/0305/06/03 '(tre mesi: aprile,maggio,giugno), ma potrebbero essere 10/04/03-15/08/03 (cinque mesi: 'aprile,maggio,giugno,luglio,agosto). Abbiamo bisogno di inserire un ciclo che valuti quanti 'mesi sono presenti fr le due date, e ci affideremo ad un ciclo For..Next. 'quindi controlleremo se la data di partenza è maggiore rispetto alla data di arrivo + 1 'mese , in caso positivo il codice eseguirà queste istruzioni, che differiscono dalle 'precedenti solo per quanto riguarda il ciclo For Next che assegnerà a mesi interi la 'colorazione per tutto il mese. In questo caso i giorni saranno reperiti dall'inizio mese, che 'ripeto, si trova nella colonna 3, e per ogni fine mese alla funzione "giorni" ElseIf TextBox6.Value > Val(TextBox5) + 1 Then 'con la variabile X prendiamo la differenza in numero tra le textbox dei mesi X = Val(TextBox6) - Val(TextBox5) r = TextBox5.Value + 4 'identifica la riga del mese data di arrivo c = Val(TextBox3) + 2 f = giorni(TextBox5) + 2 Range(Cells(r, c), Cells(r, f)).Interior.ColorIndex = 40 'inizia il ciclo che assegna a N il vettore X meno 1 (il primo mese) For N = 1 To X - 1 d = 3 'con d reperiamo l'inizio del mese fff = giorni(r + N + 1) + 2 'con fff usiamo la funzione "giorni" per reperire la fine mese. 'Usiamo r come contatore al quale aggiungiamo N + 1, + 2 (le due colonne che portano 'alla 3) 'quindi coloriamo tutte le celle del mese Range(Cells(r + N, d), Cells(r + N, fff)).Interior.ColorIndex = 40 Next 'sotto finiamo di reperire e di colorare la parte dall'inizio al giorno del mese 'corrispondente alla data di partenza rr = TextBox6.Value + 4 cc = 3 ff = TextBox4.Value + 2 Range(Cells(rr, cc), Cells(rr, ff)).Interior.ColorIndex = 40 End If End Sub Sotto, un esempio che mostra l'effetto delle istruzioni sopra, con l'esecuzione della terza condizione, data di arrivo 14/04/03, data di partenza 10/08/03 (5 mesi) e il rispetto dei relativi fine mese.
http://ennius.interfree.it/
Pagina 275
MANUALE VBA
http://ennius.interfree.it/
X EXCEL
Pagina 276
MANUALE VBA
X EXCEL
Classifica dei primi 10 Uso delle funzioni MAX e GRANDE abbinate a codice vba per la ricerca dei 10 valori più alti e relativi indici. In quest'esempio, esaminiamo come poter analizzare una tabella dati dove figurano i nomi degli agenti di un'ipotetica ditta e i relativi punteggi in un periodo determinato di tempo, per ricavare la Classifica dei primi 10, nell'arco di tutto il periodo. Questo è un esempio che si presta per ottenere numerose varianti nella ricerca di una classifica con evidenzazione degli agenti e relativi punteggi. Le funzioni usate non sono difficili da capire: la funzione MAX cerca il maggior valore presente in un elenco di dati numerici, e lo riporta nella cella dove risiede la formula. La funzione GRANDE è simile alla precedente, ma offre la possibilità di reperire la graduatoria di un valore in un insieme di dati, variando il parametro di ricerca: vediamo la sintassi delle due funzioni: MAX(num1;num2;...) es.: =MAX(A2:A6) Riporta il numero più grande tra quelli indicati nel range Se un argomento è costituito da una matrice o da un riferimento, verranno utilizzati soltanto i numeri presenti nella matrice o nel riferimento, mentre le celle vuote, i valori logici o il testo verranno ignorati. Se non si desidera che i valori logici e il testo vengano ignorati, utilizzare la funzione MAX.VALORI al posto di questa funzione GRANDE(matrice;k) Matrice è la matrice o l'intervallo di dati di cui si desidera determinare il k-esimo valore più grande. K è la posizione nella matrice o nell'intervallo di celle dei dati da restituire (partendo dal più grande). es.: =GRANDE(A2:B6;3) darà il 3° numero più grande tra i numeri presenti nel range. Se una matrice non contiene alcun dato, GRANDE restituirà il valore di errore #NUM!.Se una matrice contiene più valori uguali, verrà considerato comunque il valore che si trova nella k esima posizione tra i valori uguali. Es.. =GRANDE(A1:A10;7) ed i valori nel range sono 5,3,6,3,2,1,4,8,9,3 verrà considerato il secondo 3 (7° posizione a parità di valore). Per le altre caratteristiche delle funzioni vi rimando alla guida in linea (da consultare). Questa è l'immagine della tabella:
La cella A22 porta la funzione =MAX(B3:H19) e quindi il valore 1° classificato, le altre celle fino alla J22, sulla stessa riga, portano la funzione =GRANDE(B3:H19;2) fino a =GRANDE(B3:H19;10) che porterà il 10° classificato. La riga 23 porta solo i colori di identificazione classifica, con all'interno il codice del colore, che serve (ColorIndex) nelle istruzioni Vba per colorare la cella che ospita il valore relativo alla graduatoria. La riga 24 invece ospita il nome dell'agente che ha realizzato il relativo punteggio presente due righe sopra. Questo nome viene "pescato" dal codice, che dopo aver colorato la cella relativa alla classifica punteggio, si sposta a inizio stessa riga, copia il nome, e lo "scarica" nella rispettiva cella della riga 24. Il pulsante per attivare la routine ha una doppia funzione, serve per attivare la routine e per azzerare i valori; funziona come interruttore. Vediamo la routine: Private Sub CommandButton1_Click() If CommandButton1.Caption = "Mostra" Then CommandButton1.Caption = "Azzera" http://ennius.interfree.it/
Pagina 277
MANUALE VBA
X EXCEL
Application.ScreenUpdating = False 'evita il saltellamento sul foglio 'dichiarazioni delle variabili Dim CL As Object x = Range("A22").Value 'assegnazioni dei valori alle variabili (celle con funzioni) y = Range("B22").Value w = Range("C22").Value z = Range("D22").Value t = Range("E22").Value u = Range("F22").Value m = Range("G22").Value p = Range("H22").Value a = Range("I22").Value j = Range("J22").Value 'per ogni cella (CL) nel range B3:H19, se il valore della cella è uguale alla variabile (x) indicata, selezioni la 'cella, la colori, ti sposti alla cella di inizio riga (colonna A), copi il valore (nome agente), selezioni la cella di 'destinazione (A24) e incolli il nom. Quindi la routine continua per le rimanenti variabili (ElseIf). For Each CL In Range("B3:H19") If CL.Value = x Then CL.Select CL.Interior.ColorIndex = 6 With ActiveCell Cells(ActiveCell.Row, 1).Select Selection.Copy Range("A24").Select Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _ :=False, Transpose:=False End With ElseIf CL.Value = y Then CL.Select CL.Interior.ColorIndex = 40 With ActiveCell Cells(ActiveCell.Row, 1).Select Selection.Copy Range("B24").Select Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _ :=False, Transpose:=False End With ElseIf CL.Value = w Then CL.Select CL.Interior.ColorIndex = 38 With ActiveCell Cells(ActiveCell.Row, 1).Select Selection.Copy Range("C24").Select Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _ :=False, Transpose:=False End With ElseIf CL.Value = z Then CL.Select CL.Interior.ColorIndex = 43 With ActiveCell Cells(ActiveCell.Row, 1).Select Selection.Copy http://ennius.interfree.it/
Pagina 278
MANUALE VBA
X EXCEL
Range("D24").Select Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _ :=False, Transpose:=False End With ElseIf CL.Value = t Then CL.Select CL.Interior.ColorIndex = 4 With ActiveCell Cells(ActiveCell.Row, 1).Select Selection.Copy Range("E24").Select Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _ :=False, Transpose:=False End With ElseIf CL.Value = u Then CL.Select CL.Interior.ColorIndex = 35 With ActiveCell Cells(ActiveCell.Row, 1).Select Selection.Copy Range("F24").Select Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _ :=False, Transpose:=False End With ElseIf CL.Value = m Then CL.Select CL.Interior.ColorIndex = 15 With ActiveCell Cells(ActiveCell.Row, 1).Select Selection.Copy Range("G24").Select Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _ :=False, Transpose:=False End With ElseIf CL.Value = p Then CL.Select CL.Interior.ColorIndex = 34 With ActiveCell Cells(ActiveCell.Row, 1).Select Selection.Copy Range("H24").Select Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _ :=False, Transpose:=False End With ElseIf CL.Value = a Then CL.Select CL.Interior.ColorIndex = 8 With ActiveCell Cells(ActiveCell.Row, 1).Select Selection.Copy Range("I24").Select Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _ :=False, Transpose:=False http://ennius.interfree.it/
Pagina 279
MANUALE VBA
X EXCEL
End With ElseIf CL.Value = j Then CL.Select CL.Interior.ColorIndex = 3 With ActiveCell Cells(ActiveCell.Row, 1).Select Selection.Copy Range("J24").Select Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _ :=False, Transpose:=False End With End If Next Application.CutCopyMode = False 'fine ciclo ricerche Else 'questa è l'istruzione per togliere i colori e pulire le celle riga 24, e rinomina la Caption del commandbutton per 'reinizzializzare una nuova ricerca CommandButton1.Caption = "Mostra" Range("B3:H19").Interior.ColorIndex = xlNone Range("B24:J24").ClearContents End If End Sub
http://ennius.interfree.it/
Pagina 280
MANUALE VBA
X EXCEL
Ricerca di dati in un elenco di un foglio di lavoro Usare il metodo Find Per dimostrare che sfruttando gli esempi contenuti nella guida in linea dell'Editor di Visual Basic, è possibile, con piccole modifiche, adattare gli esempi alle nostre necessità, presento alcune soluzioni possibili sfruttando l'esempio collegato al metodo Find. Questo sotto è quanto riportato nella "Guida il linea", ottenibile anche premendo il tasto F1, quando ci si trova nell'editor di visual basic: (N.B. la seguente routine, presa dalla guida contiene un piccolo bug. Vedi la segnalazione a pagina 5 de "le vs domande", paragrafo "Errori sulla Guida") Esempio dalla guida in linea (cito:) Questo esempio trova tutte le celle nell'intervallo A1:A500 sul foglio di lavoro 1 contenenti il valore 2 e modifica il valore in 5. With Worksheets(1).Range("a1:a500") Set c = .Find(2, lookin:=xlValues) If Not c Is Nothing Then firstAddress = c.Address Do c.Value = 5 Set c = .FindNext(c) Loop While Not c Is Nothing And c.Address firstAddress End If End With E queste sono alcune possibili varianti, la prima ipotizza l'utilizzo di una cella per l'introduzione del dato da cercare (vettore). La cella d'esempio ( Cells(1, 2).Value ) identifica la cella B1, e avremmo potuto usare indifferentemente la notazione Range("B1").Value, sarebbe stato lo stesso, forse più semplice da usare e da riconoscere per colui che compìla l'istruzione. A questa cella viene associata la variabile stringa X in modo che, qualunque testo si scriva in B1, venga identificato con la X. Se il valore da cercare, anzichè testo, fosse un numero, sarebbe necessario definire la X non come String ma come Variant (Variant accetta sia testo che numero, solo occupa un pò più di spazio in memoria). Sempre per fare l'esempio, diremo che i dati si trovano sul Foglio1, nel Range A2:H100. Ovviamente ognuno metterà i riferimenti alla zona dove vorrà eseguire la ricerca, che potrà essere più ampia, o anche una sola colonna da una determinata riga ad un altra. Una particolarità legata al tipo di ricerca, è questa: con la seguente istruzione, verranno cercati valori esatti al valore da cercare : Set c = .Find(X, LookIn:=xlValues, LookAt:=xlWhole), con la seguente istruzione verranno invece trovati valori che sono una parte del valore cercato: Set c = .Find(X, LookIn:=xlValues), se, per esempio, in B1 digitiamo. "pape", verranno trovati anche "papero", op. "paperino", ecc.ecc. La seguente routine cerca quindi il valore dato, e se lo trova, seleziona la cella, altrimenti avvisa con un messaggio "Nome non Trovato". Sub Cerca1() With Worksheets(1).Range("A2:H100") Dim X As String X = Cells(1, 2).Value Set c = .Find(X, LookIn:=xlValues, LookAt:=xlWhole) If Not c Is Nothing Then firstAddress = c.Address Do c.Cells.Select Set c = .FindNext(c) Loop While Not c Is Nothing And c.Address firstAddress Else MsgBox "Nome non Trovato" End If End With End Sub http://ennius.interfree.it/
Pagina 281
MANUALE VBA
X EXCEL
Questo che segue è un'altro esempio, in cui l'introduzione del dato da cercare passa attraverso una InputBox, e quindi viene usata la imputbox come vettore per la ricerca: Sub Cerca2() With Worksheets(1).Range("A2:H100") Dim Message, Title, MyValue Message = "Inserisci il nominativo da cercare :" Title = "Ricerca Dati" ' Imposta il titolo. ' Visualizza il messaggio, il titolo MyValue = InputBox(Message, Title) Dim X As String X = MyValue 'viene assegnato alla X il contenuto della Inputbox Set c = .Find(X, LookIn:=xlValues, LookAt:=xlWhole) If Not c Is Nothing Then firstAddress = c.Address Do c.Cells.Select Set c = .FindNext(c) Loop While Not c Is Nothing And c.Address firstAddress Else MsgBox "Nome non Trovato" End If End With End Sub Un altro modo per eseguire una ricerca, e senza il metodo Find, è la seguente, però senza messaggio di avviso, visto che se trova il dato lo seleziona, se non lo trova vuol dire che il dato non è presente Sub Cerca3() For Each c In Worksheets("Foglio1").Range("A2:H100") If c.Value = Range("B1").Value Then c.Select End If Next c End Sub Volendo, anche per quest'ultima routine, è possibile sfruttare l'assegnazione del vettore ad una variabile, oppure usare una InputBox; sarà sufficiente sostituire If c.Value = Range("B1").Value Then con If c.Value = X Then... oppure If c.Value = MyValue Then... NUOVO: Un problema di tutti i cicli di ricerca (ovviamente dipende dalla procedura scelta), è che quando in un elenco ci sono più dati uguali, il ciclo, o trova il primo (come negli esempi sopra), e li si ferma, e se lo fai ripartire inizia da capo e si riferma al primo, o li trova tutti ma si ferma all'ultimo. Questo secondo caso avviene perchè nel ciclo in genere si usa l'istruzione "se lo trovi, selezionalo", ed il ciclo lo fa, ma dopo averlo selezionato prosegue e se trova altri valori uguali, li seleziona fintantochè non trova l'ultimo valore e si ferma. Allora ho pensato di inserire un'istruzione che blocchi il ciclo se trova il valore cercato, ma che lo possa riavviare dal punto in cui si è fermato; la soluzione più semplice e quella che riporto sotto, dove nel caso venga trovato il valore, faccio apparire una msgbox Si/No, che interrompe il ciclo, con una domanda: " Ti vuoi fermare?" Se premi Si, esci dal ciclo, se premi No, prosegue nel ciclo dal punto in cui si era fermato, e per ogni valore uguale si riferma e ripone la domanda. Semplice no? Sub cerca() Dim CL As Object For Each CL In Range("A1:A2000") 'colonna in cui esegue la ricerca http://ennius.interfree.it/
Pagina 282
MANUALE VBA
X EXCEL
Dim X X = Range("D1").Value 'cella che porta il valore da cercare If CL = X Then 'se la cella (CL) è ugule a X CL.Select 'faccio selezionare (fermo il ciclo) questa cella Y = Mid(CL.Address, 2, 1) + Mid(CL.Address, 4, 5) 'con Y prendo il 'riferimento alla cella che poi uso nella domanda Dim irisposta As Integer 'Imposto la msgbox e relativa domanda irisposta = MsgBox("Trovato in " & Y & " Vuoi fermarti ?", vbYesNo) If irisposta = vbYes Then 'se rispondo Si allora Exit For 'esco dal ciclo End If End If Next CL 'altrimenti proseguo al successivo End Sub Come per le altre routine, anche in questa la variabile X può venir assegnata attraverso una InputBox. Buon lavoro.
http://ennius.interfree.it/
Pagina 283
MANUALE VBA
X EXCEL
Ricerca di un dato in un elenco o tabella. (21/05/03) Ricercare dati, valori o altre informazioni all'interno dei fogli di lavoro, sembra sia uno tra gli sport preferiti dagli Excelnauti. Nel paragrafo precedente abbiamo visto delle procedure adatte a questo scopo, ma il buon Excel col suo vba, ha il gran pregio di consentirci di seguire percorsi diversi per raggiungere lo stesso scopo. Questa pagina si occuperà di mostrare alcune di queste altre procedure, esemplificando anche gli obiettivi che intendiamo ottenere come risultato di una ricerca.. Per prima cosa, in una ricerca, bisogna indicare in quale zona o area eseguirla. Vediamo alcuni sistemi di identificazione di un area: Usare i riferimenti precisi dell'area : Set zona = ActiveSheet.Range("A1:A500") Usare il riferimento a tutta una colonna: Set zona = ActiveSheet.Range("A:A") Usare il riferimento alla cella iniziale della Colonna (A nell'esempio) e alla cella finale (con End) dell'area : Set zona = Range(Cells(1, 1), Cells.End(xlDown)) Usare il riferimento a tutta l'area che contiene dati sfruttando la proprietà UsedRange del foglio di lavoro : Set zona = ActiveSheet.UsedRange Stabilita l'area, il passo successivo sarà quello di definire cosa vogliamo cercare. In questo caso, esemplificare il "cosa cercare" diventa ardua impresa visto la molteplicità nelle necessità che ogni utente potrà avere. Ci limiteremo ad alcuni esempi che comunque serviranno a capire. Imposteremo tre classiche ricerche: ricercare una data ricercare un valore numerico ricercare del testo Precisiamo intanto che le procedure usate, sono tutte impostate in modo che nel caso venga trovata la chiave di ricerca, si venga avvisati con un messaggio riportante i riferimenti alla cella, e la routine proseguirà poi segnalandoci ogni indirizzo di cella che corrisponderà alla chiave cercata. In questo modo, su lunghi elenchi, potremo annotare tutti i riferimenti per poi visualizzarli , oppure aggiungere una semplice istruzione perchè per ogni chiave trovata, venga selezionata la relativa cella. Per ognuna delle ricerche su esposte, puntualiziamo spiegazioni e facciamo qualche esempio, vediamo una prima riga di istruzione: ricerca su date: l'istruzione significa: se il valore cercato (rng.Value) è una data (IsDate) e la prima lettera a sinistra (Left(rng.Text, 1)) del valore trovato è uguale a # (cancelletto), allora... (segue istruzione di cosa fare). Questa istruzione è utile quando si imposta, per necessità visive, una larghezza di colonna inferiore alla lunghezza del dato presente in una cella; in questo caso Excel, avrete notato, non cancella i dati, ma mostra una serie di #, tanti quante sono le lettere che compongono il valore. If IsDate(rng.Value) And Left(rng.Text, 1) = "#" Then ancora su date, ma nel caso in cui si voglio trovare tutte le date di una decade (da 01 a 09 e useremo lo zero come chiave di ricerca) (da 10 a 19 e useremo 1 come chiave di ricerca) (da 20 a 29 e useremo 2 come chiave di ricerca) oppure 3 per i fine mese. If IsDate(rng.Value) And Left(rng.Text, 1) = 1 Then 'opp.= 0, opp. = 2, opp. = 3 (usando numeri come chiave di ricerca, NON vanno messi tra doppi apici) ricerca su numeri: anche in questo caso l'istruzione si basa sulla ricerca di un valore (IsNumeric), numerico in questo caso, e come chiave di ricerca sfrutteremo (Left(rng.Text, 1)) che vuol dire: il primo valore a sinistra del numero, per cui potremo trovare per esempio, tutti i valori che cominciano con 5. Ovviamente la ricerca dipende dal numero che useremo come chiave di ricerca. Se anzichè cercare solo i valori che iniziano per un determinato numero, volessimo cercare tutti i valori che iniziano per una coppia di numeri (per esempio: 25..) dovremmo variare il secondo argomento della funzione Left, sostituendolo con 2 (es: Left(rng.Text, 2)): If IsNumeric(rng.Value) And Left(rng.Text, 1) = 5 Then 'solo il primo a sinistra = 5 If IsNumeric(rng.Value) And Left(rng.Text, 2) = 25 Then 'con i primi due a sinistra = 25 Come vedete queste istruzioni, e le successive, oltre a poterle variare nel modo di stabilire una chiave di ricerca, sono facili da capire. Se poi qualcuno volesse rendere la chiave di ricerca come variabile (negli esempi i valori sono costanti, cioè inseriti nel codice), può usare una cella da destinare all'inserimento della chiave di ricerca, oppure usare una InputBox, e poi usare questi riferimenti nell'uguaglianza (uguale a), esempio: la cella E1 dove scrivere ciò che vorremo cercare, e l'istruzione diventa: If IsNumeric(rng.Value) And Left(rng.Text, 2) = Range("E1").Value Then oppure con una InputBox : dimmi = InputBox("Scrivi la coppia di numeri") If dimmi = "" Then Exit Sub 'per uscire se non si scrive niente nella inputbox http://ennius.interfree.it/
Pagina 284
MANUALE VBA
X EXCEL
If IsNumeric(rng.Value) And Left(rng.Text, 2) = Val(dimmi) Then Ultimo esempio: ricerca su testo: in queste istruzioni, simili alle altre come impostazione, la chiave di ricerca andrà posizionata tra doppi apici, e potremo eseguire ricerche con lettera alfabetica (es: = "a" opp. = "b", opp = "t") oppure per insiemi di lettere (es: = "ca" opp: = "bar", ecc.ecc) ricordandosi di modificare il numero dei caratteri che la funzione Left dovrà estrarre (Left(rng.Text, 1) opp Left(rng.Text, 2) opp Left(rng.Text, 3) ecc..Varieremo anche la prima condizione da verificare, dicendo: se il valore da cercare NON è numerico e.... usando questa istruzione: IsNumeric(rng.Value) = False, in questo modo: If IsNumeric(rng.Value) = False And Left(rng.Text, 1) = "a" Then Sarà possibile anche in questo caso rendere variabile la chiave di ricerca usando una cella oppure una InputBox, in questo caso le modifiche sono uguali a quelle già scritte sopra. Unica precisazione: poichè le ricerche su testo sono "CaseSensitive" (sensibili a maiuscole/minuscole, la "a" è diversa dalla "A") useremo questa semplice istruzione da inserire nella sezione "Generale - Dichiarazioni" del modulo che ospiterà la nostra macro: Option Compare Text Questo ci consentirà di trovare il testo comunque si sia scritta la nostra chiave di ricerca. Ed ora vediamo come comporre le routine: questa sotto ricercherà un dato (scegliamo il metodo UsedRange come identificazione area, e una inputbox per reperire il dato da cercare) e basiamo la ricerca sul testo, prima lettera del valore che sarà nelle celle. Questa routine avviserà in sequenza ogni riferimento ad ogni cella che corrisponderà alla chiave, ma non si fermerà, selezionandola. La successiva routine è simile a questa, ma si fermerà, selezionandola, ad ogni cella trovata: Sub popop() 'nome macro 'dichiarazione della variabile "rng" di tipo Range Dim rng As Range 'assegnazione alla variabile "dimmi" del valore reperito con la inputbox dimmi = InputBox("Scrivi l'iniziale da cercare") If dimmi = "" Then Exit Sub 'per uscire se non si scrive niente nella inputbox 'per ogni rng (Range o cella) nella zona del foglio di lavoro che contiene dati For Each rng In ActiveSheet.UsedRange 'se il valore della cella (rng) NON è un numero e la prima lettera è uguale a "dimmi" If IsNumeric(rng.Value) = False And Left(rng.Text, 1) = dimmi Then 'si da il messaggio del rif. alla cella e del nome ivi contenuto MsgBox "La cella che cerchi è " & rng.Address & " col valore: " & rng.Value End If 'si continua il ciclo fino alla fine dell' UsedRange Next rng End Sub e questa con la selezione della cella ad ogni chiave trovata: Sub popup() Dim rng As Range dimmi = InputBox("Scrivi l'iniziale da cercare") If dimmi = "" Then Exit Sub For Each rng In ActiveSheet.UsedRange If IsNumeric(rng.Value) = False And Left(rng.Text, 1) = dimmi Then rng.Select 'questa seleziona la cella e la evidenzia MsgBox "La cella che cerchi è " & rng.Address & " col valore: " & rng.Value End If Next rng End Sub Se vorremo creare una routine che ci consenta di fermarci ed uscire quando avremo trovato il valore che ci interessa, dovremo inserire un istruzione che ci ponga la domanda se ci vogliamo fermare. Se risponderemo SI usciremo dal ciclo, altrimenti il ciclo continuerà. Questa la routine (simile all'ultima presente nel paragrafo precedente : "Ricerca di un dato") Sub pipup() Dim rng As Range dimmi = InputBox("Scrivi l'iniziale da cercare") If dimmi = "" Then Exit Sub For Each rng In ActiveSheet.UsedRange http://ennius.interfree.it/
Pagina 285
MANUALE VBA
X EXCEL
If IsNumeric(rng.Value) = False And Left(rng.Text, 1) = dimmi Then rng.Select 'questa seleziona la cella e la evidenzia Dim irisp As Integer 'Imposto la msgbox e relativa domanda con SI/NO irisp = MsgBox("Cella " & rng.Address & " a nome " & rng.Value & " Vuoi fermarti ?", vbYesNo) ' irisp è tutta una riga If irisp = vbYes Then 'se rispondo Si allora Exit For 'esco dal ciclo e quindi dalla routine End If End If Next rng End Sub Buon lavoro.
http://ennius.interfree.it/
Pagina 286
MANUALE VBA
X EXCEL
Utilizzo del Codice per ripristinare un elenco di formule. Applicazione del Metodo Autofill Uno dei motivi di maggiore rabbia, è quando inavvertitamente cancelliamo delle celle con dei valori, celle che però contenevano anche delle formule. E tanto maggiore è l'incavolatura quando perdiamo tutto un bel range di celle, predisposte con fatica. E' possibile, tramite codice, predisporre una macro che ci rimetta le cose a posto. Sarà sufficiente associare un pulsante a questa macro, e le nostre formule ritorneranno in tutte le celle da cui sono state cancellate ( o meglio, in tutto il range di celle indicate nel codice). Per capire, facciamo un esempio, la classica colonna dove riportiamo il saldo progressivo di un conto dare/avere. avremo quindi nella colonna C il dare, nella D l'avere, ed in E il saldo, avremo una cella in E di riporto a saldo, e nelle successive righe della colonna E le rispettive formule, ognuna con i propri riferimenti. vediamo sotto un esempio:
1 2 3 4 5 6 7 8 9
B C Operazione Dare riporto a saldo
D Avere
E Saldo =SE((C3+D3)0;E2-C3+D3;"") =SE((C4+D4)0;E3-C4+D4;"") =SE((C5+D5)0;E4-C5+D5;"") =SE((C6+D6)0;E5-C6+D6;"") =SE((C7+D7)0;E6-C7+D7;"") =SE((C38+D8)0;E7-C8+D8;"")
Bene, prepareremo la nostra macro utilizzando per la formula lo stile R1C1, (che ricordo, Reimmette le formule nelle celle interessate come formule residenti) ed il metodo AutoFill per riempire, in un colpo solo, tutte le celle previste nel range di destinazione ( Range("E3:E8") ). Ovviamente decideremo noi quanto lungo sarà il nostro elenco, e sostituiremo i riferimenti alle celle. Basterà a questo punto premere il pulsante associato alla macro, e nelle nostre celle riappariranno le formule. Ci saremo in questo modo protetti da eventuali errori, in barba alla distrazione! Questa è la formula dell'esempio: Sub ripristina() Range("E3").Select ActiveCell.FormulaR1C1 = "=IF((RC[-2]+RC[-1])0,R[-1]C-RC[-2]+RC[-1],"""")" Selection.AutoFill Destination:=Range("E3:E8"), Type:=xlFillDefault End Sub La formula dice: selezionami la cella E3, e in questa cella mi inserisci la formula: SE la cella che si trova due colonne a sinistra di questa RC[-2] e la cella che si trova una colonna a sinistra di questa RC[-1] , sono diverse da zero, allora mi prendi la cella che stà una cella sopra a questa R[-1] (vedete che manca il riferimento a C, e questo vuol dire: stessa colonna) mi ci sottrai (abbrevio)RC[-2] o/e mi sommi RC[-1] , altrimenti mi lasci la cella vuota """" (nota: mentre in una formula sul foglio di lavoro lo spazio vuoto si indica con due doppi apici, nel codice ne occorrono quattro, per avere lo stesso significato) a questo punto, entra in gioco l'istruzione della riga successiva, dove troviamo AutoFill. il significato è questo: con ciò che hai messo nella cella selezionata, mi ci RIEMPI anche tutte le celle che vanno da E3 a E8. Poichè questa operazione equivale al "trascinamento" sul foglio di lavoro, il bravo Excel, provvede anche all'aggiornamento dei riferimenti nelle formule, in automatico. Ricordo che un buon metodo per imparare e modificare il codice, è quello di usare il "Registratore di macro" (presente in questa sezione). Potrete vedere, compiendo voi le azioni necessarie, come viene compilato il codice, e quindi, piano piano, assimilare le nozioni. Buon lavoro.
http://ennius.interfree.it/
Pagina 287
MANUALE VBA
X EXCEL
Una piccola raccolta di istruzioni di varia (f)utilità. (sarà aggiornata periodicamente; gli aggiornamenti saranno posti alla fine della pagina.) Limitare l'area da visualizzare a video. (disabilita le barre di scorrimento) Worksheets(1).ScrollArea = "A1:L22" '(area che rimane fissa a video) 'mentre per riapplicare lo scrolling Worksheets(1).ScrollArea = "" Nascondere le righe vuote. (in una tabella elenco, scelta una Colonna (la A per esempio) quando si voglia riassumere i dati nascondendo ev. righe vuote) Sub NascondeRiga() ScopriRiga 'viene prima chiamata questa routine per scoprire ev. righe nascoste Dim CL As Object For Each CL In Range("A1:A50") If CL.Value = "" Then CL.EntireRow.Hidden = True Next CL End Sub e questa per scoprire tutte le righe nascoste: Sub ScopriRiga() Cells.EntireRow.Hidden = False End Sub Cancellare righe vuote (in una tabella o elenco, scelta una Colonna (la A per esempio) quando si voglia cancellare le righe corrispondenti alle celle in A vuote ) Sub EliminaRighe() For Each cella In Range("A1:A200") If cella.Value = "" Then cella.EntireRow.Delete End If Next cella End Sub Ridurre a icona Excel. (quando si voglia ridurre Excel a icona per lasciare lo schermo ad una userform (in questo caso si sta sfruttando l'apertura della cartella)) Private Sub Workbook_Open() Application.WindowState = xlMinimized End Sub e per ripristinare, all'uscita da Excel Application.WindowState = xlMaximized Stampare un foglio o una selezione di celle. per un foglio ActiveWindow.SelectedSheets.PrintOut Copies:=1, Collate:=True per una selezione Selection.PrintOut Copies:=1, Collate:=True Disabilitare/Abilitare barre dei menù. (attenzione: poichè queste modifiche resterebbero su Excel quando si chiude il file, e resterebbero anche alla riapertura altri file dove c'è il bisogno che i menù si vedano, DOVRETE ripristinare i menù usando le istruzioni al rovescio, nell'evento BeforeClose del workbook) 'questa toglie la barra dei menù Application.CommandBars.ActiveMenuBar.Enabled = False 'questa toglie la barra menù Standard Application.CommandBars("Standard").Enabled = False 'oppure questa disabilita l'icona "nuovo" dalla barra menù "Standard" Application.CommandBars("Standard").Controls.Item(1).Enabled = False per riattivarle: http://ennius.interfree.it/
Pagina 288
MANUALE VBA
X EXCEL
'questa rimette la barra dei menù Application.CommandBars.ActiveMenuBar.Enabled = True 'questa rimette la barra menù Standard Application.CommandBars("Standard").Enabled = True 'oppure questa abilita l'icona "nuovo" dalla barra menù "Standard" Application.CommandBars("Standard").Controls.Item(1).Enabled = True Proteggere la visualizzazione del codice vba. Spostarsi nell'Editor di Visualbasic, selezionare dal menù Strumenti/Proprietà di VBAProject , e nella finestra che si apre scegliere "Protezione", mettere un segno di spunta a "proteggi ecc. ecc" e inserita la password, salvare la cartella, uscire e quando si rientra, aprire il progetto cartella: verrà richiesta la password, altrimenti il codice non sarà visibile. (Questo anche se in apertura foglio, avremo disabilitato le macro). Automatizzare l'apertura di una UserForm. All'apertura della cartella di lavoro o di un foglio all'apertura della cartella (WorkBook) Private Sub Workbook_Open() UserForm1.Show End Sub all'apertura di un Foglio (Worksheet) Private Sub Worksheet_Activate() UserForm1.Show End Sub Apertura di un file in sola lettura. Indicare il percorso che mira al file che interessa. L'apertura in sola lettura non consente modifiche al file così aperto. Workbooks.Open Filename:= "C:\Documenti\NomeFile.xls", ReadOnly:=True Eliminare le Interruzioni di pagina. Per eliminare in un colpo solo tutte le interruzioni di pagina presenti su un Foglio (segnalato da Michele GiGi - e-mail : [email protected]). Cells.Select ActiveSheet.ResetAllPageBreaks Misurare la memoria usata da Excel. Se volete sapere quanta memoria sta utilizzando la vostra applicazione, provate questa: Sub MisuraMe() 'la lettura viene eseguita in byte e quindi la convertiamo in kb Y = Application.MemoryUsed X = Val(Y / 1024) MsgBox "Microsoft Excel sta usando " & X & " Kb di memoria." End Sub Selezionare un Foglio di lavoro scrivendo il nome del foglio in una TextBox su una UserForm, e usando l'evento Click di un CommandButton. Private Sub CommandButton1_Click() X = TextBox1.Text Worksheets("" & X & "").Select End Sub Disabilitare il tasto destro del mouse. In questo modo si disabilita anche la comparsa del menù contestuale che appare quando si clicca destro sulle celle del foglio di lavoro: per un foglio, con messaggio di avviso: Private Sub Worksheet_BeforeRightClick(ByVal Target As Range, Cancel As Boolean) MsgBox "Il tasto destro è stato disabilitato." Cancel = True End Sub per tutti i fogli, senza messaggio: Private Sub Workbook_SheetBeforeRightClick(ByVal Sh As Object, ByVal Target As Range, Cancel As Boolean) 'è tutta una riga Cancel = True End Sub http://ennius.interfree.it/
Pagina 289
MANUALE VBA
X EXCEL
Nascondere Righe e/o Colonne. indicare il numero di riga (es. 10), e la lettera per la colonna, per ripristinare, stesse istruzioni con False al posto di True: ActiveSheet.Rows(10).Hidden = True oppure per più righe, indicare numero da : a numero, tra doppi apici: ActiveSheet.Rows("8:12").Hidden = False per le colonne: Worksheets("Foglio1").Columns("C").Hidden = True stesso discorso per più colonne: Worksheets("Foglio1").Columns("C:F").Hidden = True Reperire il nome di un file che si apre. Quando si vuole memorizzare il nome di un file che si apre, magari facendolo scrivere in una cella, (esempio in A1), creare una macro con questa istruzione: (segnalazione di Luca Paolini). Si aprirà la finestra "Apri File" e verrà memorizzato il file che si selezionerà : NewFile = Application.GetOpenFilename Range("A1").Value = NewFile Salvare in automatico la cartella su cui si è lavorato, alla chiusura della cartella stessa o di Excel. Si sfrutta l'evento Workbook_BeforeClose, e si evita la richiesta di salvataggio che Excel ci propone, con questa semplice istruzione: Private Sub Workbook_BeforeClose(Cancel As Boolean) ThisWorkbook.Save 'questa riga è l'istruzione End Sub Inserire/Togliere BloccaRiquadri. L'opzione Bloccariquadri serve per tenere bloccate una parte di celle del foglio di lavoro mentre le rimanenti celle possono scorrere liberamente agendo sulle barre di scorrimento. Si possono bloccare solo le righe (selezionando prima l'intestazione di riga), oppure solo le colonne (selezionando prima l'intestazione di colonna), oppure righe e colonne insieme se si seleziona una cella e si applica bloccariquadri. il blocco avverrà sempre a partire dalla riga sopra a quella selezionata, o la colonna a sinistra rispetto a quella selezionata, o le righe sopra e le colonne a sinistra rispetto alla cella selezionata. Esempio. selezioniamo la cella C5 (terza colonna e quinta riga): saranno bloccate le colonne A e B e le righe dalla quarta alla prima. Queste le istruzioni per applicare o togliere bloccariquadri (FreezePanes) 'Bloccare le righe (volendo bloccare le prime due righe): ActiveSheet.Rows(3).Select ActiveWindow.FreezePanes = True oppure: Rows("3:3").Select ActiveWindow.FreezePanes = True 'Bloccare le colonne (volendo bloccare le prime 3 colonne A-B-C) ActiveSheet.Columns("D").Select ActiveWindow.FreezePanes = True oppure: Columns("D:D").Select ActiveWindow.FreezePanes = True 'Sbloccare (togliere) ogni bloccariquadri: ActiveWindow.FreezePanes = False Passare da Stile riferimento A1 a Stile R1C1 - Stile A1 le Colonne sono identificate con Lettere alfabetiche. Stile R1C1 le Colonne sono identificate con Numeri. La proprietà ReferenceStyle si applica solo all'oggetto Application, ossia a Excel e quindi a tutta la cartella di lavoro. Attenzione: ricordarsi che una volta applicato uno stile, lo stesso rimane su Excel anche per successive aperture, ripristinare quindi lo stile originario usando l'evento Workbook_BeforeClose. per passare a Stile R1C1: With Application .ReferenceStyle = xlR1C1 End With per ritornare a Stile A1: With Application http://ennius.interfree.it/
Pagina 290
MANUALE VBA
X EXCEL
.ReferenceStyle = xlA1 End With Proteggere un intervallo (celle o colonne o righe) da scrittura. Si può sfruttare l'evento SelectionChange del Worksheet e con Intersect limitare l'azione solo ad una determinata area. L'esempio sotto blocca tutte le celle della colonna B. Ovviamente dovremo lasciare l'opzione "bloccata" a tutte le celle. Private Sub Worksheet_SelectionChange(ByVal Target As Range) If Intersect(Target, Columns(2)) Is Nothing Then ActiveSheet.Unprotect 'sproteggiamo il foglio se la cella selezionata non è nella B Exit Sub Else ActiveSheet.Protect 'e proteggiamo il foglio attivo End If End Sub Eliminare la richiesta di Aggiornamento Collegamenti. - Quando una cartella di lavoro contiene collegamenti ad altri fogli o celle di cartelle chiuse, viene posta da Excel una richiesta di aggiornare o meno i collegamenti. E' possibile eliminare la richiesta sfruttando l'evento Workbook_Open ed inserendo la seguente istruzione: Private Sub Workbook_Open() Application.AskToUpdateLinks = False 'istruzione End Sub Richiamare da Excel la Calcolatrice di sistema. - A volte può tornare utile avere a disposizione su un foglio di lavoro, una calcolatrice; questa una routine possibile (per Win XP) che sfrutta la Shell di sistema: Sub ChiamaCalcolatrice() Dim Pippo Pippo = Shell("C:\WINDOWS\System32\CALC.EXE", 1) End Sub Conoscere la versione del Sistema Operativo. - può necessitare di impostare istruzioni vba che cambierebbero se cambia il S.O. (ad esempio la chiamata alla Shell di sistema cambia tra Windows98 e WindowsXP). Per rintracciare via codice la versione che identifica il SO e creare quindi condizioni diverse (If...Then...Else), si può usare questa istruzione che restituisce un messaggio con la versione del SO del computer che state usando: Sub DimmiSO() MsgBox Application.OperatingSystem End Sub -------------------------------------------------------per WindowsXP si otterrebbe questa informazione: "Windows (32-bit) NT 5.01" sarà possibile quindi impostare istruzioni per quel sistema lasciando ad Else il compito di altre istruzioni; esempio: If Application.OperatingSystem = "Windows (32-bit) NT 5.01" Then ..istruzioni da eseguire in questo caso Else ..istruzioni da eseguire in tutti gli altri casi End If Colorare una cella SE conterrà un certo valore - Potremo sfruttare l'evento Change del Worksheet che si verifica quando scriviamo in una cella, modificandone il contenuto (sia che la cella sia vuota o anche cambiando un valore già residente). Sfrutteremo l'argomento Target dell'evento, che identifica la cella che ha subito il cambiamento, ponendo una condizione con If..Then Private Sub Worksheet_Change(ByVal Target As Range) If Target = 25 Then 'se il valore immesso nella cella è uguale (esempio: 25), allora Target.Interior.ColorIndex = 3 'si colora la cella di rosso End If End Sub http://ennius.interfree.it/
Pagina 291
MANUALE VBA
http://ennius.interfree.it/
X EXCEL
Pagina 292
MANUALE VBA
X EXCEL
Progetto Rubrica Indirizzi. Utilizzo: gestione di un database realizzato con Excel ad uso rubrica per indirizzi. Viene presentata una semplice rubrica per indirizzi, utilizzando alcune delle procedure e degli strumenti visti nei precedenti paragrafi di questa sezione. Impiegheremo anche una UserForm, finora non ancora analizzata, è la useremo per introdurre i dati nel nostro database. Cureremo anche la veste grafica per ottenere un effetto gradevole. Tutto il progetto si affida a due soli fogli: il primo è il foglio di apertura, dove consulteremo l'archivio attraverso una Casella combinata (Combobox) che riporterà tutti i nomi presenti nel database, riportati in ordine alfabetico: cliccando sul nome desiderato, lo selezioneremo e, nelle celle destinate ai dati correlati al nome selezionato, appariranno l'indirizzo, la città, il cap, la provincia, i telefoni, il fax. Queste celle sfruttano la funzione =CERCA.VERT. Nella foto sotto vediamo come si presenta la pagina; la freccia indica la Casella combinata; in alto vediamo due Commandbutton: "Inserisci Nuovi Dati" che aprirà la UserForm con le caselle per l'inserimento di nuovi dati, ed il pulsante "Modifica Dati" che aprirà il secondo foglio (il database) sul quale potremo modificare dati esistenti. Il terzo Commandbutton, sulla destra in basso, salva ed esce da excel, chiudendolo. Se scaricherete il file, vedrete il codice associato ad ogni pulsante. Ricordo che per lavorare sul codice di oggetti presi dalla "Casella degli Strumenti" o "Strumenti di controllo", è necessario entrare in "Modalità progettazione" cliccando sull'icona della "Squadra" presente sulla finestra degli strumenti di controllo.
Sotto vediamo la UserForm richiamata dal pulsante "Inserisci nuovi dati". E' qui che dovremo inserire, riempendo le relative caselle, i dati riguardanti il nuovo nominativo. Una volta completati tutti i campi, si potrà premere il pulsante "Registra", il quale, sempre via codice, copierà il contenuto dei campi sul secondo foglio, quello che contiene l'archivio dati (il database). E' presente un istruzione che verificherà che almeno il cognome e nome siano scritti, altrimenti si rifiuterà di registrare i dati. Volendo annullare e uscire, sarà sufficiente premere il pulsante "Esci".
La foto sotto è quella del secondo foglio, che contiene l'archivio dei dati; è presente un'istruzione che all'inserimento di un nuovo nominativo, automaticamente verrà posizionato nel giusto ordine alfabetico. Il pulsante "Indietro" consente di ritornare al foglio consultazione/inserimento. (primo foglio) http://ennius.interfree.it/
Pagina 293
MANUALE VBA
X EXCEL
Chi ha un pò di dimestichezza con il Vba, potrà leggere le istruzioni direttamente entrando nell'editor di visualbasic. File consultabile e scaricabile: Rubricas.zip 35 Kb
http://ennius.interfree.it/
Pagina 294
MANUALE VBA
X EXCEL
Salvare (meglio: copiare) un file sul floppy. (31/05/03) Un esigenza che spesso sentiamo è quella di salvare il lavoro fatto oltre che sul nostro hard-disk, anche facendone una copia su un floppy. Pratica seguita anche quando si voglia rendere disponibile una copia di un file su altri computer, magari posti nello stesso ufficio ma non collegati in rete, oppure per tutti coloro che a fine lavoro vogliono crearsi una copia di riserva da conservare su un floppy. In questo esercizio prenderemo in esame la copia della cartella aperta su cui stiamo lavorando, quindi un file .xls. La procedura di base, quella che serve per copiare il file è semplice: si basa sulla creazione di una variabile sfruttando il metodo CreateObject("Scripting.FileSystemObject"), ed assegnando poi alla variabile il percorso dove si trova il file da copiare, e la destinazione dove il file dovrà essere copiato. (Ricordo che per "percorso" si intende, in sequenza : 1) l'unità dove risiede il file - 2) la cartella ed ev. sottocartella dove risiede il file - 3) il nome del file con l'estensione). L'istruzione sarebbe molto semplice, vediamola (i nomi dei percorsi e dei file sono di esempio): Set fs = CreateObject("Scripting.FileSystemObject") fs.CopyFile "C:\ExcelWeb\Macedonia.xls", "A:\Macedonia.xls" Dove: con Set fs settiamo una variabile CreateObject ("Scripting.FileSystemObject"), mentre con il metodo CopyFile (assegnato a fs) indichiamo (tra doppi apici) il percorso del file da copiare, virgola, seguita dal percorso per la destinazione, cioè dove vogliamo che il file venga copiato (anche questo percorso messo tra doppi apici). Bastano solo queste due righe per eseguire la copia. Dovremo però munire la nostra routine di controlli per evitare che si generino errori, i più ricorrenti possono essere: Il file da copiare non viene trovato, o perchè non ancora creato, o perchè si trova in un percorso diverso da quello impostato nelle istruzioni, o addirittura perchè il nome del file è errato. Il floppy non è stato inserito nel lettore di floppy (l'unità A:\). Il file è più grande (in byte) della dimensione che un floppydisk può contenere (1.437 kb) Inoltre sarebbe opportuno decidere di volta in volta il nome con il quale vogliamo sia nominata la copia del file. Questa necessità si rivela quando, salvando periodicamente lo stesso file di origine, lo dovremo identificare nel tempo. Sarebbe comodo chiamare la copia (nell'esempio : Macedonia) Macedonia1, Macedonia2, ecc. oppure usare una data (senza le barre ( / ) : Windows NON consente di usare barre ( / ) per definire nomi di file), come Macedonia310503, oppure Macedonia010603, ecc. ecc. Per fare ciò dovremo rendere variabile la componente "nome file" inserita nel percorso di destinazione, e dovendo per forza assegnare un nome, useremo una cella del foglio di lavoro dove scriveremo detto nome, e useremo poi il riferimento a questa cella (magari assegnando la cella a una variabile) e richiamare questa nel percorso di destinazione. A questo punto dovremo impiegare un altro controllo per verificare che nella cella scelta esista un nome. Per il controllo Errori useremo queste impostazioni: On Error Resume Next - posto immediatamente dopo il nome della routine. Resume - posto immediatamente prima della fine routine (End Sub). Err.Number - usato per intercettare la condizione : se si verifica l'errore numero.... Vediamo quindi l'intera routine, in verde i soliti commenti: Sub SalvasuFloppy() On Error Resume Next 'sotto: usiamo la cella C1 per scrivere il nome con il quale verrà copiato il file, e quindi 'controlliamo con : se il Range("C1") è vuoto, allora avvisiamo ed usciamo dalla routine If Range("C1") = "" Then MsgBox "DEVI INSERIRE UN NOME PER LA COPIA DEL FILE" Range("C1").Select Exit Sub 'si esce dalla routine End If 'usiamo la variabile "nome" per memorizzare il contenuto della cella C1 nome = Range("C1").Value 'importante: poichè si presuppone di voler copiare un file esistente, su cui abbiamo 'appena lavorato, è necessario salvarlo perchè le modifiche vengano salvate sull'origine 'prima della sua copia sul floppy. ThisWorkbook.Save 'ora controlliamo la dimensione del file che vogliamo copiare, dimensionando una variabile "miadime" per "leggere" (con la funzione FileLen( "percorso e nome")) la http://ennius.interfree.it/
Pagina 295
MANUALE VBA
X EXCEL
dimensione file, 'se sarà maggiore rispetto allo spazio di un floppy vuoto, si esce dalla routine avvisando. Dim miadime miadime = FileLen("C:\ExcelWeb\Macedonia.xls") If miadime > 1457000 Then MsgBox "LA DIMENSIONE DEL FILE E'TROPPO GRANDE. IMPOSSIBILE COPIARE" Exit Sub End If 'sotto vediamo l'istruzione che provvede a copiare, dove, nel percorso di destinazione, '(A:\) usiamo il nome preso nella cella C1 Set fs = CreateObject("Scripting.FileSystemObject") fs.CopyFile "C:\ExcelWeb\Macedonia.xls", "A:\" & nome & ".xls" 'sotto: inseriamo i due controlli errore, in cui sfruttando il numero dell'errore di run-time 'che si genererebbe se non venisse trovato percorso o nome del file da copiare (primo 'controllo) o se non fosse presente un floppy nel lettore di floppy (secondo controllo), si 'esce dalla routine. If Err.Number = 53 Then MsgBox "FILE DI ORIGINE NON TROVATO" Exit Sub End If If Err.Number = 71 Then MsgBox "INSERIRE UN FLOPPY NEL LETTORE" Exit Sub End If 'se tutto è andato liscio, si avvisa con questo messaggio: MsgBox "COPIA ESEGUITA !" 'si cancella dalla memoria la variabile fs Set fs = Nothing Resume End Sub Mi sembra sia spiegato tutto abbastanza bene,
http://ennius.interfree.it/
Pagina 296
MANUALE VBA
X EXCEL
Procedura per salvare un solo foglio di una cartella di lavoro. (26/05/03) Questo esercizio è applicabile ogni qualvolta si voglia salvare un unico foglio (non tutta la cartella) di lavoro. Spesso usiamo Excel per gestire una piccola amministrazione che contempli l'emissione di fatture e/o anche di documenti di trasporto (DDT). Necessità comune ad entrambi i documenti, al di là delle routine usate per la registrazione degli stessi in appositi fogli archivio, è quella di poter salvare una copia del documento, e solo quello. In questo modo, oltre a disporre di un file .xls di dimensioni più piccole rispetto alla dimensione della cartella usata per la gestione dell'attività, potremo gestire una copia di una fattura, per rivederla per consultarla e/o per ristamparla. La maniera più semplice è questa routine che apre la classica finestra "Salva con nome", in cui potremo definire noi il nome e la cartella dove salvare la copia della fattura. Questa routine la abbineremo ad un pulsante "Salva Fattura" che terremo sul foglio adibito alla compilazione della fattura: Sub Salvafattura() 'copiamo il foglio usando il nome del foglio, in questo esempio "Fatture", e verrà creato un 'nuovo foglio che sarà una copia del foglio Fatture: Sheets("Fatture").Copy 'apriamo la finestra "Salva con nome". Dopo che avremo assegnato il nome e selezionata la cartella di destinazione, premeremo il pulsante "Salva" Application.Dialogs(xlDialogSaveAs).Show 'la copia del solo foglio che abbiamo copiato, è stata salvata, e a questo punto chiudiamo 'il foglio-copia attualmente in vista a video, ritornando al foglio originale della cartella di 'lavoro. ActiveWindow.Close End Sub Come vedete, tre semplici righe di istruzione ci hanno aiutato. La copia ora sarà disponibile da consultare quando vorremo. Nella routine sotto invece consideriamo una variante più "automatizzata" che ci consentirà di saltare la presentazione della finestra "Salva con nome", in quanto sia il nome con il quale salvare la copia della fattura, sia il percorso, lo assegneremo usando il codice vba. Come nome sceglieremo la soluzione più ovvia : il numero fattura che inevitabilmente assegneremo alla fattura, e come percorso useremo una cartella precedentemente stabilita. vediamo la routine, come sempre associata ad un pulsante posto sul foglio fattura: Sub SalvafatturaAut() Dim X As String 'questa variabile assimila il dato che è in una cella, per prendere il nome, 'con cui salvare il solo foglio, io ho messo la A1, voi metterete la cella che porta il numero 'della fattura, vedi sotto X = "Fattura" & Range("A1").Value Sheets(1).Copy 'creo un nuovo foglio copia del foglio 1 (come esempio) 'queste sotto sono le istruzioni in cui si deve fornire il percorso, io ho messo C:\Temp, voi 'metterrete la vostra cartella scelta per contenere le copie fatture: ActiveWorkbook.SaveAs Filename:="C:\Temp\" & X & ".xls", FileFormat:= _ xlNormal, Password:="", WriteResPassword:="", ReadOnlyRecommended:=False _ , CreateBackup:=False ActiveWindow.Close End Sub E la copia verrà salvata in C:\Temp con il nome : Fatturanumerodellafattura.
http://ennius.interfree.it/
Pagina 297
MANUALE VBA
X EXCEL
Sfruttare gli Eventi per posizionare le istruzioni. (24/04/03) Davo per scontato che tutti i pellegrini che usano il vba, avessero sufficientemente chiaro che cos'è un evento e come sfruttarlo, ma dalle numerose richieste che mi giungono, ritengo di cercare di fare un pò di chiarezza, se ci riesco. Per "EVENTO" si intende l'"AZIONE" che si deve compiere per ATTIVARE le istruzioni abbinate all'"Oggetto" di cui l'evento fa parte. L'EVENTO si può verificare per nostra azione diretta (come la pressione su un pulsante (es.: evento Click)), o in conseguenza di azioni indirette che compiamo noi sul foglio o sulla cartella di lavoro (come la selezione di un foglio di lavoro (es.: evento Activate)). Gli EVENTI non sono uguali per tutti gli Oggetti; ogni Oggetto possiede eventi propri, non necessariamente simili ad altri Oggetti. L'oggetto Workbook (Cartella di lavoro) per esempio possiede l'evento Open, che altri oggetti non hanno, e che, indipendentemente dalla nostra volontà, si verifica tutte le volte che in Excel apriamo una cartella di lavoro. Potremo per esempio sfruttare questo evento, per far apparire un messaggio di saluto, o una userform, tutte le volte che apriremo la cartella (o file .xls che dir si voglia). Come? ma semplicemente inserendo l'istruzione, tramite l'editor di visual basic, nell'apposita finestra. (vedere, su questa sezione, "Editor di Visual Basic", dove ci sono foto che illustrano le spiegazioni.) L'oggetto WorkSheet (Foglio di lavoro) non possiede l'evento Open, ma l'evento Activate, che NON si verifica quando si apre la cartella di lavoro ma SOLO quando si attiva un Foglio, selezionandolo. E' necessario quindi che ogni pellegrino, si documenti sul significato di ogni evento, per capire come sfruttarlo e con quale sequenza si verifica rispetto ad un'altro evento dello stesso Oggetto. In genere, nella finestrina degli Eventi, gli stessi sono elencati in ordine di sequenza. Rifacendoci al WorkSheet, è evidente che l'evento Deactivate (cioè quando il foglio perde lo stato attivo, per es. perchè si seleziona un'altro foglio) si verifica sempre DOPO che si è verificato l'evento Activate, ed infatti nel menù degli eventi appare dopo. Non è questa la sede per elencare tutti gli Eventi di tutti gli Oggetti. La Guida in linea del visual basic è sufficientemente documentata : basta scrivere nella finestra "Indice" della guida, la parola "Events", e nella finestra 3 "Selezionare un argomento" troverete l'elenco di tutti gli eventi, singoli, o raggruppati per oggetti. Suggerisco quindi, anzichè scrivere a me per domande generiche sugli eventi, di munirsi della necessaria pazienza e di andare a leggersi la guida, che almeno su questi argomenti è chiara e ben fatta. Questo sotto è l'esempio tratto dalla guida, e che riguarda l'evento Change del WorkSheet Evento Change Si verifica quando celle del foglio di lavoro sono modificate dall'utente o mediante un collegamento esterno. Private Sub Worksheet_Change(ByVal Target As Range) Target Specifica l'intervallo modificato. Può essere costituito da più di una cella. Osservazioni Questo evento non si verifica quando i valori contenuti nelle celle cambiano durante una fase di ricalcolo. Utilizzare l'evento Calcola per intercettare un nuovo calcolo nel foglio. Esempio Questo esempio modifica il colore delle celle modificate su blu. Private Sub Worksheet_Change(ByVal Target as Range) Target.Font.ColorIndex = 5 End Sub Come vedete, l'esempio è più che chiaro. Basta leggere.....
http://ennius.interfree.it/
Pagina 298
MANUALE VBA
X EXCEL
Lavorare con gli Shapes. (19/06/03) Gli Shapes (le Forme) sono un "territorio" che non conosco molto bene, ma in seguito ad una richiesta, visto poi che non è difficile capirne le basi, presento un esercizio che forse può interessare altri pellegrini. Intanto cosa sono gli Shapes: sono "oggetti" appartenenti al gruppo "Disegno", quali una Forma, una figura a mano libera, un rettangolo, un cerchio, una casella di testo, un oggetto OLE o un'immagine, e che si possono inserire sul foglio di lavoro semplicemente selezionando l'icona dell'oggetto posta nella finestra "Disegno", e poi, cliccando in un punto del foglio di lavoro, trascinare tenendo premuto il pulsante sinistro, per ottenere il dimensionamento voluto. Ogni oggetto Shape può essere inserito non solo manualmente, ma anche tramite codice vba: sconsiglio alle "reclute" l'inserimento tramite codice, visto che la creazione di uno Shape, che peraltro si ottiene con il metodo AddShape, comporta la definizione di un numero elevato di istruzioni (e a secondo il tipo di Shape scelto, di "nodi" )che servono per il posizionamento dello Shape sul foglio di lavoro, oltre ai valori che ne determinano la dimensione e le caratteristiche, operazione complessa per chi vuol compilarlo da solo. Per verificare quanto appena detto, attivate il "registratore di macro", date un nome alla macro o lasciate il nome che propone Excel, e poi inserite uno Shape, posizionandolo e dimensionandolo, agendo sui circoletti che appaiono intorno al perimetro dello Shape, e dategli un colore; ad operazione ultimata, stoppate il registratore e recatevi nell'editor di visual basic, su un modulo troverete la macro che Excel ha compilato per voi, e vedrete tutte le istruzioni che servono a "creare" e impostare lo Shape. Qui sotto vi riporto un esempio per creare uno Shape rettangolo : ActiveSheet.Shapes.AddShape(msoShapeRectangle, 108.75, 30#, 84.75, 30#).Select Selection.ShapeRange.Fill.Visible = msoTrue Selection.ShapeRange.Fill.Solid Selection.ShapeRange.Fill.ForeColor.SchemeColor = 11 Selection.ShapeRange.Fill.Transparency = 0# Selection.ShapeRange.Line.Weight = 0.75 Selection.ShapeRange.Line.DashStyle = msoLineSolid Selection.ShapeRange.Line.Style = msoLineSingle Selection.ShapeRange.Line.Transparency = 0# Selection.ShapeRange.Line.Visible = msoTrue Selection.ShapeRange.Line.ForeColor.SchemeColor = 64 Selection.ShapeRange.Line.BackColor.RGB = RGB(255, 255, 255) Ogni Shape comunque inserito, viene identificato da un nome che identifica il "tipo" di oggetto, ed un numero progressivo per ogni oggetto Shape inserito, indipendentemente dal "tipo". Ad esempio, se inseriamo un Rettangolo, nelle istruzioni andrà identificato con Shapes("Rectangle 1"), se ne inseriamo un secondo, il suo identificativo sarà Shapes("Rectangle 2"), se inseriamo un "ovale" il suo idendificativo sarà Shapes("Oval 3") e così via. Attenzione : se cancelliamo uno Shape e poi ne inseriamo uno nuovo, il numero assegnato al nuovo NON sostituisce il numero di quello cancellato, ma andrà in progressione; Excel memorizza infatti tutti gli oggetti Shape inseriti, anche quelli cancellati, ed assegna il numero successivo Ma come caspita facciamo a conoscere con quale nome e numero Excel (il suo vba) identifica un oggetto, visto che quando lo si inserisce (con una routine come quella sopra) col metodo AddShape, il nome dell'oggetto viene assegnato usando una costante (tipo : msoShapeRectangle ) (vedi elenco costanti) e non con Shapes("Rectangle X") ? Mi permetto di suggerire un metodo pratico e veloce: inseriamo manualmente il o i nostri Shapes, senza curarci di registrare niente; una volta inserito/i, attiviamo il registratore di macro indi clicchiamo sul o sugli Shapes; ora fermiamo la registrazione e nella macro creata da Excel, andiamo a leggere: troveremo il o i nomi con relativo numero indice e l'istruzione Select. Basterà ricordarsi con quale ordine abbiamo cliccato (selezionato) gli Shapes per trovare il relativo nome, un esempio: ActiveSheet.Shapes("Rectangle 1").Select ActiveSheet.Shapes("Rectangle 5").Select ActiveSheet.Shapes("AutoShape 3").Select ActiveSheet.Shapes("Freeform 2").Select ActiveSheet.Shapes("AutoShape 4").Select ActiveSheet.Shapes("Rectangle 6").Select Un'altra possibilità di intervento sugli Shapes, ma questa la facciamo dal foglio di lavoro, è quella di richiamare, cliccando col destro del mouse sullo Shape, un menù contestuale, e da questo scegliere "Aggiungi testo" (e potremo inserire una parola o una frase) oppure scegliere "Formato Forme" che http://ennius.interfree.it/
Pagina 299
MANUALE VBA
X EXCEL
aprirà la relativa finestra dove saranno possibili tutta una serie di regolazioni, dalla scelta del font ed il suo colore, al centraggio del testo, al colore di fondo dello Shape, la sua trasparenza, la possibilità di impostare sfumature di colore, bloccarlo o meno, ecc. ecc. Tutte operazioni che comunque possiamo fare dopo aver attivato il registratore di macro, in modo da poter curiosare, ed apprendere, come il codice imposta tutti i passaggi che noi abbiamo fatto. Ma veniamo all'esercizio: vogliamo "disegnare" sul foglio delle aree da usarsi come "mappe", cioè identificativi di zone, come se si fosse su una cartina topografica. Vogliamo che ad ogni click si attivino delle istruzioni, e che lo Shape cambi colore : ad ogni colore, vogliamo che vengano eseguite istruzioni diverse. Useremo lo Shape come se fosse un Commandbutton, al quale ad ogni click facciamo cambiare la Caption e quindi le istruzioni correlate. Per prima cosa, useremo, per il disegno a mano libera, la Forma: "Figura a mano libera", reperibile dal menù Disegno/Forme/Linee/Figura a mano libera, così come nella sottostante immagine:
Realizzeremo un perimetro che chiuderemo ad anello con un doppio click di sinistro, e dopo aver impostato un colore, e regolata la trasparenza al 50%, otterremo una cosa del genere:
ora prepariamo una macro, dandogli un nome, nella quale inserire le nostre istruzioni. (quando la macro sarà compilata, tornando col destro sullo Shape, nel menù contestuale sceglieremo: "Associa macro" associandogli il nome della macro. Le istruzioni sono semplici, unica cosa che sottolineo è la necessità di selezionare lo Shape (per identificarlo) usando il metodo Select di cui Selection è una proprietà che utilizzeremo usando l'istruzione With : Sub Dimmi() ActiveSheet.Shapes("Freeform 1").Select 'si seleziona lo shape With Selection 'con la selezione attiva: 'sotto: Se il colore dello shape è ugualr a verde, allora If .ShapeRange.Fill.ForeColor.SchemeColor = 13 Then 'mi cambi in suo colore in giallo .ShapeRange.Fill.ForeColor.SchemeColor = 3 'ed esegui queste istruzioni (io ho messo una semplice messagebox, ma potranno essere 'eseguite qualsiasi tipo di istruzioni) : MsgBox "Sono uno Shape" Range("A1").Select 'per deselezionare lo shape http://ennius.interfree.it/
Pagina 300
MANUALE VBA
X EXCEL
Else 'altrimenti (cioè se il colore non è verde, e infatti sarà giallo dopo il primo click) 'esegue queste altre istruzioni (nell'esempio ancora un messaggio) MsgBox "Buongiorno a tutti" 'ora colora di nuovo di verde, creando l'alternanza per la condizione If .ShapeRange.Fill.ForeColor.SchemeColor = 13 Range("A1").Select 'per deselezionare lo shape End If End With End Sub E questo, a parte il messaggio che non mostro, è l'effetto del cambio colore:
Come vedete non è difficile capire le istruzioni, l'unica cosa, non sono riuscito a trovare i codici colori usati da Fill.ForeColor.SchemeColor, consiglio i volenterosi, di selezionare sul foglio lo Shape, attivare il registratore di macro, e dalla finestra Formato Forme, cominciare a cambiare colore all'oggetto; in questo modo, a fine registrazione, avrete i codici colore scritti nella macro che il registratore avrà creato. Potrete segnarveli e poi cancellare la macro. Buon lavoro
http://ennius.interfree.it/
Pagina 301
MANUALE VBA
X EXCEL
Sintassi (21/07/03) Sicuramente uno degli aspetti nella compilazione di codice, è come fare a scrivere, cioè quale sintassi usare, per far capire ad Excel determinati parametri. Un veloce esempio per illustrare cosa si intende, viene da una recente domanda postami: "come scrivere l'istruzione per far capire ad Excel che i riferimenti alle celle (cioè gli indirizzi cella) che delimitano una zona che si intende selezionare, sono rappresentati da valori scritti in due celle" . Simuliamo l'esempio: in A1 scriviamo C1, e in B1 scriviamo H1: A B C D E F 1 C1 H1 2 vorremo quindi selezionare la zona compresa tra C1 ed H1. La prassi di usare due celle per scriverci i riferimenti ad altre celle, deriva dal fatto che si vogliono rendere variabili le zone da selezionare di volta in volta, e in questo modo saremo noi ad indicare le aree da selezionare modificando i riferimenti nelle due celle fisse (A1 e B1 o quelle che preferite voi). Non possiamo scrivere espressioni strane, tipo Range("A1.Value:B1.Value").Select o similari, perchè il debugger ci dà una una mazzolata in testa, e ci rimanda con foglio di via, all'asilo. Per fortuna che ogni tanto la nostra cpu si rimette a funzionare, ed elabora: cosa ci vuole perchè Excel possa identificare una zona? I riferimenti alle celle ! Assegniamo quindi a due variabili questi riferimenti: X = Range("A1").Value (ed X sarà uguale a C1) Y = Range("B1").Value (ed X sarà uguale a H1) Ora, per capire il perchè della sintassi che adopereremo, bisogna precisare che il riferimento A1 in Range("A1") (lo stesso per B1) viene visto come un riferimento letterale, cioè una "stringa"e quindi posto tra doppi apici; X e Y sono invece variabili (di tipo Variant), e NON vanno messe tra doppi apici (scusate, ma meglio non mi riesce spiegarmi), per cui una istruzione come Range(X : Y) dovrebbe andare bene, invece no perchè ci sono i due punti ( : ) che indicano l'intervallo, che non sono di tipo Variant, ma testo e quindi i due punti vano messi tra due doppi apici, concatenandoli con il segno di concatenazione: & tra i due valori Variant - vediamo quindi la giusta sintassi: Range(X & ":" & Y).Select Quello che poi faremo con la selezione, non è affare di questa pagina, ma la selezione C1:H1 ci sarà. Vediamo ora il caso in cui si voglia indicare un intervallo i cui la sintassi per i riferimenti è mista: cioè uno dei riferimenti è in stile notazione A1, e l'altro e reperito tramite una variabile. Supponiamo quindi di voler identificare (e selezionare) tutte le righe che intercorrono tra la prima riga (la numero 1, ma potrà essere qualunque altra, ma definita a priori) e la riga corrispondente alla cella in quel momento attiva. Non sapendo prima quale sarà la cella attiva, useremo nelle istruzioni, una variabile che identifichi la riga della cella attiva, tipo: X = ActiveCell.Row (ed X sarà uguale al numero di riga della cella selezionata in quel momento) Rows(X).Select L'istruzione è giusta e con Rows(X) rispettiamo quanto sopra detto e selezioniamo la riga intera. Se ora però cerchiamo di usare la sintassi appena vista per selezionare tutte le righe tra la riga numero 1 e la numero X, scrivendo così: Rows("1:X").Select otteniamo un bell'errore di run-time: "tipo non corrispondente"; infatti X è una variabile Variant e come tale non può essere inserita in una "stringa" (gli intervalli messi tra doppi apici), a meno che non si usi la sintassi prevista, cioè tra due doppi apici e due segni di concatenazione ( & ). Questa sarà allora la giusta istruzione (in rosso la correzione): X = ActiveCell.Row Rows("1:" & X & "").Select Attenzione! questo tipo di istruzioni (selezioni di intervalli) dovranno risiedere in macro appositamente costruite e non bisogna inserirle nell'evento WorkSheet.Selection_Change; il perchè è facile capirlo: l'attivazione delle istruzioni si basa appunto su un cambio di selezione, che è quello che le istruzioni in pratica fanno: cominciano a selezionare la prima riga (per proseguire fino alla riga X) ma lì si fermano in ossequio alle istruzioni che vengono ripetute, e trovando la prima riga come attiva, lì si fermano. Altro esempio Ora vediamo un esempio in cui si vogliano salvare o aprire file i cui nomi sono rappresentati da due o tre valori presenti in altrettante celle di un foglio di lavoro. Un tipico esempio potrebbe essere quello di un foglio archivio fatture, dal quale, tramite macro, vogliamo aprire per consultazione una fattura salvata in precedenza. Vediamo la situazione: http://ennius.interfree.it/
Pagina 302
MANUALE VBA
X EXCEL
N.Fatt Data Nominativo Imponibile ImportoIva TOTALE 1 1 07-08-03 Filippini Spa 1.000 200 1.200 2 2 31-08-03 Giocondi srl 1.500 300 1.800 3 3 31-08-03 Lippetti F. & C 2.000 400 2.400 e che si sia deciso, nel salvataggio del foglio emissione fattura, di usare, come nome da assegnare alla copia, il N.Fatt, la data, ed il nominativo cliente, per cui il file salvato si presenterebbe: 107-08-03Filippini Spa.xls oppure avremmo potuto salvarlo con nominativo, data, n.fatt. non ha importanza la chiave che sceglieremo come ordinamento per il salvataggio, l'importante sarà seguire sempre lo stesso ordine, sia per salvare che per aprire poi una copia fattura. Una precisazione: visto che usiamo anche il campo "Data" come componente del nome da salvare, ricordo che Windows NON consente l'uso di barre ( / ) nei nomi di files, per questo usiamo il trattino ( - ) come separatore di data. Ora dovremo decidere quale campo scegliere da selezionare per attivare le istruzioni di apertura file, tenendo presente che dovremo "concatenare" gli altri due campi che formano il nome del file da aprire. Supponiamo di selezionare un campo "Nominativo": dovremo usare una variabile che "leghi" insieme i valori di tre celle: il Nominativo , che sarà Activecell in quanto lo selezioneremo la Data, che sarà una cella a sinistra rispetto all'Activecell (useremo Offset per reperire il valore) il N.Fatt, che sarà due celle a sinistra rispetto all'Activecell (useremo Offset per reperire il valore) Quindi la sintassi da usare per comporre la variabile "nome" sarà: nome = Activecell.Offset(0, -2) & Activecell.Offset(0, -1) & Activecell Selezionando quindi "Filippini Spa", "nome" sarà uguale a: 107-08-03Filippini Spa Ovviamente la sequenza della concatenazione e quindi degli Offset dipenderà dal campo che decideremo di usare come chiave iniziale di apertura, se scegliessimo di selezionare un numero di fattura, rendendo questo Activecell, "nome" sarebbe uguale a: nome = Activecell & Activecell.Offset(0, 1) & Activecell.Offset(0, 2) La routine per l'apertura della copia diventa (col primo caso): Sub aprifile() Dim X As String nome = Activecell.Offset(0, -2) & Activecell.Offset(0, -1) & Activecell X = "C:\VostraCartella\" & nome & ".xls" Workbooks.Open Filename:=X, ReadOnly:=False End Sub Aggiornerò questa pagina con altri "suggerimenti". Buon lavoro.
http://ennius.interfree.it/
Pagina 303
MANUALE VBA
X EXCEL
Utilizzare il VBA al posto della Funzione SOMMA.SE (05/05/03) Ancora un esercizio con un doppio ciclo For Each ... Next per sostituire la funzione Somma.Se non facilmente applicabile in uno schema di tabella come quello che prendiamo in esame. Questo esercizio è rivolto a tutti coloro che disponendo di una tabella con inseriti dati alla rinfusa, vogliano reperire totali di valori, da riportare in un'altra tabella. Per "totali di valori" si possono ricercare importi, numeri, numero presenze, ore, ecc. ecc. riferiti a nominativi precisi, quali Ditte, Operai, Camere, Fascicoli, Nominativi in genere, Titoli i libri o di film, e chi più ne ha più ne metta. E' molto importante definire lo schema di una tabella, cioè come impostare campi (colonne) e record (righe), molto dipende infatti da una loro impostazione precisa e ordinata, scegliendo la chiave di registrazione più opportuna. Nell'esempio prenderemo quindi come chiave di registrazione una Data. Vediamo un esempio nel quale per ogni data immessa nella colonna A, abbiamo bisogno di registrare quanti interventi eseguiamo presso una determinata Ditta. Useremo quindi le colonne seguenti la A, per riportare il numero di interventi e a lato a quale ditta si riferiscono. Disponendo di più operatori, destineremo tante coppie di colonne per quanti operatori lavoreranno. Nell'esempio ne ho considerati 5, quindi 10 colonne. Cosa vogliamo sapere? Desideriamo tenere sotto controllo il TOTALE del numero di interventi per Cliente (ditta). Il risultato lo vogliamo in altra zona del foglio (ma potrebbe essere su un altro foglio). Per comodità visiva per la zona di raccolta totali uso le celle da A16 a C27: nella colonna A riporteremo i nomi delle Ditte e nella colonna C otterremo i totali per ogni ditta.
Abbiamo bisogno quindi di un ciclo che per ogni ditta riportata in colonna A, zona "raccolta", "spazzoli" tutta la zona della tabella principale alla ricerca dello stesso nome ditta, e trovatala, cominci a totalizzare il valore che si trova nella cella a sinistra. Per questa "spazzolatura" (ricerca) useremo un altro ciclo interno al primo che, munito di un totalizzatore, restituisca alla fine la somma dei valori trovati. Questo totale lo affideremo alla cella della colonna C, stessa riga del nome cercato. Per ovviare all'ipotesi che i nomi scritti nella tabella degli interventi possano differire per maiuscole/minuscole dallo stesso nome scritto nella tabella di raccolta, usiamo nel modulo che ospita la routine, sezione "Generale - Dichiarazioni", l'istruzione : Option Compare Text Option Compare Text ___________________________________________________________________ Sub CercaeSomma() Dim CL As Object Dim C As Object 'riga sotto: per ogni cella nel range A16:A27 For Each CL In Range("A16:A27") 'riga sotto: controllo se nella cella esiste un nome, se le cella è vuota, passo alla 'successiva (con l'ultimo next) If CL.Value = "" Then GoTo 10 'altrimenti inizio il secondo ciclo per controllare tutte le celle nel range D1:Y59 For Each C In Range("C2:K13") http://ennius.interfree.it/
Pagina 304
MANUALE VBA
X EXCEL
'se trovo in nome che è in A (nel CL in quel momento attuale) If C.Value = CL.Value Then 'allora incremento un totale col valore che è nella cella accanto al nome trovato tot = tot + C.Offset(0, -1).Value End If 'controllo tutte le celle con Next C per controllare l'attuale nome Next C 'finito il ciclo interno, assegno alla cella della colonna H, stessa riga di CL, il totale ottenuto CL.Offset(0, 2).Value = tot 'poi azzero il contatore tot = 0 10: 'e proseguo il ciclo per la successiva cella in A che ripete il ciclo interno Next End Sub In pratica il primo ciclo controlla, dall'inizio alla fine, tutte le celle del Range A16:A27, e per ogni cella, attiva il secondo ciclo che ricerca nel Range C2:K13 tutte le celle il cui valore corrisponde, sommando i valori che sono nella cella a sinistra (Offset(0, -1)). E' evidente che se le aree saranno diverse basterà indicare negli appositi range i riferimenti che ci necessitano. Se poi la tabella di destinazione risiedesse su un'altro foglio, oltre ovviamente a definire il range diverso in cui si troveranno i nomi ditte di cui vogliamo il totale, useremo l'accortezza di lanciare la macro da questo foglio, e nelle istruzioni cambierà solamente l'indicazione della zona dove effettuare la ricerca, aggiungendo il nome del foglio, così: For Each C In Worksheets(1).Range("C2:K13") Buon lavoro.
http://ennius.interfree.it/
Pagina 305
MANUALE VBA
X EXCEL
Sostituzione di un valore esistente con un altro valore. Lavorando con tabelle o elenchi, può capitare di voler aggiornare dei dati, con altri dati nelle stesse celle. Si potrebbe voler modificare, per esempio, un'aliquota iva in un elenco di articoli, che serve a calcolare il prezzo di vendita (e quindi un valore numerico), oppure un valore percentuale di ricarico (sempre valore numerico), o ancora un nome di un agente in una lista di calcolo provvigioni (valore testo), o una data di scadenza formato data), insomma qualunque dato ci serva di voler modificare, dove esistano più valori uguali (se ci fosse da modificare un solo valore, forse conviene farlo manualmente) e PURCHE' tutti i dati da modificare siano nello stesso campo (colonna), anche se su righe non conseguenti. Se i valori da modificare fossero sempre gli stessi (modificando e modificatore), potremmo usare una routine con questi valori già impostati nel codice stesso, ma per rendere più gestibile la cosa, in modo da poter definire di volta in volta qual'è il valore da modificare e qual'è il valore che va in sostituzione, e su quale campo eseguire l'intervento, useremo tre variabili per l'assegnazione di tre celle dove scriveremo: il valore che vogliamo modificare(A1), il valore che lo sostituisce(B1), il riferimento alla colonna (campo) dove vogliamo effettuare le sostituzioni(C1). Supponiamo quindi di avere un elenco così formato (vedi sotto), e di dover provvedere alla modifica dell'aliquota iva del 20 sostituendola con una nuova aliquota: 15 (utopia). In A1 scriveremo 20 (il valore da modificare) in B1 scriveremo 15 (il valore in sostituzione) e in C1 il riferimento alla colonna relativa all'inizio colonna dove intendiamo fare le sostituzioni (F2). L'elenco sarà lungo (supponiamo) 300 righe. A B C D E F G H 1 20 15 F 2 Codice Articolo Costo Ricarico Prezzo al.iva Imp.Iva Prezzo Ven 3 Questa sarà la nostra routine che cercherà nel range F2:F300 tutte le celle che portano il valore 20, e lo sostituirà col valore 15: Sub FindValore() Dim X, Y, Z, W 'dichiarazione variabili di tipo Variant (numeri o lettere) X = Range("A1").Value 'assegnazione delle variabili alle celle, in questo caso : X=20 Y = Range("B1").Value 'nuovo valore in sostituzione : Y=15 Z = Range("C1").Value 'colonna su cui operare : Z=F 'assegnazione del Range di intervento (lunghezza: dalla cella 2 alla 300) W = Z + "2:" & Z + "300" 'inizio ciclo ricerca With Worksheets(1).Range(W) ' (W=F2:F300) Set c = .Find(X, LookIn:=xlValues) 'c = cerca il valore X (in A1) da cercare in W If Not c Is Nothing Then firstAddress = c.Address Do c.Value = Y 'se X trovato, sostituzione del valore presente in c con Y (range B1) Set c = .FindNext(c) 'prosecuzione del ciclo ricerca If c Is Nothing Then Exit Sub End If Loop While Not c Is Nothing And c.Address firstAddress End If End With End Sub Se avessimo voluto sostituire dei valori fissi (20 in 15), questa la routine (più semplice): Sub FindValoreFisso() With Worksheets(1).Range(F2:F300) 'range in cui eseguire la ricerca Set c = .Find(20, LookIn:=xlValues) 'si cerca il valore 20 If Not c Is Nothing Then firstAddress = c.Address Do http://ennius.interfree.it/
Pagina 306
MANUALE VBA
X EXCEL
c.Value = 15 'si sostituisce con il valore 15 Set c = .FindNext(c) If c Is Nothing Then Exit Sub End If Loop While Not c Is Nothing And c.Address firstAddress End If End With End Sub
http://ennius.interfree.it/
Pagina 307
MANUALE VBA
X EXCEL
Operazioni su numeri con risultato nella colonna a lato. (23/04/03) E' proprio vero che le esigenze di ognuno di noi sono talmente varie che non è possibile definire degli esempi da standardizzare. Rimane tuttavia la possibilità di citare tutte le possibili applicazioni che si presentano di volta in volta, cercando di spiegare le necessità occorrenti ai vari casi, e relative soluzioni proposte, sperando così di poter fornire esempi assimilabili per altre esigenze. Questa volta riporto ancora un esercizio risolto con il solito ciclo For Each ...Next, ma in cui ovviamente sono diversi, dagli altri esempi visti, le problematiche riscontrate. Vediamo il problema : in una colonna in cui il numero di righe può variare, sono inseriti dei numeri. si richiede una macro che prenda il valore contenuto nella cella A1 e lo sottragga al valore contenuto nella cella A2, il risultato deve essere collocato nella cella B1 e cosi di seguito con la cella A2 sottratta ad A3 ed il risultato in B2. Il tutto deve essere ripetuto fino a che la macro trova le celle della colonna A contenenti un valore. Forse un immagine spiega meglio cosa si vorrebbe ottenere:
In B1 abbiamo la differenza di A1-A2 (10-20=-10), in B2 la differenza di A2-A3 (20-30=-10), in B3 la differenza di A3-A4 (30-50=-20), in B4 la differenza di A4-A5 (50-30=20), ecc. ecc. La routine è semplice: visto che non sapremo quanto sarà lungo l'elenco nella colonna A, useremo la funzione End per trovare l'ultima cella che contiene dati, quindi eseguiremo un ciclo che per ogni cella della colonna A, otterrà il valore della cella stessa in quel momento identificata, lo sottrarrà al valore della cella successiva (identificata con Offset(1, 0)) e questo risultato lo inserirà nella cella a sinistra rispetto alla cella in quel momento attiva, identificandola con Offset(0, 1). Questa la routine: Dim CL As Object 'sotto: reperisco tutta l'area che contiene dati nella 'colonna A: dalla prima cella: Cells(1, 1) all'ultima trovata 'occupata: Cells(1, 1).End(xlDown) Set zona = Range(Cells(1, 1), Cells(1, 1).End(xlDown)) 'poi inizio il ciclo di ricerca con: per ogni CL (cella) 'contenuta in "zona" For Each CL In zona 'nella cella accanto a quella selezionata (CL) è la cella selezionata 'ma con CL.Offset(0, 1) identifico la cella, stessa riga (0) ed 'una colonna a destra) e in questa faccio fare la differenza tra la cella attiva e la cella sottostante identificata con CL.Offset(1, 0) CL.Offset(0, 1) = CL.Value - CL.Offset(1, 0) 'con Next si passa alla cella successiva alla prima selezionata, fino alla fine delle celle 'presenti in "zona" Next End Sub Un esercizio molto semplice ma che ci abitua al concetto di ciclo, suggerendoci anche come reperire gli estremi di un'elenco e a identificare le altre celle che ci interessano con Offset.
http://ennius.interfree.it/
Pagina 308
MANUALE VBA
X EXCEL
Splittare (dividere, separare) un file di testo importandolo in Excel. Termine italianizzato dell'inglese To Split, che vuol dire : dividere. Vagando in quell'immensa banca dati che è la Guida in Linea di Excel (purtroppo a volte di non facile consultazione), ho trovato delle istruzioni, e curiosando fra le righe, è venuto fuori che potrebbero interessare dei "pellegrini", visto anche un certo tipo di richieste pervenutemi. Si tratta della possibilità di importare dei file di testo e comporre in un foglio di lavoro, una tabella divisa in righe e colonne con i dati provenienti dal file di testo. Sono necessari alcuni accorgimenti per ottenere un risultato valido, come quello di usare un separatore, come la virgola o il punto e virgola, tra una parola e l'altra di una stessa riga del file di testo, in modo che excel, per ogni parola e per ogni separatore, assegni la parola ad una cella, e saranno tante celle sulla stessa riga, per quante parole e separatori sono presenti nella riga del file di testo. Per ogni riga successiva presente nel file di testo, saranno assegnate altrettante righe sul foglio di lavoro. L'aspetto più interessante di questo metodo è forse rappresentato dal fatto che molti database in circolazione, conservano i loro dati non in tabelle, ma in file, magari non con estensione .Txt, ma che del Formato testo ne fanno la loro struttura, infatti i dati sono conservati in stringhe di testo dove i dati dei vari campi sono dati separati dalla virgola. Sarà possibile quindi importare uno di questi database perchè Excel provveda a creare una tabella con i dati così caricati. E conosciamo tutti l'importanza di lavorare con Excel con dati inquadrati in tabelle.. Potremo anche predisporre dei nostri file di testo per creare elenchi da importare e gestire in Excel. Esemplificando, potremmo disporre di un file di testo come l'esempio sotto: nome file: Miofile.txt Codice,Articolo,costo,ricarico,prezzo,aliva,imp.iva,vendita 2210ab,borsa,36,80%,,20 2310ab,borsello,41,90%,,20 2410ab,busta,36,100%,,20 2410bb,bustina,25,100%,,20 e questa sarà la tabella che Excel ci creerà facendogli importare un file di testo compilato come l'esempio sopra (va da se che le formule nelle celle le dovremo compilare DOPO aver importato il file) :
Precisazioni : nel file di testo, dopo i valori di percentuale, ci sono due virgole senza nessun valore in mezzo: Excel interpreta lo spazio vuoto tra due virgole, come uno scarto di colonna, tant'è che nella tabella i valori corrispondenti (20) vengono posizionati nella colonna "aliquota iva" come deve essere. Attenzione al formato celle che ospiteranno i dati, dovranno essere settate in funzione del tipo di dato che ospiteranno; la colonna D è stata impostata a formato celle: "percentuale". Questo per evitare interpretazioni errate da parte di Excel. Una precisazione : per importare valori con decimali, non si può adoperare la virgola, perchè Excel la interpreta come separatore di elenco, ma il punto e virgola. Questa comunque la routine, dove dovrà essere indicato il percorso completo del file da importare, mentre sarà possibile definire in quale cella far importare il file; nell'esempio ho usato la A1: Sub miofile() With ActiveSheet.QueryTables.Add(Connection:="Text;C:\Documenti\Miofile.Txt", _ Destination:=Range("A1")) .Name = "Split" .FieldNames = True .RowNumbers = False .FillAdjacentFormulas = False .PreserveFormatting = True .RefreshOnFileOpen = False .RefreshStyle = xlInsertDeleteCells .SavePassword = False .SaveData = True .AdjustColumnWidth = True .RefreshPeriod = 0 http://ennius.interfree.it/
Pagina 309
MANUALE VBA
X EXCEL
.TextFilePromptOnRefresh = False .TextFilePlatform = 850 .TextFileStartRow = 1 .TextFileParseType = xlDelimited .TextFileTextQualifier = xlTextQualifierSingleQuote .TextFileConsecutiveDelimiter = False .TextFileTabDelimiter = True .TextFileSemicolonDelimiter = True .TextFileCommaDelimiter = True .TextFileSpaceDelimiter = False .TextFileColumnDataTypes = Array(1, 1, 1, 1) .TextFileTrailingMinusNumbers = True .Refresh BackgroundQuery:=False End With End Sub Alcune considerazioni: l'istruzione si basa sull'oggetto QueryTables che utilizza il metodo Add per creare una (letteralmente) "tavola di consultazione" con dati provenienti da una fontedati (in questo caso un documento di testo). La QueryTables possiede numerose proprietà che consentono di "pilotare" le impostazioni nella formazione della tabella. Quì sotto riporto alcune spiegazioni, mentre invito gli eventuali interessati ad andarsi a scoprire le altre nella guida in linea (del Visual Basic Editor). Parametri da modificare per: IMPORTARE TESTO SEPARATO DA VIRGOLA PER AVERE TESTO SU PIU' COLONNE (esempi precedenti): .AdjustColumnWidth = True .TextFileTextQualifier = xlTextQualifierSingleQuote .TextFileCommaDelimiter = True IMPORTARE TESTO SEPARATO DA PUNTO E VIRGOLA PER AVERE TESTO SU PIU' COLONNE (in questo caso è possibile usare la virgola per definire numeri decimali): .AdjustColumnWidth = True .TextFileTextQualifier = xlTextQualifierdDoubleQuote .TextFileCommaDelimiter = False IMPORTARE TESTO SEPARATO DA VIRGOLA PER AVERE TESTO SU UNA COLONNA (stile CSV, separato da virgole): .AdjustColumnWidth = False .TextFileTextQualifier = xlTextQualifierSingleQuote .TextFileCommaDelimiter = False
I file .CSV sono file che è possibile usare come database per il Web. Il motore di ricerca usato nella sezione "le vs domande" su questo sito, sfrutta codice Javascript per "pescare" i dati che formano il database del motore, in un file .CSV, che si ottiene salvando un solo foglio di Excel, selezionando come formato di salvataggio il formato .CSV. A chi interessa può leggersi su "le vs domande", pag. 6, "salvare file in formato .csv", dove è presente anche la routine per farlo. Se qualcuno, mi vorrà illuminare sull'argomento, sarà il benvenuto.
http://ennius.interfree.it/
Pagina 310
MANUALE VBA
X EXCEL
Uno strano comportamento. (09/02/03) Segnalo un caso a cui non trovo risposta. Usando un "CommandButton" preso da "Strumenti di Controllo" alias "Casella degli Strumenti" (e quindi un oggetto ActiveX), e sfruttando il suo evento Click per eseguire istruzioni, ho notato che alcune istruzioni vengono eseguite in maniera impropria. Le indicazioni che seguono hanno due precisi scopi: primo: segnalare ad altri questo comportamento, ed eventualmente come aggirare il fatto. secondo: se qualcuno conosce il perchè del comportamento e mi vuole cortesemente illuminare. Ma veniamo al fatto: Copiare un range di dati da un foglio in un altro foglio. Operazione eseguibile in molti modi e comunque necessità abbastanza diffusa nei nostri lavori. Altra necessità richiesta all'operazione, trovare la prima riga libera sul foglio destinazione, dove incollare i dati copiati. Quindi prepariamo le nostre istruzioni e le assegniamo ad un pulsante che risiede sul foglio di partenza. Queste le istruzioni e relativi commenti: Set Zona = Worksheets(1).Range("tuazona") 'si assegna alla variabile Zona un range di celle del foglio1 al quale range è stato assegnato il nome "tuazona, nell'esempio il range A2:B20. Worksheets(2).Select 'si seleziona il foglio di destinazione (foglio 2) 'sotto: si inizia un ciclo di ricerca, a partire dalla riga 2 del foglio 2, cercando la prima riga con l'istruzione "While Cells(iRow, 1).Value "" "che vuol dire: fino a che non trovi una riga (iRow), nella colonna A (, 1) con la cella vuota, passi alla riga successiva (iRow + 1) quando la trovi, esci dal ciclo (Wend) Dim iRow As Integer iRow = 2 While Cells(iRow, 1).Value "" iRow = iRow + 1 Wend 'trovata la riga con la cella vuota, incollo i dati contenuti in Zona Zona.Copy Cells(iRow, 1) E qui avviene lo strano comportamento: inserendo l'istruzione nell'evento Click di un CommandButton posto sul foglio1, i dati presenti in "tuazona", anzichè venire copiati nella prima riga libera, colonna A, del foglio2, vengono incollati nella colonna A ma del foglio1, in coda ai dati presenti. Eppure le istruzioni vengono seguite: se sospendiamo le istruzioni del ciclo di ricerca, vediamo che il foglio 2 viene regolarmente selezionato. Ed è qui, sul foglio 2 che dovrebbe avvenire la ricerca della prima riga libera. Invece la routine seleziona il foglio2 e poi incolla sul foglio1 (dove risiede il Commandbutton) eseguendo lì il ciclo di ricerca. Che la routine funzioni, non c'è dubbio: basta provare in questi altri modi: Assegnare le istruzioni ad una macro, ed associare la macro ad un pulsante creato con gli strumenti Disegno/Rettangolo o addirittura allo stesso evento Click del CommandButton, richiamando però la macro e non inserendo direttamente ivi le istruzioni. Usare una Userform da richiamare con un Commandbutton o pulsante messo sul foglio 1, ed assegnare le istruzioni ad un Commandbutton posto sulla stessa Userform (evento Click). Assegnare le istruzioni all'evento Click di un Commandbutton, ma posto sul foglio 2, cioè lo stesso della destinazione. Sotto: l'immagine del foglio1 con i pulsanti:
http://ennius.interfree.it/
Pagina 311
MANUALE VBA
X EXCEL
Sotto: l'immagine del foglio 2 con l'incollaggio eseguito perfettamente usando uno qualsiasi dei tre pulsanti tranne il pulsante "Archivia" sul foglio1, oppure usando il pulsante "Archivia" ma sul foglio 2:
Sotto: l'immagine del Foglio 1 e cosa avviene se si usa il pulsante "Archivia" con le istruzioni poste nell'evento Click: i dati sono incollati al di sotto dei dati di partenza:
Nota: le istruzioni impiegate copiano tutto: dati e formati, compreso i bordi e il colore celle. allego file d'esempio da scaricare e utilizzare : EsempioCB.xls
http://ennius.interfree.it/
20 Kb
Pagina 312
MANUALE VBA
X EXCEL
Passare da Stringhe a Formule Quando le formule hanno bisogno di una rappresentazione visiva, ma si vuole anche che vengano tradotte in formule per eseguire il calcolo, cominciano i dilemmi, perchè se scegliamo da Strumenti/Opzioni/Visualizza e spuntiamo la casella di "Formule", vedremo, sul foglio di lavoro, le formule, ma non i risultati. Questo esempio lo presento a seguito di una domanda rivoltami , e può interessare tutti quei lavori dove si debba illustrare il calcolo che porta ad un risultato, come per esempio preventivi o fatture di/per lavori nel campo edilizio, dove si è usi fornire le spiegazioni di come un risultato si è ottenuto, attraverso la scrittura della formula che l'ha generato. E qui casca l'asino quando si voglia automatizzare il calcolo. Infatti il problema di far accettare una formula come descrizione, non esiste, in quanto è sufficiente scegliere da "Formato Cella" il formato Testo, e tutto ciò che verrà scritto nella cella sarà da excel considerato sempre e soltanto testo (cioè una stringa di testo), ma esiste il problema di far capire ad excel che nella cella accanto (per esempio) a quella che contiene la stringa testo, vogliamo che avvenga il calcolo rappresentato dalla stessa stringa testo. Ipotizziamo di avere in A1 la visualizzazione della formula come stringa, ed in B1 di volere il conteggio generato dalla formula. Sarà inutile tentare con formule (in B1) tipo =A1 oppure ="="&A1 perchè otterremo solo una copia della stringa. Si può ovviare alla bisogna con l'utilizzo di una riga di codice, inserita in una macro, oppure in un evento tipo Change del Foglio di lavoro, usando un'istruzione così: Range("B1").Formula = "=" & Range("B1").Offset(0, -1) oppure Range("B1").Formula = "=" & Range("A1") Il nocciolo dell'istruzione è "=" & che rende in B1 il contenuto di A1 preceduto dal segno uguale e quindi formula. Due precisazioni importanti e necessarie: La descrizione della formula in A1 (stringa) dovrà essere scritta SENZA il segno di uguale come in questo esempio: (4.00+3.15+2.22)*2.55 e NON =(4.00+3.15+2.22)*2.55 , pena un errore del debugger che non riconosce il dato per la presenza finale di due segni di uguale. Nella descrizione della formula in A1 (stringa) per indicare i decimali, DOVRA' essere usato il PUNTO al posto della VIRGOLA, come si usa nella sintassi inglese. Ricordo che l'operazione di conversione da stringa a formula, avviene attraverso il codice Vba, che "ragiona" in inglese: se gli si fornisce una stringa da interpretare dove i decimali sono virgole, il Vba non la riconosce e si genera un errore. Excel poi convertirà il risultato fornito dal codice in sintassi italiana, con le virgole al posto dei punti: sarà infatti sufficiente controllare la formula in B1 per vederla così: =(4+3,15+2,22)*2,55 Sotto vediamo meglio un esempio di un foglio di lavoro normalmente usato per il "COMPUTO METRICO", dove si fà appunto uso di caselle (Colonna E9) che ospitano numeri che rappresentano misure da usare per ottenere le quantità (Colonna G). E' in questa colonna (G) dove l'istruzione contenuta nella macro, deve effettuare la conversione il formula della stringa numeri della cella E.
Poichè su fogli di questo tipo si preferisce preimpostare una certa lunghezza di righe, sarà necessario predisporre una formula per ogni riga, ma nel caso che si salti una riga per ottenere una impostazione http://ennius.interfree.it/
Pagina 313
MANUALE VBA
X EXCEL
grafica gradevole, le celle nella colonna G (che non trovano stringhe da convertire nelle celle relative alla colonna E) porterebbero dei segni di uguale che esteticamente non sono graditi. Si puè predisporre una routine che controlli se esistono dati nelle celle colonna E, e solo in questo caso, svolgano la conversione. Queste istruzioni eseguono un ciclo di ricerca dal Range E7 fino alla E201, e se trovano stringhe numeri, le convertono in formula, altrimenti non eseguono la conversione. Sub calcola2() On Error Resume Next Dim CL As Object For Each CL In Range("E7:E201") If CL = "" Then GoTo 10 Else CL.Offset(0, 2).Formula = "=" & CL End If 10: Next Resume End Sub Buon lavoro.
http://ennius.interfree.it/
Pagina 314
MANUALE VBA
X EXCEL
Sub Totali in vba - come ottenerli. (28/06/03) I questo esercizio che presento, vedremo come applicare dei subtotali ad un elenco di dati, nei campi (colonne) che ospitano valori numerici. Vedremo anche la stessa tecnica applicata ad un elenco filtrato, e come evidenziare in grassetto i caratteri dei subtotali, cambiando anche colore sia ai fonts sia alle celle. Oltre a svariate procedure che possiamo realizzare con istruzioni appropriate in vba, questa volta seguiremo una procedura tipica del foglio di lavoro, tradotta in codice vba, che è quella che possiamo ottenere tranquillamente (senza bisogno del codice) andando sul menù Dati e scegliendo l'opzione "Subtotali". Questa procedura da foglio è già stata descritta su questo sito, sezione "primi passi", articolo "Usare il filtro". Ottenere subtotali è una necessità che si può avere quando si disponga di elenchi dati di cui vogliamo dei parziali relativamente ad un periodo (es: una data di fine mese in un elenco fatture), oppure ad un nominativo, oppure a quanto venduto di un determinato articolo, ecc. ecc. Le necessità di ognuno sono tante che è impossibile esemplificarle, l'importante è afferrare il concetto. Vediamo una tabella creata come esempio:
In questa tabella troviamo 5 campi: data fatt, nominativo, imponibile, iva, totale (imp. + iva); se si dovesse identificare i campi con un numero (che in una tabella parte sempre da sinistra), data fatt sarebbe il numero 1, nominativo il numero 2, ecc. ecc.; parlo di numeri e non di intestazioni di campo, perchè gli "argomenti" del metodo vba "Subtotal" che useremo, identificano i campi su cui agire in base al "numero di campo". Dovremo altresì decidere in base a quale "Gruppo" ottenere i subtotali (per gruppo si intendono ognuno dei campi a disposizione), cioè in questo esempio, dovremo decidere se vorremo i subtotali organizzati per il "Gruppo" "Data fatt" oppure "Nominativo", visto che mi sembra assurdo volerlo per gli altri campi, di cui invece vorremo la SOMMA. Vediamo intanto la sintassi del Metodo Subtotal: (tra parentesi gli argomenti separati da una virgola) espressione.Subtotal(GroupBy, Function, TotalList, Replace, PageBreaks, SummaryBelowData) (invito i "pellegrini" ad abituarsi a consultare la "guida in linea (vedi)" digitanto Subtotal in "cerca" e selezionando "Metodo Subtotal" nella finestra degli argomenti trovati, per leggersi i significati relativi agli argomenti) Ed ora vediamo due esempi della procedura, nella quale organizzeremo i Subtotali raggruppati prima per data fatt. e poi per nominativo, con relative immagini: Sub ApplicaSubtotali() 'per prima cosa identifichiamo l'area, compreso le intestazioni di campo, su cui agire. 'Poichè si presuppone che non sapremo quanto lungo sarà il nostro elenco, usiamo End 'per reperire l'ultima cella occupata lato destro della tabella, ed assegniamo l'area alla 'variabile "zona": Set zona = Range(Cells(3, 1), Cells(3, 5).End(xlDown)) 'ora applichiamo a "zona" i subtotali, organizzandoli per "Data fatt.", campo n. 1 (GroupBy:=1), usando la funzione Somma (Function:=xlSum), e chiedendo il subtotale ai campi 3, 4 e 5 (imponibile,iva, totale) con (totallist:=Array(3, 4, 5)). L'istruzione sotto è 'tutta unica, si va a capo con _ (barra bassa) per mancanza di spazio http://ennius.interfree.it/
Pagina 315
MANUALE VBA
X EXCEL
zona.Subtotal GroupBy:=1, Function:=xlSum, totallist:=Array(3, 4, 5), _ Replace:=True, PageBreaks:=False , SummaryBelowData:=True End Sub e questa è l'immagine del risultato in cui si vedono i subtotali organizzati in funzione delle date uguali :
Vediamo subito anche un'altra routine necessaria : la rimozione dei subtotali, una volta analizzati i risultati. Capire queste istruzioni è facile: si identifica l'area a cui si sono applicati i subtotali (zona) e si rimuovonocon RemoveSubtotal: Sub RimuoviSubtotali() Set zona = Range(Cells(3, 1), Cells(3, 5).End(xlDown)) zona.RemoveSubtotal End Sub Importante: questo risultato con i subtotali organizzati per Gruppi di date è stato ottenuto grazie all'ordinamento progressivo ascendente con cui le fatture sono state registrate, cioè ogni fine mese, e quindi le date uguali sono una seguente all'altra. Ma cosa succederà quando andremo ad applicare il subtotale sul campo"Nominativo", che non porta i nominativi "raggruppati", ma distesi nei vari mesi? Succederebbe che per ogni nominativo verrebbe fornito un subtotale, in quanto cambia il nominativo ad ogni riga, ed otterremmo un elenco molto lungo di subtotali che non servirebbero allo scopo. E allora? Allora impariamo una regola che dovremo tenere sempre presente: dovremo "raggruppare" i Nominativi (in questo caso) usando un ordinamento che ponga in ordine alfabetico i nominativi (e quindi raggruppandoli), e i loro dati correlati. In questo modo è come se applicassimo un filtro che riunisca tutti i valori uguali, solo che anzichè filtrare un nominativo per volta, li "filtriamo" tutti. Inseriremo le istruzioni per l'ordinamento PRIMA delle istruzioni per i subtotali, ed il gioco è fatto. Vediamo le istruzioni e l'immagine del risultato: Sub ordinaapplicaSt() 'impostiamo la zona su cui agire Set zona = Range(Cells(3, 1), Cells(3, 5).End(xlDown)) 'sotto; con la zona, applichiamo l'ordinamento ascendente sul campo Nominativo, che 'inizia come dati dalla cella B4 With zona .Sort Key1:=Range("B4"), Order1:=xlAscending, Header:=xlGuess, _ OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom End With 'indi usiamo il metodo Subtotal agendo sul GroupBy:=2 , cioè il campo Nominativo zona.Subtotal GroupBy:=2, Function:=xlSum, totallist:=Array(3, 4, 5), _ Replace:=True, PageBreaks:=False, SummaryBelowData:=True End Sub e questo sarà il risultato: http://ennius.interfree.it/
Pagina 316
MANUALE VBA
X EXCEL
Anche qui vorremo rimuovere i subtotali, ma dovremo seguire una strada diversa rispetto alle istruzioni per la rimozione vista sopra in quanto abbiamo modificato anche l'ordinamento di tutti i dati. In questo caso, dovremo ricostruire l'elenco come era in origine, impostato sulle date fattura ed useremo ancora un ordinamento ascendente ma basato questa volta sulla chiave "Data fatt.", cioè sulla colonna A a partire dalla cella A4, e queste le rispettive istruzioni, ma con l'istruzione RemoveSubtotal posta PRIMA di quella sull'ordinamento: Sub RimuoviordinaapplicaSt() Set zona = Range(Cells(3, 1), Cells(3, 5).End(xlDown)) zona.RemoveSubtotal With zona .Sort Key1:=Range("A4"), Order1:=xlAscending, Header:=xlGuess, _ OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom End With End Sub Questa procedura in realtà non ripristina la tabella proprio come era all'origine, in quanto si avrà un ordinamento progressivo sulle date, ma con nominativi posti in ordine alfabetico; nessun danno perchè gli importi relativi ad ogni nominativo saranno quelli giusti, ma.....esiste un'altro modo, che dovremo prevedere quando creeremo i campi di una tabella: lasceremo la prima colonna, la A, come colonna contatore, partendo quindi da uno incrementando una unità per ogni riga. In questo modo, se applicheremo ordinamenti basati su date, o nominativi, o altro, potremo sempre ritornare all'ovile scegliendo come chiave d'ordinamento per il ripristino, la colonna con i numeri, ed i dati ritorneranno veramente come prima. Come promesso in apertura articolo, ora vediamo come colorare caratteri, renderli grassetto, e colorare le celle che ospiteranno i sub totali. Vediamo subito un immagine che chiarisce cosa intendo, e poi la routine e le spiegazioni:
http://ennius.interfree.it/
Pagina 317
MANUALE VBA
X EXCEL
Per la routine useremo una macro a parte, che richiameremo a fine di una delle precedenti routine, in modo che applicheremo l'ordinamento, i sub totali e infine questa: Sub ColoraSt() Dim CL As Object 'sotto: settiamo come zona SOLO le celle della colonna sulla quale stiamo applicando i 'subtotali, nell'esempio sopra, la colonna 2 (nominativo) dalla B3 all'ultima cella occupata Set zonac = Range(Cells(3, 2), Cells(3, 2).End(xlDown)) 'inizia il ciclo per ogni cella in zonac For Each CL In zonac 'per identificare su quale riga si formeranno i subtotali (non possiamo predeterminarlo 'prima), cerchiamo la parola "Totale", che può trovarsi la prima a destra o l' ultima a 'sinistra nelle celle che spazzoliamo con in ciclo For Each, se "Totale" viene trovato, 'allora: If Right(CL.Value, 6) = "Totale" Or Left(CL.Value, 6) = "Totale" Then 'con la cella (CL) così trovata, impostiamo il colore rosso al font ed il grigio nella cella: With CL .Font.ColorIndex = 3 .Interior.ColorIndex = 15 'con Offset(0, 1) identifichiamo la cella a destra di quella trovata e coloriamo, 'aggiungendo il Grassetto al font .Offset(0, 1).Font.ColorIndex = 3 .Offset(0, 1).Interior.ColorIndex = 15 .Offset(0, 1).Font.Bold = True 'lo stesso facciamo con la cella : due celle a destra (Offset(0 , 2)) .Offset(0, 2).Font.ColorIndex = 3 .Offset(0, 2).Interior.ColorIndex = 15 .Offset(0, 2).Font.Bold = True 'e quindi con la terza (Offset(0 , 3)) .Offset(0, 3).Font.ColorIndex = 3 .Offset(0, 3).Interior.ColorIndex = 15 .Offset(0, 3).Font.Bold = True End With ' fine con End If Next 'passa alla cella successiva fino alla fine delle celle in zonac End Sub http://ennius.interfree.it/
Pagina 318
MANUALE VBA
X EXCEL
E quindi la routine "ordinaapplicaSt" vista sopra, con l'aggiunta del richiamo alla macro ColoraSt, diventa così: Sub ordinaapplicaSt() 'impostiamo la zona su cui agire Set zona = Range(Cells(3, 1), Cells(3, 5).End(xlDown)) 'sotto; con la zona, applichiamo l'ordinamento ascendente sul campo Nominativo, che 'inizia come dati dalla cella B4 With zona .Sort Key1:=Range("B4"), Order1:=xlAscending, Header:=xlGuess, _ OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom End With 'indi usiamo il metodo Subtotal agendo sul GroupBy:=2 , cioè il campo Nominativo zona.Subtotal GroupBy:=2, Function:=xlSum, totallist:=Array(3, 4, 5), _ Replace:=True, PageBreaks:=False, SummaryBelowData:=True ColoraSt 'si richiama la macro per colorare fonts e celle End Sub Quando si rimuoveranno i subtotali, usando la macro RimuoviordinaapplicaSt() spariranno anche le impostazioni di colore e grassetto, senza bisogno di aggiungere istruzioni. Ognuno potrà scegliersi i colori che preferirà semplicemente cambiando il valore assegnato a ColorIndex. (per la tabella Colori vedi in questa sezione : "Colori e ColorIndex"). Buon lavoro.
http://ennius.interfree.it/
Pagina 319
MANUALE VBA
X EXCEL
Suonare con Excel. (13/06/03) - Aggiornato al 22/08/03 Far emettere suoni ad Excel può essere utile quando si voglia essere avvisati se per esempio in una cella compare un determinato valore, testo, numero o data che sia. Sembra però che i progettisti di Excel abbiano deciso di fare a meno di suoni. Si legge infatti, nella guida in linea a proposito della proprietà CanPlaySounds : "Questa proprietà non deve essere utilizzata. Le note sonore sono state eliminate da Microsoft Excel." Pare che gli unici suoni gestiti tramite Excel, siano i suoni legati al famoso "Assistente di Office", infatti, sempre nella guida in linea, alla proprietà Sounds, viene riportato il seguente esempio: "In questo esempio l’Assistente di Office viene visualizzato, animato e messo in grado di riprodurre suoni.": With Assistant .Visible = True .On = True If Not Sounds Then Sounds = True .Animation = msoAnimationGreeting End With Esiste tuttavia la possibilità di eseguire file .wav, sfruttando una libreria di Sistema, la "winmm.dll". Vediamo come fare: Inseriamo, nella sezione "Generale - Dichiarazioni" di un Modulo la seguente dichiarazione: (è tutta un'istruzione anche se su due righe, se copiate NON dimenticate il trattino basso _) Declare Function sndPlaySound32 Lib "winmm.dll" Alias "sndPlaySoundA" _ (ByVal lpszSoundName As String, ByVal uFlags As Long) As Long Creiamo (nello stesso Modulo) e diamo il nome alla macro che ospiterà il richiamo alla Funzione sndPlaySound32. Come argomento della funzione, dovremo scrivere il percorso completo ed il nome del file .wav da suonare. Ognuno scriverà il proprio, come esempio ho usato C:\Temp ed il nome di in file copiato lì da Windows\Media (la cartella dei suoni di sistema). Potrete usare anche suoni registrati da voi, con la vostra voce, usando l'utility "Registratore di suoni" di Windows. Unico suggerimento: poichè durante l'esecuzione della macro non sarà possibile eseguire nessuna operazione sul foglio di lavoro, consiglio di utilizzare suoni brevi per non bloccare il foglio troppo a lungo. Sub MioSuono() Call sndPlaySound32("C:\Temp\ringin.wav", 0) End Sub Ora sceglieremo un evento per inserire l'istruzione che farà scattare in automatico l'esecuzione del suono se si verificherà una certa condizione: nell'esempio sotto ipotizzo che se nella cella A1 compare la parola "pippo" (attenzione alle maiuscole/minuscole (*)), si attiva il suono tramite il richiamo della macro su esposta (MioSuono): Private Sub Worksheet_Change(ByVal Target As Range) If Range("A1").Value = "pippo" Then MioSuono End If End Sub * Ricordo che per ovviare all'inconveniente delle maiuscole/minuscole, quando si fa uso di "testo" nel codice, possiamo inserire nella sezione "Generale - Dichiarazioni" (del Worksheet in questo caso) l'istruzione: Option Compare Text Aggiornamento Nasce un problema quando si voglia inviare la cartella con il file vaw ad un amico, o spostare il file vaw in una cartella diversa : cambiando il percorso che mira al file vaw, il file non verrà trovato e quindi niente suono. Ma esiste la possibilità di "incorporare" il file vaw nella cartella in modo che diventi un tutt'uno con il file sonoro. Questa soluzione è stata suggerita da Stefano Luise, e-mail [email protected] e questa la sua spiegazione: Ho trovato questa routine di un certo Tiziano Marmiroli - Microsoft MVP - Office ([email protected]) e segnalo il modo per farlo facendo sì che il file sia unico: Menu Inserisci> Oggetto> Da file e inserisci il tuo file wav. Prendi nota del nome che Excel gli assegna (es. "Oggetto 1"). http://ennius.interfree.it/
Pagina 320
MANUALE VBA
X EXCEL
Da Visual Basic Editor: --------------------------------------------Private Sub Workbook_Open() Sheets("Foglio1").Shapes("Oggetto 1").Select Selection.Verb Verb:=xlPrimary End Sub 'sostituire a "Oggetto 1" il nome dell'oggetto e 'a "Foglio1" il nome del foglio che lo contiene --------------------------------------------All'apertura del foglio parte il suono. Ciao, Stefano Un grazie a Stefano. Buon lavoro.
http://ennius.interfree.it/
Pagina 321
MANUALE VBA
X EXCEL
Le TextBox nelle UserForm e il SetFocus. Quando si usano delle istruzioni in vba per controllare se i dati immessi in una TextBox sono corrispondenti alle nostre esigenze, o non restino vuote (senza dati), è necessario utilizzare degli "eventi" che attivino questi controlli, eventi legati ad "oggetti" presenti sulla UserForm. In genere io uso l'evento Click di un CommandButton, al quale faccio prima controllare l'esattezza dei dati ammessi nelle TextBox, e se tutto è Ok, l'esecuzione delle istruzioni previste per quell'evento. Tipico esempio quello sotto. Private Sub CommandButton1_Click() If TextBox1 = "" then 'Se la textbox1 è vuota MsgBox "Inserire i dati richiesti" 'fai apparire questo messaggio TextBox1.SetFocus 'riporti il focus sulla TextBox1 Exit Sub 'esci dalla routine e non esegui le istruzioni sottostanti End If Range("A1") = TextBox1 'in caso la textbox1 contenga dati, esegue questa istruzione End Sub Come vediamo, usiamo l'istruzione SetFocus per riportare il focus sulla textbox interessata. Poichè l'esecuzione di questa istruzione è affidata all'"oggetto" CommandButton che in questo momento ha il focus, il codice esegue il comando senza fallire. Questo perchè dopo aver generato l'evento Click(Index As Long) il controllo attivo passa all'"oggetto" immediatamente successivo sulla userform, che possieda la proprietà "TabIndex" . ( Quando si inseriscono oggetti sulla userform, tutti gli oggetti inseriti che possiedono questa proprietà, ricevono un indice progressivo assegnato dal programma, il valore indice (TabIndex) inizia con zero (0) assegnato al primo oggetto, e si incrementa di un valore per ogni altro oggetto inserito. E' quindi questo valore che determina la sequenza di selezione oggetti generata per esempio, dal tasto Invio.) Ritornando al nostro Click, l'istruzione TextBox1.SetFocus Porta il focus sull'oggetto indicato anzichè lasciarlo all'oggetto previsto dal successivo valore presente nel TabIndex del CommandButton. E dopo questa lungagnata, "Non mi faccio un brodo? Ma me lo faccio doppio!" Come diceva un comico in una reclame televisiva. E' successo, e non me ne ero mai accorto, che se invece di un evento Click di un CommandButton, si voglia scegliere un evento proprio della stessa TextBox1 per ottenere questo benedetto controllo immissione dati, siamo necessariamente portati ad utilizzare due eventi tipici: AfterUpdate oppure Exit. Però entrambi questi eventi, sembra non intercettino un istruzione come TextBox1.SetFocus in quanto l'evento stesso è successivo alla lettura dell'istruzione, e come tale segue la normale procedura di selezione dell'oggetto successivo rappresentato dal suo TabIndex. In parole povere, non si riesce a far ritornare il focus sulla TextBox1. (Questa istruzione in effetti viene eseguita, ma è tanto rapida che non la vediamo, vedremo solo ciò che Exit o AfterUpdate fanno alla fine: quella di spostare il focus sull'oggetto successivo). Tra l'altro, per un esempio come quello citato all'inizio, dove si voglia controllare se una textbox rimane vuota, l'evento AfetUpdate non interverrebbe comunque: AfterUpdate vuol dire: "dopo l'aggiornamento", ma se in una textbox non scriviamo niente, non l'abbiamo "aggiornata" e quindi l'evento non viene attivato. Rimane quindi un'unica possibilità, se vogliamo usare un evento di una textbox, quello di scegliere l'evento Exit . E non sarà più necessario usare un istruzione SetFocus, ma sfruttare il metodo Cancel dell'evento Exit con una semplice istruzione come questa: Cancel = True Questa istruzione equivale ad interrompere l'uscita dalla textbox (exit) e lascia il focus sulla textbox stessa. Andrà posizionata subito sotto l'istruzione If...Then in modo che si interrompa l'uscita riposizionando lo "Stato Attivo" (focus) sulla textbox, e possano essere eseguite le successive istruzioni. Presento un esempio nel quale si controlla che nella textbox si inseriscano solo numeri e non lettere o numeri e lettere (IsNumeric) e un controllo che impedisca l'immissione di valori negativi, il tutto con la segnalazione di un messaggio di "Errato", e la selezione del valore immesso come non valido. Questo è l'esempio: Private Sub TextBox1_Exit(ByVal Cancel As MSForms.ReturnBoolean) Dim MyVar, MyCheck, Search, SearchString, where Dim X X = Mid(TextBox1, 1, 1) 'legge il primo carattere nel textbox MyVar = TextBox1.Value 'Assegna il valore alla variabile. MyCheck = IsNumeric(MyVar) ' Restituisce Vero. http://ennius.interfree.it/
Pagina 322
MANUALE VBA
X EXCEL
If MyCheck = False Or X = "-" Then 'se non è un numero o se inizia col segno meno, allora Cancel = True 'QUESTO è IL PUNTO IN CUI POSIZIONARE CANCEL SearchString = TextBox1.Text 'text che contiene i dati Search = TextBox1 'text con parola da cercare where = InStr(SearchString, Search) If where Then TextBox1.SelStart = where - 1 ' l'inizio della selezione e TextBox1.SelLength = Len(Search) ' la lunghezza. 'TextBox1.SetFocus MsgBox "Errato !!" End If Else TextBox2.SetFocus End If End Sub Se sarò stato di aiuto per qualcuno, tanto meglio. Buon lavoro.
http://ennius.interfree.it/
Pagina 323
MANUALE VBA
X EXCEL
Traduttore Funzioni Italiano-Inglese e viceversa. Dopo varie richieste presento un traduttore delle Funzioni da italiano a inglese e viceversa, che potrà aiutare nel reperire la corrispondenza nelle due lingue. Il database è sul foglio2, e potrà essere ampliato a piacimento, basterà poi variare i riferimenti ai nomi che identificano le aree che formano il RowSource delle ListBox. Sotto un immagine:
File scaricabile e consultabile : Tradfunzioni2000.zip
http://ennius.interfree.it/
42 Kb
Pagina 324
MANUALE VBA
X EXCEL
La Funzione Trim - precisazioni (23/06/03) Abbiamo già visto, nell'articolo "Funzioni Stringa" in questa sezione, come lavora la funzione TRIM. La Funzione Trim serve per rendere un valore stringa (cioè del testo) SENZA spazi iniziali o finali (con le Funzioni Trim non vengono eliminati gli spazi "intermedi" ). Cioè se in una cella ci troviamo con una stringa tipo: Procopio Lacustre applicando la funzione Trim ci troveremmo ad aver eliminato gli spazi prima di Procopio e dopo Lacustre, MA non avremmo eliminato gli spazi tra Procopio e Lacustre, ed avremo: Procopio Lacustre E fin qui niente di nuovo, sotto il sole. Ma la funzione TRIM possiede una sorella gemella che corrisponde all'italiano ANNULLA.SPAZI . Anche qui niente di nuovo; quasi tutte le funzioni disponibili (in italiano) sul foglio di lavoro, hanno le corrispondenti funzioni in inglese da usare con il codice vba, e viceversa. Solo che la funzione Annulla.Spazi fà qualcosa di diverso dalla sorella Trim. Leggiamo cosa dice la guida in linea in merito alla funzione Annulla.Spazi : "Rimuove tutti gli spazi dal testo ad eccezione dei singoli spazi tra le parole. Utilizzare la funzione ANNULLA.SPAZI sul testo creato con altre applicazioni che può presentare una distribuzione irregolare degli spazi." Quel "singoli" è la chiave che differenzia le due funzioni, che dovrebbero operare in uguale maniera, ma se si adopera la funzione Annulla.Spazi, la frase sopra diventa così: Procopio Lacustre Anche un sordo nota che in questo caso sono stati eliminati tutti gli spazi vuoti, tranne uno tra Procopio e Lacustre. E questo alla faccia di noi "pellegrini" che troppo spesso diamo per scontato che le cose simili si comportino similmente, o quanto meno erriamo nel considerarle uguali. In realtà le due funzioni devono essere usate con modalità diverse. La funzione Trim, via codice, ci consente di modificare il testo contenuto in una cella, AGENDO direttamente sulla cella stessa, esempio: Sub miaElido() Range("A1") = Trim(Range("A1")) End Sub In questo caso verranno eliminati solo gli spazi iniziali e finali, ma non intermedi, e lo facciamo direttamente nella cella stessa. Per vederla lavorare come lavora Annulla.Spazi, la funzione Trim va trasformata in FORMULA. Come tale, però, non potrà essere inserita nella stessa cella che si vuole "trimmare" (si genera un errore di tipo "riferimento circolare") ma dovremo usare un'altra cella per ospitare la cella "trimmata", così. Sub miaElidos() Range("A2").Formula = "=TRIM(A1)" End Sub Ed avremo in A2 il nome senza spazi tranne lo spazio tra le due parole. Mi sembrava doveroso far notare le diversità nell'uso che si fa della stessa funzione, italiana o inglese che sia. Buon lavoro.
http://ennius.interfree.it/
Pagina 325
MANUALE VBA
X EXCEL
Triplo Ciclo For .. Next I cicli For ... Next sono legati al concetto di insiemi. Questi insiemi possono essere oggetti di vario tipo : controlli, barre di comando, celle del foglio di lavoro, fogli di lavoro, ma anche matrici, valori, ecc. ecc. Quando si deve eseguire una verifica o una ricerca su ogni componente di un insieme, come per esempio cercare un valore in una serie di celle (Range), allora e utile ricorrere proprio alla struttura di un ciclo For ... Next. Sono due tra le strutture cicliche di controllo (ne esistono altre) : un ciclo che cercherà PER OGNI componente dell'insieme, usando la forma For Each di un ... Next 0 Then zona.ClearContents For I = 2 To zona.Cells.Count - cont zona.Cells(1) = 1 zona.Cells(I) = zona.Cells(I - 1) + 1 Next End If End Sub Buon lavoro.
http://ennius.interfree.it/
Pagina 329
MANUALE VBA
X EXCEL
Trovare il Path Tutte le seguenti macro dovranno essere inserite in un modulo ed associate ad un relativo pulsante. Per avere in percorso completo del file attualmente aperto in Excel: Sub DimmiPath() MsgBox "Il path è " & ThisWorkbook.Path End Sub Per trovare un file o tutti i file data una cartella e una estensione. Nell'esempio sotto, verranno cercati tutti i file con estensione .xls ( .Filename = "*.xls" ) nella cartella Documenti ( .LookIn = "C:\Documenti" ) . Verrà riportato il numero di quanti file ci sono ed il loro nome, in ordine alfabetico. Sub Trovafile() H
H
Set fs = Application.FileSearch With fs .LookIn = "C:\Documenti" .Filename = "*.xls" If .Execute > 0 Then MsgBox "There were " & .FoundFiles.Count & _ " file(s) found." For i = 1 To .FoundFiles.Count MsgBox .FoundFiles(i) Next i Else MsgBox "There were no files found." End If End With End Sub Una variante alla precedente routine, è la seguente, di cui riporto solo il passo da modificare. Nella precedente macro, la cartella in cui eseguire la ricerca è inserita nel codice, e quindi fissa. Per eseguire la ricerca in altre cartelle sarebbe necessario modificare il codice. La variante che vi sottopongo consentirà di scegliere la cartella in cui eseguire la ricerca, semplicemente scrivendo il percorso completo della cartella, in una cella del foglio di lavoro, ad esempio in A1 (dove scriveremo, sempre per esempio, C:\MiaCartella) Set fs = Application.FileSearch With fs .LookIn = "Range(A1)" 'parte da modificare Per avere l'elenco di tutti i file .xls presenti nella cartella dove risiede il file attualmente aperto in Excel Sub QualiFile() With Application.FileSearch For i = 1 To .FoundFiles.Count MsgBox .FoundFiles(i) Next I End With End Sub Per avere il percorso (path) predefinito utilizzato da Excel per aprire i file. Sub Default() MsgBox "L'attuale percorso predefinito è " & _ Application.DefaultFilePath End Sub H
http://ennius.interfree.it/
H
Pagina 330
MANUALE VBA
X EXCEL
Utilizzare le InputBox per inserire dati sul Foglio di lavoro. Spesso vorremmo disporre, per introdurre dati su determinate celle del foglio di lavoro, di una maschera che raccolga il dato da immettere, e lo "scarichi" poi in una cella per attivare le procedure presenti sul foglio stesso. Queste maschere di introduzione dati, altro non sono che "Finestre di Dialogo" (le ImputBox). Ecco una semplice routine, da inserire in un modulo e da associare ad un pulsante che attivi l'InputBox. Nell'esempio utilizzo la Cella A1 come destinazione del dato inserito, potrete modificare il riferimento per la cella che più vi aggrada. In verde sono i commenti : Sub Richiesta() Dim Message, Title, MyValue 'sotto: Imposta il messaggio. Message = "Inserisci Quel che Vuoi :" Title = "Inserimento Dati" ' Imposta il titolo. ' Visualizza il messaggio, il titolo MyValue = InputBox(Message, Title) If MyValue "" Then 'se l'inputbox è diverso da vuoto Range("A1").Value = MyValue 'allora copia in A1 il testo End If End Sub E' possibile utilizzare la stessa InputBox per inserire più dati in celle diverse, se i dati corrispondono a valori predeterminati. Ipotizziamo di dover introdurre 3 tipi di dati condizionali (SE) in tre diverse celle. In A1 vorremo numeri solo SE compresi tra 1 e 100, in A2 sempre numeri solo SE compresi tra 200 e 300, ed infine in A3 vorremo dati solo SE sono testo. Ricordo che, anche qui, i riferimenti e i valori condizionali potranno essere adattati alle vostre esigenze, compreso l'inserimento, tramite ElseIf ......Then di nuove istruzioni prima di End If. Sub multi() Dim Message, Title, MyValue Message = "Inserisci Quel che vuoi :" Title = "Inserimento Dati" MyValue = InputBox(Message, Title) If MyValue >= 1 And MyValue = 200 And MyValue