Il blog di Gianni Giaccaglini

Blog su VBA e VSTO
Gianni Giaccaglini

My Links

News

NB - V. anche gli ARTICOLI (in fondo a questa barra)
Solo quesiti validi a: giannigiac@tin.it
Il mio nuovo libro


La mia nipotina ELISA

Foto con dedica a ME di
Bill Gates giovanissimo
nei mitici anni 80!

Categorie Post

Categorie Articoli

Archivio

Immagini

Blog Stats

mercoledì 23 giugno 2010 #

Recensione di un utile manuale MANNING su WPF (in inglese, perché approvato dall'editore)

A very useful and complete WPF handbook, by Manning

The book from MANNING I am going to review:

 

WPF in Action with Visual Studio 2008

By Arlen Feldman and Maxx Daymon

November, 2008 - 520 pages

Printed book: $ 44.99

E-book PDF: $ 27.50

http://www.manning.com/feldman2/

In this site two free PDF files can be download:
Chapter 2 (introduction to PDF) and Chapter 7
0,8 MB of source code can be dowloaded

WPF (Windows Presentation Foundation) is a technology Microsoft created in order to encourage a next generation of Window Forms (and of Web pages, created with SilverLigth) with a lot of enhancements in terms of multimedia features and structural ones, too . It is not very new, for its first edition was issued in Windows 2005, but till now it was adopted by not many developers, in Italy. The main reason is clear, in my opinion: WPS implies a paradigm strongly different with respect of classic Windows Forms, so people has much work to do and much time to spend for learning and mastering it.

The new XAML declarative language, derived from XML, in conjunction with imperative “code behind” in C# or VB languages, is appealing but you have not only to change mentality, what is obvious, but you also need understand a lot of subtle concepts as relative margins and many controls containers - StackPanel, Grid, DockPanel, Canvas etc. – and their combinations!, to mention only the first ones a new user meet with.

There are two other practical difficulties:

  • the project pane, astonishing in itself, is hard in use (so I like better do the job in the XAML…);
  • Intellisense often presents too many options, without a good filtering of those correct in a given context.

Microsoft supports WPF with many articles and posts in MSDN etc. but what is most important is a systematic BOOK ON WPF or on VISUAL STUDIO 2010 with large chapters onWPF. Till now none is in Italian and this situation will continue in the future, in my opinion.

NOTE – Visual Studio 2010 by Alessandro Del Sole, from SAMS, is a good choice for everybody interested to a completed and in depth description both of VS 2010 and WPF:
http://www.amazon.com/Visual-Basic-2010-Unleashed-Alessandro/dp/0672331004
In other similar handbook, attention is to be paid, because only general and often generic treatment is devoted to WPF…

WPF in action in two word

Written in a witty, engaging style, WPF in Action with Visual Studio 2008 can be read cover-to-cover or used to reference specific problems and issues. The approach is systematic and rational, but at the same time it start from practical and always focused on how you'll use WPF in real development scenarios. You'll learn how to handle the many new issues presented by the extreme flexibility of WPF. The authors also provide numerous tips and suggestions for how to work efficiently.

MAIN CONTENTS:

  • WPF using Visual Studio 2008
  • Real-world example applications
  • Tips and techniques
  • Advice for Windows Forms developers
  • Drawing and animation
  • Command handling and data-binding
  • XBAP and ClickOnce

 

Visiting the Manning site you can find many frequently asked questions
http://www.manning.com/about/faq.html

Finally a little example from the Manning ‘s book:

The abstract Drawing class in .NET has several different derivations for different purposes.

 

Types of Drawings

Class Purpose

 

ü  ImageDrawing Used to hold a graphic, such as a bitmap. This is the low-level, lower-overhead class to use to hold an Image—versus the Image class, which has all the event support, layouts, and so on;

ü  VideoDrawing Lets you “draw” a video clip that can be played, started, stopped, and so on;

ü  GeometryDrawing A drawing made up of various shapes such as curves. This is what was used to create the magnifying glass from figure 14.13;

ü  GlyphRunDrawing Lets you draw text with extreme accuracy—you’d use this if you were going to build a typesetting application;

ü  DrawingGroup Groups multiple drawings together.

 

These different types of drawings can be defined in XAML. An ImageDrawing might look like this:

<ImageDrawing Rect="0,0,100,100" ImageSource="C:\WINDOWS\Web\Wallpaper\Bliss.bmp"/>

?>

posted @ 21.02 | Feedback (0)

domenica 20 giugno 2010 #

Col Clock si prolunga la vita di molti applicativi! Più un FINALE MONDIALI sviluppato in WPF

ULTIM'ORA. Sto studianndo WPF (Windows Presentation Manager) che in Visual Studio permette di creare finestre molto più multimediali rispetto alle Winndows Form. Questa mia creatura sviluppata in occasione dei Mondiali sudafricani sfrutta la griglia (Grid) WPF con caselle di testo disposte secondo lo schema a piramide rovescia, a partire dai QUARTI di finale. Secondo un algoritmo da me più volte sfruttato con Excel + VBA il primo pulsante avvia una simulazione basata su numeri randommizzati attorni ai VALORI stimati. I nomi delle squadre sono generici, i valori sono livellati sul 6 ma gli uni e gli altri si possono modificare prima del clic. Un'imperfezione (che un bel dì sanerò, ma non so quando...) impedisce di tener conto di  modifiche ai valori successive alla prima, comunque cliccando sul secondo pulsante ci si mette una pezza.

Scaricare da:

http://www.giannigiaccaglini.it/download/FinaliCampionato.zip

Tornando al Clock, si tratta di un trucco vergognoso, ma stavolta la vergogna mi pare pertinente a pur nobili produttori di software. Applicativi due tipi, principalmente, entrambi "a scadenza":

- versioni beta, inclusa l'ultima di Office;

- antivirus, perlomeno quello molto titolato che io uso (ma dico il peccato, non il peccatore).

Come si sa, gli uni e gli altri, dopo i due-tre mesi previsti si disattivano. Ma, a onta degli sviluppatori che non l'hanno previsto (!!) basta retrodatare l'orologio di sistema e tutto procede regolarmente! Il dirty trick testé rivelato forse esclude sì e no i bambini in tenera età... Per cui uno si chiede: possibile che programmatori superesperti non abbiano escogitato qualche misura più robusta ma semplice, come tenere sul LORO sito, un database degli utenti registrati con le rispettive scadenze?

Inconvenienti collaterali alla retrodatazione? Alemeno due:

- la posta in partenza con data sbagliata, per cui il destinatario la trova in fondo e non in cima all'Inbox;

- applicazioni Excel basate sulla data corrente giusta (tipicamente, usando la funzione =NOW() e simili e formule correlate).

Con un po' di pazienza il rimendio è presto trovato, basta rimettere a posto il Clock! Ma va? Ma sì. Nel primo caso l'antivirus non fa una piega, visto che la sua protesta si ha solo all'avvio di Windows. E con Excel? La risposta è quella che tutti si attendono: si deve PRIMA aprire Excel, POI aggiustare l'orologio.

 

posted @ 10.19 | Feedback (0)

lunedì 24 maggio 2010 #

Subtotali per mese con un semplice ma utile trucco

Subtotali per mese con un semplice ma utile trucco

Prima di procedere, segnalo un utile articolo Microsoft che descrive come velocizzare WINDOWS 7, semplicemente sfruttando una chiavetta USB di adeguata capacità:

http://www.microsoft.com/italy/pmi/comefare/tecnologia/readyboost/default.mspx

Ma torniamo a bomba, con il tema del titolo.

Si abbia un intervallo database del tipo seguente:

 

A

B

C

D

E

F

G

1

DATA

TIPO

QUANT

PREZZO

RICAVO

2

04/01/2010

AA01

              21,00

10

          210,00

3

13/01/2010

AA02

              15,00

15

          225,00

4

14/01/2010

AA01

              42,00

10

          420,00

5

21/01/2010

AA02

              22,00

10

          220,00

6

02/02/2010

AA02

              34,00

10

          340,00

7

04/02/2010

AA01

              12,00

15

          180,00

8

15/02/2010

AA03

              50,00

15

          750,00

9

18/02/2010

AA03

              33,00

5

          165,00

10

22/02/2010

AA01

              42,00

5

          210,00

11

01/03/2010

AA01

              32,00

5

          160,00

12

03/03/2010

AA03

              42,00

10

          420,00

Si desiderano totali parziali relativi a ciascun mese. Premesso che in ogni caso è necessario che sia applicato l’ordinamento (sort) secondo la data, si possono percorrere almeno due vie: estrazione dei record specifici mediante un filtro opportuno o, più comodamente, sfruttando i subtotali. Ma come fare, visto che non esiste in origine un campo del mese?

La soluzione, cui probabilmente molti (ma non tutti) avranno pensato, consiste nell’inserimento, sulla sinistra di un altro campo intestato MESE (giustappunto, e a causa di sfrenata fantasia letteraria):

 

A

B

C

D

E

F

G

H

1

MESE

DATA

TIPO

QUANT

PREZZO

RICAVO

2

Gennaio

04/01/2010

AA01

              21,00

10

          210,00

3

Gennaio

13/01/2010

AA02

              15,00

15

          225,00

4

Gennaio

14/01/2010

AA01

              42,00

10

          420,00

5

Gennaio

21/01/2010

AA02

              22,00

10

          220,00

6

Febbraio

02/02/2010

AA02

              34,00

10

          340,00

7

Febbraio

04/02/2010

AA01

              12,00

15

          180,00

8

Febbraio

15/02/2010

AA03

              50,00

15

          750,00

9

Febbraio

18/02/2010

AA03

              33,00

5

          165,00

10

Febbraio

22/02/2010

AA01

              42,00

5

          210,00

11

Marzo

01/03/2010

AA01

              32,00

5

          160,00

12

Marzo

03/03/2010

AA03

              42,00

10

          420,00

 

Inoltre nelle celle da A2 in giù va messa una formula del tipo seguente:

=SCEGLI(MESE(B2);"Gennaio";"Febbraio";"Marzo";"Aprile";"Maggio";. . . ;"Novembre";"Dicembre")

Nota per i primo-armigeri. La formula precedente si può inserire in A2 e poi copiare in basso o più astutamente in tre mosse: 1. Selezionare l’intero campo da A2 in basso; 2. Inserire la predetta formula in A2; 3. Premere Ctrl+Invio anziché l’usuale Invio.

L’applicazione e rimozione dei subtotali agognati si può infine meccanizzate con il codice macro qui sotto riportato:

Dim swMettiTogli As Boolean ' Definizione a livello modulo: conserva il suo valore

 

Sub MettiTogliSubtotali()

  swMettiTogli = Not swMettiTogli ' True/False in alternanza

  With Range("A1") ' Così la macro opera "a distanza"

                   ' cioè pure se A1 NON è selezionato

    If swMettiTogli Then

      .Subtotal GroupBy:=1, Function:=xlSum, TotalList:=Array(6), _

          Replace:=True, PageBreaks:=False, SummaryBelowData:=True

    Else

      .RemoveSubtotal

    End If

  End With

End Sub

 

La macro può essere consumata da tutti come ricetta, mentre ai più esperti suggerisco l’esegesi autogestita.


NOTA BENE. I subtotali NON si applicano a una tabella - oggetto ListObject in VBA - ma solo a un normale intervallo dotato di intestazioni. Altrimenti la nostra bella macro fallisce...

?>

?>

posted @ 11.24 | Feedback (0)

mercoledì 19 maggio 2010 #

Alibi quasi perfetto, divagazione elementare su tale post

Il post cui mi riferisco è quello in cui ho ideato una macro per Word in grado di salvare a intervalli dati un certo file .doc. Così il criminale che utilizzasse questo trucchetto potrebbe assentarsi per compiere il misfatto dichiarando: "Ma in quel frangente stavo lavorando a quel certo documento...".

Il post in parola è visitabile all'URL seguente:

http://blog.shareoffice.it/giannigiaccaglini/archive/2009/10/12/10256.aspx

Tempo fa ho avuto un ripensamento, anzi due:

  • ma non sarebbe più comodo, per il ns. poco raccomandabile tipo (che magari è solo un/una adultero/adultera, non necessariamente un assassino) RETRODATARE il clock del PC semplicemente al rientro dal misfatto, continuando a lavorare per un  tempo pari a quello dell'assenza quindi ripristinando l'orologio, diciamo così fischiettando come se nulla fosse stato?
  • ma siamo sicuri che ripetuti salvataggi lascino tracce in un (per me misterioso...) qualche file log del sistema operativo?

Dubito quin sul secondo punto. Al più la traccia potrebbe essere la data dell'ULTIMO salvataggio, un po' poco per un alibi serio, no? Quanto al primo non penso proprio che i famosi periti di un tuttora ben noto processo sarebbero stati in grado di accorgersi dell'inganno, che - si badi bene ! - potrebbe essere espletato anche da un utente di medio calibro, contrariamente all'altro sofisticato espediente VBA.

[Ripensandoci , il nostro cattivo soggetto potrebbe salvare file distinti , di vario tipo .doc oppure .xls ecc.]

In attesa di ricevere riscontri da esperti VERI di Windows mi permetto di dire che i sullodati periti non erano poi così ferrati: infatti non hanno preso in considerazione l'alternativa che a un distrattone come me non era passata per la capa. La quale vanifica le conclusioni loro come quelle, di segno contrario, dei periti precedenti. Forse era meglio tacere, dichiarando che in quella questione, come del "diman" della melanconica poesia di Lorenzo il Magnifico (*), "non si aveva certezza"...

(*) Promemoria per liceali falliti:

Quant'è bella giovinezza

che si fugge tuttavia.

Chi vuol esser lieto, sia.

Del diman non v'ha certezza.

 

 

posted @ 13.04 | Feedback (0)

martedì 11 maggio 2010 #

Due trucchi per Userform: pulsanti “in cascata” e controlli filtrati per nome

Due trucchi per Userform

Si tratta di due sciocchezzuole, però del tipo “toh, chi ci avrebbe pensato?”...

Primo trucco: pulsanti “in cascata”

Questa ideuzza mi è venuta esaminando una soluzione implementata mediante le modernissime finestre WPF, ossia basate sulla spettacolare e multimediale tecnologia Windows Presentation Foundation. In quel caso si aveva una serie di pulsanti di cui di volta in volta veniva fatto emergere in primo piano quello richiesto dal contesto.

Ovviamente niente del genere è possibile con le normali Form, men che meno con le semplici UserForm che ci passa il convento VBA. Giusto? Non del tutto, almeno nell’esempietto che vado a illustrare. L’ipotesi è di aver a che fare con due (o più) pulsanti uno soltanto dei quali va sottoposto all’utente, di volta in volta. Il trucco è presto detto: rendere visibili, di volta in volta, soltanto quello che occorre, evitando così tediosi switch booleani.

Si tratta della proprietà Visible da porre =False o =True secondo necessità. L’alternativa, va ricordato, è l’attivazione o meno della proprietà Enabled, comunque nell’esempio che sto per proporre ho inoltre previsto di sovrapporre i pulsanti in guisa tale che uno soltanto (tra quelli che interessano) sia in vista, ergo concretamente a disposizione dell’utente.

Ed ecco le mosse da compiere:

  1. Creare una Userform, dotandola di due pulsanti CommandButton1 e CommandButton2 di dimensioni identiche, aventi etichetta (Caption) “Primo” e, rispettivamente, “Secondo”;
  2. Trascinarli facendo in modo che il primo si sovrapponga al secondo;
  3. Aggiungere un controllo Label1 etichettato “FINE LAVORI”;
  4. Inserire il codice macro seguente.

Private Sub CommandButton1_Click()

  If MsgBox("Vuoi eseguire il PRIMO lavoro?", vbQuestion + vbYesNo, "") = _

  vbNo Then Exit Sub

  PrimoLavoro

  CommandButton1.Visible = False

  CommandButton2.Visible = True

End Sub

 

Private Sub CommandButton2_Click()

  If MsgBox("Vuoi eseguire il SECONDO lavoro?", vbQuestion + vbYesNo, "") = _

  vbNo Then Exit Sub

  SecondoLavoro

  CommandButton2.Visible = False

  If MsgBox("Vuoi eseguire di nuovo il PRIMO lavoro?", vbQuestion + vbYesNo, "") = _

  vbYes Then

    CommandButton1.Visible = True

    Exit Sub

  End If

  Label1.Visible = True

End Sub

 

Le generiche routine PrimoLavoro e SecondoLavoro potrebbero essere del tipo seguente (mentre saranno ovviamente adattate ai casi singoli):

Sub PrimoLavoro()

  MsgBox "Ora eseguo il primo lavoro", vbInformation, ""

End Sub

 

Sub SecondoLavoro()

  MsgBox "Ora eseguo il secondo lavoro", vbInformation, ""

End Sub

 

Le varie routine parlano da sole e sono di agevolissima sperimentabilità pertanto mi limito a dire che, c.v.d., l’utente dispone inizialmente del solo pulsante “Primo”, cliccando il quale se a.d.r. (a domanda risponde, come nei verbali tribunalizi) Yes viene lanciata PrimoLavoro, al che il primo pulsante è occultato e, alla comparsa del secondo... A tutti sarà insomma palese che se si verifica il duplice assenso dell’utilizzatore a PrimoLavoro segue SecondoLavoro e la (quasi-farsa?) si conclude con la visualizzazione della scritta (Label1.Visible=True) “FINE LAVORI”.

Chicca finale. La nostra Userform potrebbe essere lanciata – in modo atipico e comunque poco noto – tramite un hyperlink come questo posto, diciamo, sul primo foglio di lavoro:

 

A

B

C

D

E

1

 

 

 

 

 

2

 

 

Lancia Userform

 

 

3

 

 

 

 

 

4

 

 

 

 

 

5

 

 

 

 

 

 

Il codice che si scatena cliccandovi sarà il seguente, relativo all’evento FollowHyperlink da inserire nel modulo Foglio1 dell’Editor VBA:

Private Sub Worksheet_FollowHyperlink(ByVal Target As Hyperlink)

   Load UserForm1

   UserForm1.Show

End Sub

Secondo trucco: controlli filtrati per nome

Si abbia una UserForm dall’aspetto, grossomodo, seguente:

 

 

 

 

 

DATA

 

 

 

 

 

 

 

CLIENTE

 

 

 

 

 

 

 

CATEGORIA

II

 

 

 

 

 

 

TIPO LAVORO

II

 

 

 

 

 

 

IMPORTO

 

 

 

 

 

 

 

 

OK

 

 

 

 

 

 

In fondo ad essa, com’è ovvio, si trova un CommandButton1 etichettato “OK”, inoltre dei vari controlli sulla destra quelli contraddistinti  con “II” sono delle ComboBox e le due II imitano alla grossolana le relative frecciotte. Ciascuna di tali caselle a discesa è caratterizzata dalla proprietà RowSource, pari rispettivamente a “TabellaCat” e “TabellaTipoLav”. Si tratta di due intervalli del foglio di lavoro così pre-denominati contenenti intuibili codici di categoria e di tipo di lavoro. In tal modo nelle due combo box si può attingere a tali elenco scegliendo quel che si desidera.

Dimenticavo di dire che la nostra Userform è una maschera di input. Per semplicità si pensi che il pulsante OK ne inserisca i dati in una riga aggiuntiva di una certa Tabella1 del foglio, ma solo a patto che – come minimo – tutte le caselle di testo e i combo box non siano vuote. L’inghippo che nasceva derivava, tra l’altro, dal fatto che nelle UserForm non esiste un modo standard per selezionare controlli funzionalmente omogenei. Nel nostro caso la omogeneità “funzionale” accomuna caselle e combo box, escludendo invece le label e il commandbutton.

In altre occasioni me la cavavo con cose del genere:

For Each Ctrl In Me.Controls

  ' Esplora solo le caselle di testo

  If Left(Ctrl.Name,7) = "TextBox" Then

    MsgBox Ctrl.Name

  End If

Next

 

Nota bene. Non esiste una qualche proprietà che indichi il tipo di controllo...

Nel caso in questione, inoltre, volevo segnalare all’utente il significato del dato, che di fatto viene indicato dalle varie etichette sulla sinistra (DATA, CLIENTE, ecc.). La soluzione è scaturita dalla nozione seguente, che facilmente si ignora o si dimentica:

i controlli di una UserForm si possono individuare non solo con l’indice (numero) ma anche mendiante il loro rispettivo nome.

Insomma se la casella di testo del cliente è il quarto controllo ed ha nome TextBox2 essa può essere indirizzata con Controls(4) o, indifferentemente, con Controls(“TextBox2”).

La soluzione. Consiste nell’adottare due vettori (Array) correlati e opportunamente riempiti, relativi l’uno ai (soli) controlli Textbox e Combobox, l’altro ai corrispettivi nomi di dato (espressi dalle etichette, peraltro indipendentemente da queste).

Senza ulteriori indugi ecco la macro dell’evento scatenato dal pulsante OK.

Private Sub CommandButton1_Click()

  ' Definisci NuovoRec la riga sotto l’ultima di Tabella1

  Dim NuovoRec As Range, CellaRec As Range

  With Range("Tabella1").End(xlDown).Offset(1)

    Set NuovoRec = Range(.Cells(1), .Cells(1, 7))

  End With

  ' Vettore nomi controlli di input

  NomiControlli = _

  Array("TextBox1", "TextBox2", "ComboBox1", "ComboBox2", "TextBox3")

  ' Vettore nomi dei corrispondenti dati di Tabella1

  NomiVariabili = _

  Array("DATA", "CLIENTE", "CATEGORIA", "TIPO LAVORO", "IMPORTO")

  Dim NomeCtrl As String, NomeVariab As String

  Dim i As Integer

  ' Verifica che tutti dati immessi non siano nulli

  For i = 0 To UBound(NomiControlli)

    NomeCtrl = NomiControlli(i)

    NomeVariab = NomiVariabili(i)

    If Controls(NomeCtrl).Text = "" Then

      MsgBox "Il campo " & NomeVariab & " DEVE essere riempito!"

      Exit Sub

    End If

  Next

  ' Trasferimento dei dati della maschera nel NuovoRec

  For i = 0 To UBound(NomiControlli)

    NomeCtrl = NomiControlli(i)

    NuovoRec(1, i + 1).Value = Controls(NomeCtrl).Text

  Next

End Sub

 

Commenti essenziali, per gli esperti cui mi rivolgo. Sul foglio di lavoro – quello in cui viene lanciata la nostra Userform, con un pulsante ivi incorporato o con l’hyperlink del primo trucco – si abbia una Tabella1 così denominata e dall’aspetto seguente:

DATA

CLIENTE

CATEGORIA

TIPO

IMPORTO

12/05/2010

Pallino Pinco

ABC01

Questo

12.500

15/05/2010

Nanni Telegrafista

ACD05

Quell’altro

  9.500

Eccetera.

 Si tratta di un oggetto ListObjest ossia, per intenderci, del tipo che fa sì che inserendo nuovi dati nella riga immediatamente sottostante essa viene automaticamente “arruolata”, ergo inclusa nel nome “Tabella1” (la parte sotto le intestazioni, per la cronaca).

Qui giunti per il buon intenditore cui mi rivolgo non spendo altre chiacchiere salvo far notare che l’ultimo ciclo For i = 0 To Ubound(NomiControlli)... Next sostituisce, sinteticamente, le singole istruzioni in un primo tempo adottate:

NuovoRec(1, 1) = TextBox1.Text

NuovoRec(1, 2) = TextBox2.Text

NuovoRec(1, 3) = ComboBox1.Text

Eccetera 

?>

posted @ 16.57 | Feedback (0)

giovedì 29 aprile 2010 #

Aggiornare una tabella pivot (con una macro, forse, inedita)

Aggiornare una tabella pivot (con una macro, forse, inedita)

Una tabella pivot è un oggetto molto complesso, per cui non c’è da stupirsi che in ambito VBA abbondino le sue proprietà e metodi, un ginepraio da cui non è facile districarsi. Può essere di un (qualche) aiuto il Registratore di macro che, sulla creazione di un “pivot” (come d’ora in poi chiamerò alla breve tale oggetto che addirittura ha come nome completo un logorroico “rapporto tabella pivot”) applicato a un intervallo database alias elenco o tabella. Per fissare le idee diciamo che sia posto in un Foglio2 denominato ELENCO ORDINI a partire da una certa cella A6 e dotato di intestazioni del tipo COD, DATAORD, CLIENTE, QUANT, IMPORTO ecc. (in un ardito sussulto di originalità...). Sulle nostre specifiche azioni il Registratore ci spiattella qualcosa del genere:

Sub CreaPivot()

'

' CreaPivot Macro

 

    Range("A6").Select

    ActiveWorkbook.PivotCaches.Create(SourceType:=xlDatabase, SourceData:= _

        "ELENCO ORDINI!R6C1:R176C19", Version:=xlPivotTableVersion12) _

        .CreatePivotTable TableDestination:="Tabella Pivot!R1C1", TableName:= _

        "Tabella_pivot1", DefaultVersion:=xlPivotTableVersion12

    Sheets("Tabella Pivot").Select

    Cells(1, 1).Select

    ActiveWorkbook.ShowPivotTableFieldList = True

    With ActiveSheet.PivotTables("Tabella_pivot1").PivotFields("CLIENTE")

        .Orientation = xlRowField

        .Position = 1

    End With

. . . OMISSIS

End Sub

 

In sintesi, possiamo apprendere magari ricorrendo all’Help, che:

  • il nostro pivot è racchiuso in un oggetto PivotCache facente parte dell’insieme PivotCaches della cartella di lavoro attiva ActiveWorkbook;
  • la fonte dati è del tipo database Excel (SourceType:=xlDatabase) e deriva da una precisa SourceData, definita – si badi bene! – mediante una stringa di sintassi ovvero con indirizzo nel formato R1C1 anziché nel più usuale A1;
  • il nome (default) di tale tabella pivot è “Tabella_pivot1” (un ulteriore pivot verrebbe denominato “Tabella_pivot2” ecc.); in genere ci conviene accettare tale nome.

Lascio come esercizio la comprensione delle successive istruzioni, che visualizzano il pivot creato in un foglio ad hoc Sheets(“Tabella Pivot)” che, dimenticavo, ho inserito preliminarmente. Mi limito a far notare che Tabella_pivot1 è anche il nome della tabella pivot incorporata in tale foglio dedicato.

Ma vale davvero la pena di sfruttare concretamente una macro del genere? A mio avviso potrebbe aver senso a due condizioni: a) si desidera un modello “leggero”, ossia privo di pivot; b) il modello non viene salvato completo di pivot, altrimenti si va incontro a confusione. In altri termini, il pivot viene creato al volo, una tantum.

