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

Controlli e ActionPanes nei VSTO di Word

SEGNALAZIONE UTILE. L'amico Emanuele (Mattei! chi altro potrebbe essere?) ha varato il sito professionale www.donetromacesta.org al quale ho pensato di offrire un piccolo contributo su Excel + VBA. Eccone il link:

http://www.dotnetromacesta.org/common/articoli/office/vba/Gestire_una_galleria_di_immagini.aspx

 

Visitate gente, visitate...

Controlli e ActionPanes nei VSTO di Word

NOTA PRELIMINARE. Questo post iniziza con un appello a quanti la sanno lunga sull’Actions pane e sul suo proficuo utilizzo in Word. Scrivere a giannigiac@tin.it . Sarò lieto di pubblicare ogni suggerimento valido in materia.

Incorporare controlli su un foglio di lavoro è una pratica accettabile, che tra l’altro ha il pregio dell’immediatezza, laddove sono posti accanto ai dati da elaborare con la macro (o il codice “managed” in ambiente VSTO, Visual Studio Tools per Office). Una controindicazione è, parlando in generale, il fatto che i controlli di tipo ActiveX e assimilati compaiono anche in stampa, né sembra possibile occultarli anche solo pro tempore. Si provi infatti la seguente macro, che ipotizza la presenza sul foglio di lavoro sia di controlli “classici” di Excel che di tipo ActiveX:

Sub NascondiSvela(Vis As Boolean)

   For Each Sh In Me.Shapes

    Sh.Visible = Vis

  Next

End Sub

 

Sub Stampa()

  NascondiSvela False

  Me.PrintPreview

  NascondiSvela True

End Sub

Nota Beninteso, in presenza di immagini o forme illustrative che perciò vanno conservate in stampa occorre una routine più mirata, che cioè occulti solo oggetti indesiderati.

Come non ci vuol molto a capire la prima Sub al primo richiamo con Vis = False nasconde tutte le forme, inclusi i controlli ActiveX, anch’essi assimilati a Shapes e inclusi in tale insieme, mentre con Vis = True li visualizza tutti di nuovo. Pertanto l’istruzione centrale di anteprima (o di stampa tout court) dovrebbe essere corretta, giusto? Sbagliato. I controlli classici non sono stampati ma quelli ActiveX anche se messi fuori vista vengono di fatto stampati.

Tuttavia l’inconveniente può essere ovviato (o inesistente) in Excel, dove esistono più fogli di lavoro, basta che i dati da stampare si trovino su un foglio (ad esempio riepilogativo) esente da controlli incorporati.

Il caso Word (con difficoltà iniziali varie)

La situazione è meno felice in Word, ove il documento è unico, soprattutto perché qui non esistono i controlli-classici di Excel, ma soltanto gli ActiveX. Non occultabili a monte della stampa, come già visto con Excel. La situazione appare imbarazzante quando si lavora nel mondo VSTO. Una soluzione brutale l’avrei anche trovata e la riporto in fondo al presente post, ma non pare troppo soddisfacente. Prima di proseguire trovo importante una premessa. I VSTO tradiscono diverse aspettative per chi pensa di ottenerne una sorta di applicativi office-oriented. Ecco i punti più imbarazzanti:

  • In Excel, il mancato supporto di funzioni d’utente (UDF, User Defined Function) implementate con codice .NET;
  • In Word, all’impossibilità di associare a routine scorciatoie di tastiera – comode e “naturali” visto che la digitazione qui regna sovrana - si unisce l’assenza di tali routine nel modulo Normal.dot, che ne consente l’utilizzo con qualunque documento.

In questi casi (e altri di cui non mi sovviene) l’imbarazzo deriva dal fatto che tali preziose funzionalità restano possibili con il buon vecchio sistema di macro VBA anche con un “applicativo” VSTO. Ma in tal modo la security del managed code va un poco a farsi benedire. O no?

Rimanendo a quel che passa il convento Word VSTO, che strumenti ci offre per ottenere un qualche MioDoc.xlx che abbia un minino di valenza applicativa, magari “dedicata” e semichiusa? A dire il vero, le possibilità sono notevolissime, basti pensare al trattamento di archivi Open XML su disco con la specifica API SDK 1.0 + LINQ o la nuova SDK 2.0 “strongly typed”. Prossimamente conto di offrirne almeno un esempio, in cui un intervallo o una tabella Excel vengono importati nel documento Word. Ma la domanda cruciale è la seguente:

Come lanciare in modo semplice funzionalità del genere?

Confesso che all'inizio non ho avuto modo né tempo per esaminare soluzioni avanzate, in primo luogo un Ribbon opportunamente personalizzato. Fra le “semplici” evocate testé dalla domanda comincio con l'annoverare due possibilità:

Ø  Controlli ActiveX incorporati in cima al documento, tipicamente un paio di pulsanti, Button1 e Button2;

Ø  La finestra Azione documenti (Actions pane).

Il secondo metodo si presenta più allettante, in quanto tale pannello si colloca alla destra del documento e promette di scatenare le azioni da noi desiderate evitando di incorporare alcunché nel documento. Ma qui cominciano le dolenti note, almeno in prima battuta (per fortuna, in fondo al post c'è il lieto fine):

1)      Non è possibile caricare l’Actions pane nell’IDE VSTO di Word, lo si può fare solo con il Word “normale”;

2)      esso non viene in alcun modo “visto” da macro VBA, contrariamente a quel che avviene nel mondo VSTO.

3)      Nel Word normale non c’è alcun verso di inserire controlli nell’Actions pane, mentre l’operazione si direbbe consentita in ambiente VSTO, perché all'inizio non si riesce a farlo (ma il modo esiste, v. alla fine!)

Le operazioni di cui al primo punto sono tediose e per pigrizia non le riporto in dettaglio. Dico solo che si parte col pulsante Office, attivando Opzioni di Word... quindi Componenti aggiuntivi ecc. Dopo di che per visualizzare il sospirato pannello si agisce sulla scheda Sviluppo seguita da Pacchetti di espansione e via di seguito, operazioni queste fattibili pure nei VSTO (ma che confusione, non è vero?).

Un po’ di codice managed

Nei VSTO di Word tutte le routine, di regola, sono racchiuse nella classe ThisDocument. Per non scontentare impazienti e frettolosi riporto subito i miei primi tentativi esplorativi, riusciti.