Una prassi più ragionevole consiste nel creare manualmente un pivot in base a una situazione qualsiasi ma non vuota dell’elenco origine (SourceData, in VBA) affidandone la gestione sempre manuale ad un utente consapevole, che se la cavi con gli strumenti che il riquadro Elenco campi tabella pivot offre a iosa.

Nota. VBA rispecchia tali azioni e volendo si può affidare quelle permesse a un utente-zombie ad appositi pulsanti per macro. Chi ne ha voglia si accomodi, comunque questo non interessa il pigro che qui scrive.

Una questione però resta sul tappeto, dovuta al fatto che (a differenza di quanto avviene coi grafici normali) pivot e grafici tabelle pivot non si aggiornano automaticamente a fronte di modifiche dell’elenco “pivotato” (se si perdona l’estemporaneo quanto orrendo neologismo). Peggio che mai vanno le cose se quest’ultimo subisce aggiunte o cancellazione di record (en passant, è un mestiere che neanche i grafici classici sanno fare).

Qui ci vuole una macro. Ecco quella annunciata, con un certo non del tutto ingiustificato trionfalismo, nel titolo:

Sub AggiornaPivot()

  MsgBox ActiveWorkbook.PivotCaches(1).SourceData ' Servito per debug

  ' Cambia l’elenco origine dei dati

  ActiveWorkbook.PivotCaches(1).SourceData = _

  Foglio2.Name & "!" & _

  Range("InizioElenco").CurrentRegion.Address(True, True, xlR1C1)

   ' Rinfresca di fatto la tabella pivot

   ActiveSheet.PivotTables("Tabella_pivot1").PivotCache.Refresh

End Sub

 

Premetto che né nei sacri testi né sul web si trova nulla del genere (eccezion fatta per i più zelanti, ovviamente), mentre abbondano le macro per modificare in vari mondi un pivot, in un primo tempo ho fortunosamente trovato il metodo Refresh applicabile all’oggetto PivotCache che a sua volta è un oggetto figlio della nostra PivotTables(“Tabella_pivot1”). Così ho sperato che l’ultima istruzione fosse sufficiente allo scopo di aggiornamento che mi prefiggevo. Illusione! Per due motivi:

1.       soprattutto non tiene conto dell’aggiunta o cancellazione di record (per la cronaca, nemmeno con tabelle, oggetti ListObjects);

2.       la modifica di un record (esistente), diciamo “Brambilla & C.” => “Brambilla Snc” creava nel pivot un risultato nuovo ma anomalo (divertitevi a trovarlo e, se ci riuscite, spiegatemi il motivo di tale bug).

Il rimedio, presto trovato, consiste  nel riassegnare dinamicamente la sorgente dati – SourceData del nostro PivotCaches(1) della cartella di lavoro attiva. E qui è nato un ammattimento dovuto alla particolare sintassi della stringa da assegnare a SourceData, nella fattispecie "ELENCO ORDINI!R6C1:R176C19". Questa corrisponde a "ELENCO ORDINI!A6:S176" (Il nostro elenco originario si estendeva da riga6, colonna 1 a riga 176, colonna diciannovesima).