Public Class ThisDocument

 

    Private Sub ThisDocument_Startup(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Startup

        If MessageBox.Show("Cucù, sono " & Me.Name & _

         vbLf & "Due più due = quattro?", , MessageBoxButtons.YesNo) _

         = DialogResult.Yes Then

            MsgBox("Bravo! Sette più")

        End If

        ActiveWindow.ActivePane.View.Zoom.Percentage = 100

        ActionsPane.Show()

    End Sub

 

    Private Sub ThisDocument_Shutdown(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Shutdown

 

    End Sub

 

    Private Sub ActionsPane_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles ActionsPane. Click

      MsgBox("Action pan cliccato!")

    End Sub

 

    Private Sub ActionsPane_DoubleClick(ByVal sender As Object, ByVal e As System.EventArgs) Handles ActionsPane.DoubleClick

        MsgBox("Hai dato un doppio clic sull'ActionsPane. Bravo!")

    End Sub

 

    Private Sub ActionsPane_MouseDoubleClick(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles ActionsPane.MouseDoubleClick

        MsgBox("Hai dato un doppio clic. Bravo!")

    End Sub

 

    Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click

        MsgBox("Bubbusette!")

    End Sub

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click

        MsgBox("Bubbuotto!")

    End Sub

End Class

 

Premesso che in testa al documento erano stati inseriti due controlli Button (scegliere nella sezione Controlli comuni o anche Windows Form), le routine messaggistiche ad essi abbinate sono banali, anzi ridicole. Più interessante la Sub dell’evento ThisDocument_Startup ovviamente equivalente alla Document_Open di Word normale entrambe scatenantesi al caricamento del documento. Interessanti e, come ripeto, possibili solo coi VSTO non tanto le prime istruzioni di squallido umorismo bensì le ultime due, entrambe richieste per visualizzare il sospirato riquadro di cui si parla. Richieste e, direi, indispensabili affinché abbiano concreto utilizzo le altre, relative a due degli eventi dell’oggetto ActionsPane. Questi sono una vera caterva, come è facile scoprire.

Io mi sono limitato ai tre più semplici, abbinando a ciascuno stupidissimi messaggi. Mi sarei anche accontentato di surrogare con essi –nell’incapacità autolesionisticamente ammessa di inserire controlli nel fatidico pannello – un paio di funzionalità magari anche potenti & evolute. Purtroppo si incappa in altre due non del tutto piacevoli sorprese:

  • L’evento Click predomina sugli altri due, e li “oscura”per cui, volendoli usare occorre rinunciare al Click;
  • In compenso gli altri due sono sinonimi per cui una doppia cliccata li scatena entrambi, uno di seguito all’altro (questo punto non è grave né del tutto inatteso, resta solo la domanda sul motivo per cui un medesimo evento abbia due nomi distinti, sottigliezza che non sono in grado di comprendere...).

Rimanendo nel quadro dei tentativi iniziali ecco poi, sotto forma di macro VBA, il codice brute-force per evitare di stampare i due pulsanti ipotizzati in cima al documento, nelle sue prime due righe:

Sub MiaStampa()

  Selection.HomeKey Unit:=wdStory

  Selection.MoveDown Unit:=wdLine, Count:=2, Extend:=wdExtend

  Selection.Cut

  MsgBox "Adesso stampo!"

  . . .  istruzioni di Stampa . . .

  Selection.Paste

End Sub

 

La brutalità dipende dal taglio (Cut) delle prime due righe, previa selezione delle medesime e, con esse, dei due bottoni. Niente paura, perché a valle della stampa essi vengono re-incollati (Paste)assieme alle due righe. Addirittura la routine funziona anche se evocata da uno dei pulsanti. Provare per credere con:

Private Sub CommandButton1_Click()

  MiaStampa

End Sub

Ma i controlli si possono inserire nell’Action pane!

Come? Una tormentosa ricerca su MSDN me l’ha fatto capire. Meglio tardi che mai.

Il segreto di questo preannunciato lieto fine è presto detto:

Pulsanti e controlli di ogni tipo si possono inserire nell'Action pane,. ma non lo si può fare manualmente, ma dinamicamente, tramite codice ad hoc. 

Le delucidazioni su MSDN parlano il linguaggio di chi s’intende di .NET, la qual cosa può essere criptica e scoraggiante per i praticoni come il sottoscritto e, in genere, per chi è alle... seconde armi con Visual Studio.

Di fatto, per fortuna basta osare, ottenendo il risultato espresso dal codice seguente:

    Private Sub ThisDocument_Startup(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Startup

        Dim MioButton As New Button()

        MioButton.Text = "Clicca"

        AddHandler MioButton.Click, AddressOf MioButton_Click

        ActionsPane.Controls.Add(MioButton)

   . . . O M I S S I S . . .

        ActiveWindow.ActivePane.View.Zoom.Percentage = 100

        ActionsPane.Show()

    End Sub

    Private Sub MioButton_Click(ByVal sender As Object, ByVal e As EventArgs)

        MsgBox("Hai cliccato sul MioButton dell'Action pane!!!")

    End Sub

   . . . O M I S S I S . . .

End Sub

Per la delizia dei sapientoni in MSDN si spiega che sono in gioco i cosiddetti “delegati”... Studiate gente, ma noi praticoni per ora ci possiamo accontentare del caso appena visto, commentando le varie istruzioni (che così dovrebbero risultare eloquenti).

Ø  -all’avvio del documento (evento Startup) viene creato un nuovo pulsante MioButton, qui etichettato col banale “Clicca”;

Ø  Subito viene definito, anzi aggiunto un, diciamo così, “manipolatore” (AddHandler) dell’evento Click del MioButton “indirizzato” (AddressOf) allo stesso MioButton (l’indirizzamento può anche puntare a un altro controllo... cosa in genere da evitare, perché fonte di confusione...);

Ø  A questo punto,a valle della routine di StartUp, si definisce la Sub dell’evento MioButton_Click del tutto simile a quella di ogni button, interno o anche esterno all’Action pane.

Per completezza, riporto infine un’istruzione che consente di inserire una serie di pulsanti (o, in genere, controlli) preliminarmente creati e definiti:

.ActionsPane.Controls.AddRange(New Control(){button1, button2, button3})

Come dovrebbe essere evidente, AddRange sintetizza una serie di aggiunte e altrettante istruzioni basate sul metodo Add relativo a un singolo controllo.

?>

?>

posted on lunedì 8 giugno 2009 16.16