La seconda istruzione della macro risolve brillantemente (modestia a parte) il problema. Partendo dalla cella pre-denominata “InizioElenco” (*).

Nota (*) Che poi sarebbe la A6 dell’esempio, ma così la cosa assume carattere più generale e indipendente da eventuale inserimento di righe vuote in testa all’elenco.

Tale clou consiste nel concatenamento di Foglio2.Name & “!” che nel nostro esempiucolo darebbe “ELENCO ORDINI!” seguito da un Range("InizioElenco").CurrentRegion.Address(True, True, xlR1C1). Qui ricordo che CurrentRegion fornisce l’intero intervallo “attorno” alla cella “InizioElenco” ovvero il database corrente a patto che – attenzione! – le intestazioni si trovino a riga 1 o, quantomeno, siano precedute da una riga vuota e, inoltre, non vi siano “buchi” o righe aggiuntive fasulle in fondo. Il tutto culmina con la proprietà Address(True, True, xlR1C1) che grazie al terzo argomento fornisce l’agognato indirizzo nella sintassi dovuta.

Riordino, sovente necessario

Come si scopre, il più delle volte l’aggiornamento implica che non solo un nuovo record (come è da attendersi) ma anche un record soltanto modificato (come invece non ci si aspetta) venga collocato in fondo al pivot. In tali casi sarà opportuno prevedere un riordino. Il codice VBA, che potrebbe essere inserito in fondo alla routine AggiornaPivot, è del tipo seguente (notare la variante, rispetto ad ActiveSheet, che evoca il foglio di nome “Tabella Pivot“ a distanza, ovvero anche se tale foglio non è al momento attivo:

With Worksheets("Tabella Pivot").PivotTables("Tabella_pivot1")

  .PivotFields("CLIENTE").AutoSort xlAscending, "CLIENTE"

End With

 

Qui viene chiamato in gioco PivotFields ovvero l’insieme dei campi del pivot, di cui nell’esempio si considera quello etichettato “CLIENTE”, al quale a sua volta viene applicato il metodo Autosort con opzioni di limpida semantica: xlAscending e “CLIENTE” (chiave di sort, guardacaso il campo in questione.

La scelta di tale campo dipende ovviamente dal caso concreto. Per concludere, si esamini la Sub seguente che esplora i vari PivotFields messaggiandone i nomi:

Sub EsploraCampiPivot()

  Dim CampiPivot As PivotFields, CampoPivot As PivotField

  Set CampiPivot = _

  Worksheets("Tabella Pivot").PivotTables("Tabella_pivot1").PivotFields

  For Each CampoPivot In CampiPivot

    MsgBox CampoPivot.Name

  Next

End Sub

 

?>

?>

posted @ 17.45 | Feedback (0)

mercoledì 14 aprile 2010 #

Creare automaticamente il backup di un modello Excel

Creazione automatica del backup di un modello

Mi pervengono le richieste più strane e inattese. L’ultima, espressa nel titolo e esternata da un tal Nonricordo Chisia , è la possibilità di effettuare tramite macro VBA un archivio Excel che sia la copia precedente di quello che di volta in volta viene aperto e, il più delle volte, aggiornato. Si tratta d’una pignoleria? Giudichi ognuno, di fatto l’esigenza nasce laddove è prevista una notevole attività di data entry ad esempio nelle righe di una tabella. La responsabilità dell’utente resta sempre malgrado opportune tecniche di convalida e persino con il rimedio che vado subito a suggerire.

Sia come sia ecco la ricetta, che comunque ritengo curiosa e interessante.

Nota. In quel che segue, per fissare le idee, si supporrà che il file da curare e la sua versione... postuma siano ModelloBase.xlsm e, rispettivamente, ModelloBaseBackup.xlsm. I perfezionisti se hanno voglia e tempo potranno sostituirli con generici parametri.

Primi tentativi, uno problematico l’altro... illusorio

Come sarà subito venuto in mente a molti, incluso il succitato Nonricordo Chisia (ignobile nomignolo che verrà perdonato, spero), si potrebbe ricorrere all’evento Open del modulo ThisWorkbook del nostro ModelloBase. Ne descrivo a parole le operazioni manuali:

  1. Salvare (subito) ModelloBase in ModelloBaseBackup;
  2. Chiudere ModelloBase! (infatti dopo il passo 1 è aperto solo ModelloBaseBackup);
  3. Aprire nuovamente ModelloBase.

Così facendo però se affidiamo tali operazioni all’evento di apertura di ModelloBase, perché il passo 3 implica la ripresa del passo 1, creando un loop infinito (o no?). Chi ne ha voglia può studiare una qualche variabile booleana del tipo switch di primo giro, ma il tutto appare macchinoso, poco chiaro e fonte di rischi più o meno (in)attesi.

Un uccellino (di quelli che frullano nella testa nottetempo o in tram) mi ha allora detto: perché non ricorrere all’istruzione FileCopy? Detto fatto:

Nota. L’ipotesi, direi scontata, è che ModelloBase e ModelloBaseBackup risiedano nella stessa cartella di file.

Private Sub Workbook_Open() ' Evento del file ModelloBase

  ChDir Me.Path ' Fissa la directory di ModelloBase

  FileCopy "ModelloBase.xlsm", "ModelloBaseBackup

End Sub

 

Giusto? Ahimè no, perché Excel rigetta l’istruzione FileCopy applicata a un ModelloBase.xlsm attualmente aperto. L’illusione nacque dall’idea che i file Excel risiedono su RAM. Vero, ma l’applicativo mantiene un contatto con l’archivio su disco (tra l’altro vitale e non virtuale nel caso di swapping con modelli enormi e/o con sovraccarico di RAM per altri motivi).

La ricetta, un ripiego comunque interessante

Prima di cedere le armi, ho infine escogitato il ricorso a un archivio ausiliario, diciamo Menu.xlsm, il cui scopo primario (o addirittura unico) sia quello di lanciare a sua volta ModelloBase.Xlsm. Si può pensare a qualcosa del genere, che suggerisce un Menu relativo a più modelli che a volte fanno parte di un’applicazione:

 

A

B

C

D

1

ModelloBase.xlsm

<= legame ipertestuale

 

 

2

QuestAltroModello

"

 

 

3

QuellAltroModello

"

 

 

4

ecc.

"

 

 

 

Nota. I legami ipertestuali costituiscono una variante rispetto a dei pulsanti di comando di cui non tutti si ricordano.

Insomma Menu.xlsm conterrà nel modulo ThisWorkbook la macro d’apertura seguente:

Private Sub Workbook_Open()

  ChDir Me.Path

  FileCopy "ModelloBase.xlsm", "ModelloBaseBackup.xlsm"

End Sub

L’utente dovrà poi cliccare sul link predetto. Stavolta Excel non protesta, ovviamente a patto che l’utente non abbia già aperto il fatidico ModelloBase (v. più avanti il rimedio a questo caso estremo).

Infine ho supposto che Menu.xlsm sia semplicemente un archivio “di lancio” relativo al ModelloBase, nel qual caso sarà meglio chiamarlo, piuttosto, ModelloBaseLancio.xlsm. esso potrebbe non contenere nulla, tranne il seguente codice nel solito ThisWorkbook:

Function ModelloAperto(NomeWrk As String) As Boolean

  Dim Wrk As Workbook

  ModelloAperto = False

  For Each Wrk In Workbooks

    If Wrk.Name = NomeWrk Then

      ModelloAperto = True

      Exit For

    End If

  Next

End Function

 

Private Sub Workbook_Open()

  Dim ModBase As String, ModBackup As String

  ChDir Me.Path

  ModBase = "ModelloBase.xlsm"

  ModBackup = "ModelloBaseBackup.xlsm"

  Dim Msg As String, Titolo As String

  If Not ModelloAperto("ModelloBase.xlsm") Then

    FileCopy ModBase, ModBackup

    Workbooks.Open ModBase

  Else

    Msg = "Non ho creato il backup " & ModBackup & _

          vbLf & "perché " & ModBase & " era già aperto..."

    Titolo = ModBase & " non va aperto prima di " & Me.Name

    MsgBox Msg, vbCritical, Titolo

  End If

  Application.DisplayAlerts = False

  If MsgBox("Chiudo il Menu?", vbYesNo, "") = vbYes Then Me.Close

End Sub

Commenti? Nisba, li affido all’esegesi fai-da-te del visitatore, cui dico solo che la funzione ModelloAperto esplora tutti gli oggetti dell’insieme Workbooks (cartelle di lavoro aperte) restituendo True se e solo se della combriccola fa parte la cartella di lavoro chiamata “NomeWrk” – che poi sarà la nostra ModelloBase.xlsm come passatale dalla macro Workbook_Open. E spero sia chiaro al proverbiale duo Chicche & Sia (*) il messaggio che uscirebbe se l’incauto utilizzatore aprisse per primo ModelloBase.xlsm.

(*) Di decurtisiana memoria, se ben ricordo.

 

?>

posted @ 13.53 | Feedback (0)

giovedì 18 marzo 2010 #

Calcoli tradotti al volo in valori (ricetta originale!)

QUESTA "RICETTA" NASCE DALLA RICHIESTA FATTAMI DA UNO SVILUPPATORE DI MODELLI EXCEL CON VBA, SPECIALIZZATO IN PROBLEMI DI STUDI DENTISTICI:

 

Paolo Bortolini

Sito: www.studiobortolini.com

Blog: http://managementodontoiatria.wordpress.com/

 

A onor del vero, dopo avergliela escogitata e proposta, mi ha de visu mostrato che nel frattempo anch'egli era suppergiù pervenuto ad un'idea stostanzialmente consimile... (succede, non s'inventa nulla a questo Excel-mondo).

Calcoli tradotti al volo in valori (ricetta originale!)

Si abbia una tabella del tipo seguente - con dati fasulli, colonne e righe omesse –sulla cui parte destra (da colonna H in avanti) si vogliono riportare formule anche complesse copiate in basso su tutti i record.

 

A

B

C ... F

G

H

I

J

1

Data

Nominativo

ALTRI CAMPI...

Classe

Costi

Volumi

Resa

2

01/12/2009

Rossi A.

. . .

AB

   219,38

58,50

  182,89

3

01/12/2009

Bianchi G.

. . .

BB

   219,38

87,00

  211,39

4

01/12/2009

Verdi F.

. . .

CFS

     43,88

3,80

    47,69

5

01/12/2009

Mauri R.

. . .

MM

     87,75

3,84

    91,56

6

01/12/2009

Andreini M.

. . .

FR

     26,33

10,00

    36,34

7

01/12/2009

Rossi A.

. . .

OO

     65,81

3,90

    69,62

8

01/12/2009

Bianchi G.

. . .

PM

     87,75

3,75

    68,44

9

01/12/2009

Verdi F.

. . .

RD

       8,78

3,81

    12,59

10

01/12/2009

Mauri R.

. . .

RD

     13,16

4, 66

    16,97

11

01/12/2009

Andreini M.

. . .

ED

   153,57

8,85

 € 102,43

12

01/12/2009

Rossi A.

. . .

RD

     21,94

3,75

    24,25

ALTRE RIGHE...

12499

01/12/2009

Zucchi L.

. . .

AX

   219,38

82,50

   221,89

12500

01/12/2009

Brambilla P.

. . .

BB

   219,38

58,00

   182,00

 

Il problema che subito si presenta deriva dal fatto che le migliaia di righe in ballo sono troppe, con due conseguenze poco simpatiche: le formule appesantiscono il file e, peggio ancora, rallentano paurosamente l’esecuzione.

Nota. Per la cronaca, nel modello che mi è stato commissionato diverse formulacce, per soprammercato “matriciali”, erano del tipo
 =SE(Y2<=Dataqmax;SOMMA(SE(DataC<=Dataqmax;SE(DataC>=Dataqmax-Orizzonte;SE(ClienteC=0;SE(CatC=AA2;SE(PC=AB2;Consumo;))))))/AQ2;0).

Prima soluzione (inadeguata)

Partendo dal normale intervallo della bozza affidatami dal committente, ovvero non da una tabella “canonica” (ListObject in VBA), ho dapprima pensato a una macro che, partendo da una prima riga contenente essa soltanto le formulacce da colonna H in poi, le ricopiasse in basso per tutte le righe sottostanti da 2 a 12500 (o giù di lì). Peggio che andar di notte, ovviamente: si registravano tempi biblici di ricalcolo.

Poi mi sono detto che salvare i risultati in termini di VALORI poteva comunque essere utile, in vista di ulteriori elaborazioni con altri programmi come Access o applicazioni sviluppate in Visual Studio. Ed ecco allora le macro VBA con le quali ho tentato di concretizzare l’idea. Si esamini anzitutto la seguente, seguita dalla sua brava Sub di prova.

Sub IncollaSpeciale(Scarto As Integer)

  Dim RigaFrm As Range, RigaDest As Range

  Set RigaFrm = Range("RigaFormule")

  Set RigaDest = RigaFrm.Offset(Scarto)

  RigaFrm.Copy RigaDest

  RigaDest.Copy

  RigaDest.PasteSpecial Paste:=xlPasteValues

  Application.CutCopyMode = False

End Sub

 

Sub ProvaIncollaSpeciale()

   IncollaSpeciale 3

End Sub

 

La IncollaSpeciale applica appunto all’intervallo preliminarmente denominato “RigaFormule” (nella figura precedente potrebbe avere coordinate quali H1:M1 o simili) contenenti le fatidiche formule a riga 2 da H in poi) il comando omonimo Copia seguito da Incolla Speciale con opzione Valori. Tali operazioni si traducono in VBA in queste successive istruzioni: a) proprietà Copy applicato a RigaForm che registra il Range(“RigaFormule”) assegnandole come destinazione una RigaDest shiftata secondo l’argomento Scarto; b) a tale RigaDest è di nuovo applicato Copy; c) quindi, sempre RigaDest, è assoggettato a PasteSpecial con opzione Paste:=xlPasteValue che, come qui si desidera,incolla i valori e non le formule; d) chiude un (inevitabile) Application.CutCopyMode = False che elimina la dannata casella ruotante che altrimenti continuerebbe a girare attorno all’origine (o alla destinazione? Verificate voi stessi...).

Ed ecco la macro centrale che utilizza la copia in basso dei valori, lasciando in vita la riga 2 di formule, secondo un certo numero di record NumRec come argomento.

Sub CopiaDati(NumeroRec As Integer)

  Dim Inizio As Date

  Inizio = Now()

  SvuotaRigheMenoUna

  Application.ScreenUpdating = False

  Dim i As Integer

  For i = 1 To NumeroRec

    IncollaSpeciale i

  Next

  MsgBox "Inizio: " & Inizio & vbLf & "Fine: " & Now()

End Sub

 

A questo punto non voglio tediare gli esperti cui mi rivolgo con commenti che ora ritengo superflui. Costoro potranno divertirsi a sfruttare CopiaDati passandole un NumRec pari alle righe meno una del nostro elenco, nonché a creare una macro SvuotaRigheMenoUna che, appunto, ripulisce tutte le celle che stanno sotto la riga 2 a partire da colonna H (o quel che l’è).

La pensata precedente può essere interessante per situazioni intermedie, diciamo con non più di 200-300 righe, ma purtroppo delude in gran parte le speranze. Infatti se garantisce un minor appesantimento di formule l’esecuzione del codice presenta riduzioni di tempo piuttosto scarse, come indicano - provare per credere – le istruzioni iniziale Inizio = Now e finale MsgBox ecc. che segnalano circa 10 minuti già con un migliaio di righe.

Nota. L’applicazione che mi era stata data “in cura” prevedeva l’inserimento di record di dettaglio quotidianamente, anche più di uno al giorno, per uno e persino più ANNI! Le aspettative di certi utenti sono a volte utopistiche...

Altro tentativo fallito

Successivamente ho sperimentato la variante consistente nel tradurre il fatidico intervallo in una Tabella , mediante l’apposito comando (in Excel 2007 posto nella scheda omonima) che automaticamente affibbia il nome default Tabella1. La cosa è in ogni caso interessante per due motivi:

1)      offre gratuitamente filtri automatici sui vari campi e funzionalità di filtro avanzato nonché l’automatico arruolamento come nuovo record di tale Tabella1 ogni volta che si aggiunge qualcosa sotto l’ultima riga precedente;

2)      riporta, sempre automaticamente, le formule inserite (da H in poi, sempre per stare al nostro caso) in tutti i record vecchi o nuovi.

Ma purtroppo anche in questo caso i tempi di ricalcolo si rivelano altresì solo leggermente più contenuti: confidavo in un qualche sistema di ottimizzazione offerto da Excel con le tabelle, alias ListObject. Aspettativa ingenua (miracoli non se ne fanno, neanche in casa Microsoft), almeno con le molte e complesse formule della nostra riga da H in poi e i tempi gli appesantimenti del file restano inaccettabili.

Eureka! Ovvero un uovo di Colombo

Per giunta il mio incontentabile committente pretendeva che venisse esportato su un diverso foglio l’intervallo di righe relative a un certo criterio (principalmente quelle compresi fra due date fissate dall’utente finale). Di qui il secondo uovo di Colombo, più complicato perché richiede codice VBA, ma comunque brillante.

Visto che i vari record sono immessi giorno per giorno l’aggiornamento di ciascuno secondo le formule di destra si può affidare a una macro che le calcola inserendone i valori.

L’idea appena detta si concretizza con un record ausiliario posto su colonne lontane da quelle del database. Si tratta di una cosa del genere, che ripropone a parte un tipico record della nostra Tabella1:

 

Y

Z

AA

AB

AC

AD

AE

AF

AG

eccetera

1

01/12/2009

Rossi M.

Xxx

zzz

Ecc.

Ecc.

Ecc.

Formula 1

Formula 2

eccetera

 

Si fa presente che le prime 7 celle costituiscono campi di input, mentre quelle accanto, da colonna AF in avanti contengono le dannate formule. Orbene, prima di esaminare la prima basilare macro si sappia che agli intervalli delle une e delle altre sono stati affibbiati i nomi QuestoRecord e, rispettivamente, QuesteFormule. Eloquenti, direi.

Ed ecco la macro in parola, seguita da una SvuotaRighe a sua volta richiamata dalla prima con evidente finalità:

Sub AggiornaValori()

  If ActiveCell.Column <> 8 Then

    MsgBox "Cella attiva DEVE essere in colonna H!", vbCritical

    Exit Sub

  End If

  Dim SetteCelle As Range

  With ActiveCell

    Set SetteCelle = Range(.Offset(0, -1), .Offset(0, -7))

  End With

  SetteCelle.Copy Range("QuestoRecord")

  Range("QuesteFormule").Copy

  ActiveCell.PasteSpecial Paste:=xlPasteValues

  Application.CutCopyMode = False

End Sub

 

Sub SvuotaRighe()

  Dim PrimaCella As Range, UltimaCella As Range

  With Range("RigaFormule")

    Set PrimaCella = .Cells(1, 1)

    Set UltimaCella = .Cells(.Count)(1, 1)

  End With

  If IsEmpty(PrimaCella) Then Exit Sub

  If IsEmpty(PrimaCella.Offset(1, 0)) Then

    Range(PrimaCella, UltimaCella).Clear

  Else

    Range(PrimaCella.End(xlDown), UltimaCella).Clear

  End If

End Sub

 

Commenti. Tacitiani e limitati alla Sub AggiornaValori (la seconda è affidata all’esegesi autogestita). La variabile SetteCelle serve a registrare l’intervallo delle prime celle dei campi di input (il valore 7 deriva dal caso trattato e va allineato a situazioni differenti). La macro s’incarica poi di copiare il Range SetteCelle nel Range QuestoRecord , con conseguente calcolo delle specifiche formule nella zona QuesteFormule, che a loro volta vengono incollate col PasteSpecial di soli valori nella cella attiva.

Nota. Quel che va sottolineato, chi non l’avesse ancora inteso, è che di questa manovra in due tempi non si può fare a meno, perché inserendo anche temporaneamente le formule accanto alla cella attiva le avrebbe generalizzate sull’intera colonna H, creando il rallenti che si voleva evitare...

Qualcuno obietterà che l’utente potrebbe dimenticare di lanciare AggiornaValori. Eccolo accontentato con una macro che li aggiorna tutti, circostanza nella quale il distrattone potrebbe andarsi a prendere un caffè, una tantum si spera!

Sub AggiornaTuttiValori()

  Dim Inizio As Date

  Inizio = Now()

  Load UserForm1

  UserForm1.Show

  Dim CampoCosti As Range, i As Integer

  Dim SetteCelle As Range

  Set CampoCosti = _

  Foglio1.ListObjects("Tabella1").ListColumns("COSTI GENERALI").Range

    On Error Resume Next ' Patch dovuta a causa dell'orribile caso #DIV/0!

    For i = 2 To CampoCosti.Cells.Count

    If CampoCosti.Cells(i) = "" Then

      With CampoCosti.Cells(i)

        Set SetteCelle = Range(.Offset(0, -1), .Offset(0, -7))

      End With

      SetteCelle.Copy Range("QuestoRecord")

      Range("QuesteFormule").Copy

      CampoCosti.Cells(i).PasteSpecial Paste:=xlPasteValues

      Application.CutCopyMode = False

    End If

  Next

  UserForm1.Hide

  Unload UserForm1

  Range("A3").Select

  MsgBox "Inizio: " & Inizio & vbLf & "Fine: " & Now()

End Sub

 

Anche qui mi astengo da commenti, in modo pilatesco quanto pigro. Preciso anzi che ho preparato una versione che, per limitare il predetto coffee break, esclude i record già sistemati. La lascio per esercizio. Piuttosto si esamini quest’altra macro, relativa all’evento DoubleClick del Worksheet ove risiede la tabella:

Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)

    If Target.Column = 8 Then AggiornaValori

    Cancel = True

End Sub

 

Come a tutti è chiaro, essa agisce ogni volta che si dà una doppia cliccata su una cella di colonna H (quella del campo contenente la prima formulaccia).

L’evento risolutore. Alla fine, riflettendo, ci si rende conto che le macro precedenti meno AggiornaValori e la sua ancella SvuotaRighe sono pleonastiche o quasi. Questo perché l’evento che sistema tutto o quasi (*) è l’evento Change sempre del foglio della Tabella1:

Private Sub Worksheet_Change(ByVal Target As Range)