Home

 
 
 
 
 



 
 
 
 

 
 
 

 
 
 
 
 









Blog2theMax
Il blog del team di Code Architects

In questo periodo sono occupato quasi settimanalmente con lo Starting Innovation Tour, un ciclo di conferenze gratuite organizzate da Microsoft nelle principali città italiane per mostrare a sviluppatori e partner le principali novità di Vista e Office 2007. Dopo Catania, Napoli, Bologna, Bari e Roma, questa settimana tocca a Padova, poi Milano, Firenze e Torino. A mio parere la conferenza è davvero un'ottima occasione per chi non ha ancora visto con i propri occhi (o toccato con mano, con preferite) le nuove major release dei due prodotti principali di MS. Io mi occuperò delle sessioni su Office, ovvero:

  • Sviluppo con Office 2007 Client Side: come sfruttare il nuovo formato XML dei documenti per modificare o creare nuovi documenti senza avere Office installato - da applicazioni desktop o web - nonchè una panoramica sulla creazione di add-in, Action Pane e ribbon custom.
  • Windows SharePoint Services 3.0: nuove funzionalità per le liste e document library (cestino, supporto per versioning esteso, supporto RSS,...), nuovi template (blog e wiki tanto per essere moderni, ma altro ancora), utilizzo masterpage a-là ASP.NET 2.0, webpart, utilizzo workflow built-in o utilizzo del nuovo SharePoint Designer per la definizione dei propri flussi.
  • Office SharePoint Portal 2007: Excel Services (il motore di calcolo di Excel direttamente sul server + rendering via browser + esposizione del motore tramite web service!), Forms Services (praticamente le form InfoPath via browser!) e Business Data Catalog (indicizzazione e visualizzzione di record presi da un qualunque database o fonte di accesso ai dati esterna)

Insomma, mi sembra ci sia un bel po' di roba da vedere :-) Per non parlare delle bellissime e impressionanti demo grafiche fatte con WPF per Vista! Dai, ci vediamo ad una delle prossime tappe allora...

11/20/2006 2:11:10 PM (GMT Standard Time, UTC+00:00) #  | Comments [0] | 

Ho ripreso una macro che avevo scritto qualche anno fa: Essa impostava l'ordine di build dei progetti nella solution in base alle referenze tra i vari progetti.

VS.NET fa in automatico una cosa del genere quanod le referenze sono di tipo progetto, ma questo è un problema quando si devono gestire delle soluzioni che hanno un grado variabilità notevole in termini di progetti che la compongono.

Questo addin fa lo stesso mestiere per le referenze di tipo assembly...
La routine è stata aggiornata alla versione 2005 per gestire i progetti di tipo ASP.NET / WEB Service che rappresntano un tipo specifico di progetto.

 

Option Strict On
Imports System
Imports System.Windows.Forms
Imports EnvDTE
Imports EnvDTE80
Imports System.Diagnostics

Public Module CalcDepends

Public Function GetBuildOutPutWindow() As OutputWindowPane
Dim win As Window = DTE.Windows.Item( _
EnvDTE.Constants.vsWindowKindOutput)
Return CType(win.Object, OutputWindow).OutputWindowPanes.Item("Build")
End Function


Public Const c_prjtypeVBNET As String = "VBPROJ"
Public Const c_prjtypeCS As String = "CSPROJ"

Public Enum projectType
VBNET
CSHARP
UNKWOWN
End Enum
Public Function getProjectType(ByVal p_prj As Project) As projectType
Dim l_prjtype As String = p_prj.FullName.Substring(p_prj.FullName.Length - 6).ToUpper
If l_prjtype = c_prjtypeCS Then
Return projectType.CSHARP
ElseIf l_prjtype = c_prjtypeVBNET Then
Return projectType.VBNET
Else
Return projectType.UNKWOWN
End If

End Function

Public Sub CalcBuildDepends()
Try
GetBuildOutPutWindow.OutputString("++++++++++++++ ENTERING CalcBuildDepends ++++++++++++++" & vbCrLf)
Dim l_bd As BuildDependency
For Each l_bd In _
DTE.Solution.SolutionBuild.BuildDependencies
l_bd.RemoveAllProjects()
Next

For Each l_prac As EnvDTE.Project In _
DTE.Solution.Projects
If TypeOf l_prac.Object Is VSLangProj.VSProject Then
If getProjectType(l_prac) = projectType.CSHARP Or getProjectType(l_prac) = projectType.VBNET Then
Dim l_ActVSProject As VSLangProj.VSProject = CType(l_prac.Object, VSLangProj.VSProject)
' I get the Assembly Name of the Current Project
For Each ref As VSLangProj.Reference In l_ActVSProject.References
For Each l_pr As EnvDTE.Project In DTE.Solution.Projects
' looking for projects in the solution that are in the current project references list
If TypeOf l_pr.Object Is VSLangProj.VSProject Then
If getProjectType(l_pr) = projectType.CSHARP Or getProjectType(l_pr) = projectType.VBNET Then
Dim l_VSProject As VSLangProj.VSProject = CType(l_pr.Object, VSLangProj.VSProject)
'GetBuildOutPutWindow.OutputString("Checking if " _
' & l_ActVSProject.Project.Name _
' & " references " _
' & l_pr.Properties.Item("AssemblyName").Value.ToString & vbCrLf)

If (l_pr.Properties.Item("AssemblyName").Value.ToString = ref.Name) Then
Try
DTE.Solution.SolutionBuild.BuildDependencies.Item( _
l_ActVSProject.Project).AddProject(l_pr.UniqueName)
GetBuildOutPutWindow.OutputString("**** Adding Reference " & l_pr.Name & " to " & l_prac.Name + " *****" & vbCrLf)
System.Windows.Forms.Application.DoEvents()
Catch ex As System.Exception
MessageBox.Show("Error adding dependency " + l_pr.UniqueName + _
" to " + l_ActVSProject.Project.Name + " Error is:" + _
ex.Message)
End Try
End If
End If
End If
Next
Next
End If
ElseIf TypeOf l_prac.Object Is VsWebSite.VSWebSite Then
Dim l_ActVSProjectWS As VsWebSite.VSWebSite = CType(l_prac.Object, VsWebSite.VSWebSite)
For Each ref As VsWebSite.AssemblyReference In l_ActVSProjectWS.References
For Each l_pr As EnvDTE.Project In DTE.Solution.Projects
If TypeOf l_pr.Object Is VSLangProj.VSProject Then
If getProjectType(l_pr) = projectType.CSHARP Or getProjectType(l_pr) = projectType.VBNET Then
Dim l_VSProject As VSLangProj.VSProject = CType(l_pr.Object, VSLangProj.VSProject)
'GetBuildOutPutWindow.OutputString("Checking if " _
' & l_ActVSProject.Project.Name _
' & " references " _
' & l_pr.Properties.Item("AssemblyName").Value.ToString & vbCrLf)

If (l_pr.Properties.Item("AssemblyName").Value.ToString = ref.Name) Then
Try
DTE.Solution.SolutionBuild.BuildDependencies.Item( _
l_ActVSProjectWS.Project).AddProject(l_pr.UniqueName)
GetBuildOutPutWindow.OutputString("**** Adding Reference " & l_pr.Name & " to " & l_prac.Name + " *****" & vbCrLf)
System.Windows.Forms.Application.DoEvents()
Catch ex As System.Exception
MessageBox.Show("Error adding dependency " + l_pr.UniqueName + _
" to " + l_ActVSProjectWS.Project.Name + " Error is:" + _
ex.Message)
End Try
End If
End If
End If
Next
Next
End If
Next
GetBuildOutPutWindow.OutputString("++++++++++++++ EXITING CalcBuildDepends ++++++++++++++" & vbCrLf)
'MessageBox.Show("Recalculation Done")
Catch ex As System.Exception
MessageBox.Show("Error in seek:" + ex.Message)
End Try
End Sub

End Module

3/15/2006 5:05:56 PM (GMT Standard Time, UTC+00:00) #  | Comments [0] | 

Ciao a tutti, 
   vi riporto di seguito il calendario ufficiale dei prossimi webcast architetturali tenuti da Maurizio, Pierre, me ed altri. Il primo appuntamento e' domani. Illustrero' le novita' della rinnovata versione dell'Enterprise Library di Patterns & Practices per poi proseguire fino a giugno con una nutrita serie di argomenti e approfondimenti su tecnologie reali.

Calendario dei prossimi Architect Webcast  
§Febbraio
7/2: Pattern architetturali per la realizzazione di applicazioni e servizi - Parte I
14/2: Pattern architetturali per la realizzazione di applicazioni e servizi - Parte II
21/2: Introduzione alla metodologia agile MSF 4.0 con Visual Studio 2005 Team System
28/2: Progettare il Web Testing nel mondo Enterprise con Visual Studio 2005 Team Test
§Marzo
07/3: BizTalk Server 2006: uno strumento per tutta l'azienda
14/3: BizTalk Server 2006: mille e uno usi di uno strumento versatile
21/3: BizTalk Server 2006 e lo sviluppo di applicazioni orientate ai servizi
28/3: WinFX: Windows Workflow Foundation - Parte I
§Aprile
04/4: WinFX: Windows Workflow Foundation - Parte II
11/4: Realizzare servizi distribuiti con Windows Communication Foundation - Parte I
19/4: Realizzare servizi distribuiti con Windows Communication Foundation - Parte II
27/4: Architettura SOA. Perché non se ne può fare a meno?
§Maggio
09/5: Le applicazioni client negli scenari d'integrazione - Parte I
16/5: Le applicazioni client negli scenari d'integrazione - Parte II
23/5: Interoperabilità e migrazione tra .NET e COM
§Giugno
06/6: Smart Client. Unire il meglio di idee e tecnologie diverse
13/6: Il dato al centro dell'informazione aziendale. Come gestirlo
20/6: Snellire i processi aziendali gestendo il flusso di informazioni con Office
27/6: Smart Document: la nuova faccia del documento

2/6/2006 5:54:25 PM (GMT Standard Time, UTC+00:00) #  | Comments [0] | 

Windows Communication Foundation (WCF) e Windows WorkflowFoundation possono essere utilizzati in un ambiente di produzione.

Ulteriori dettagli qui:

http://msdn.microsoft.com/winfx/getthebeta/golive/default.aspx

BIT e informazioni qui:

http://www.windowsworkflow.net

http://windowscommunication.net

 

 

1/18/2006 7:03:33 PM (GMT Standard Time, UTC+00:00) #  | Comments [0] | 

Se potessi fare una survey istantanea, proverei a fare le seguenti due domande:

  1. Il programma che usate più frequentemente è Visual Studio?
  2. Avete mai usato le regular expression con il comando Find di Visual Studio?

Scommetterei che l'80% di voi risponderebbe SI alla prima domanda, ma che il 99% risponderebbe NO alla domanda successiva. Il che è alquanto bizzarro: stiamo parlando di una delle feature più potenzialmente utili dell'IDE eppure pochissimi la utilizzano o sanno addirittura che esiste.

Il vero problema è che la sintassi delle regular expression di Visual Studio è completamente differente da quella della classe Regex, quindi per usare questa feature occorrerebbe imparare un altro dialetto per le regular expression, e questo è troppo per la maggior parte degli sviluppatori. Microsoft dovrebbe usare le regex standard anche per questo comando: potrebbero farlo facilmente e in poco tempo, e senza creare problemi di compatibilità con i progetti esistenti.

In attesa che Microsoft si decida a fare questo piccola-grande innovazione, potete divertirvi con quello che avete a disposizione. Ecco alcuni esempi, tratti dal mio nuovo libro Programming Microsoft Visual Basic 2005: The Language:

:i = :z   Cerca nel codice le assegnazioni di un intero a una variabile. (:i sta per un qualsiasi identificativo, :z rappresenta una costante intera). In VB (ma non in C#) trova però anche dei false match, quando una espressione contiene un operatore di uguaglianza.

:i = :q   Cerca le assignazioni di una costante stringa (:q) a una variabile.

(Dim|Private|Public) :i As String   Cerca le dichiarazioni di variabili e field di tipo stringa (solo VB). E' facile adattarla ad altri tipi di dati.

Dim <(:Lu(:Ll)*)+> As   Cerca le dichiarazioni di variabili VB locali che usano un nome in PascalCase e che quindi violano le coding guideline di Microsoft (le variabli locali dovrebbero essere in camelCase)

^:b*'.+\n   Cerca le righe di commento in VB, ossia le righe che cominciano con apostrofo. (Non considera la keyword REM.) Sostituendo l'apostrofo con // si può usare questo pattern anche in C#

Dim {:i} As (.|\n)#<\1>    Evidenzia la porzione di codice tra la dichiarazione di una variabile locale e la prima occorrenza della variabile nel codice. Ripetendo questa ricerca in modo da matchare tutte le variabili locali in un metodo, si puo' controllare se vale la pena spostare la dichiarazione della variabile in modo da avvicinarla al suo primo utilizzo nel codice. (Vedi l'effetto in figura)

1/16/2006 4:56:25 PM (GMT Standard Time, UTC+00:00) #  | Comments [0] | 

Tra le varie WebCast su Visual Studio Team System, segnalo questa che mi pare la più esaustiva e piavevole per chi è agli inizi: Visual Studio Team System , fa un giro completo con il giusto livello di dettaglio su workitems, il nuovo controllo del sorgente, e le varie features di test

.. buone vacanze

 

12/21/2005 11:22:34 AM (GMT Standard Time, UTC+00:00) #  | Comments [0] | 

Ai primi anni '90 avevo una piccola software house tutta mia, di nome Softwhale (chi è bravo con l'inglese apprezzerà il gioco di parole, spero :-) ). Già da allora il mio pallino era scrivere tool per sviluppatori, ed avevo realizzato alcuni prodotti niente affatto male, tra cui dSwapper (una utility che abbatteva il limite dei 640K per i programmi Ms-Dos), Batch Wizard (un compilatore per file batch, che molte software house italiane hanno usato per creare procedure di installazione intelligenti), e QBKIT (una libreria di 700+ nuove funzioni per QuickBasic, incluso menu e finestre 3D). Ma uno dei maggiori successi di quegli anni fu NOWAY , un piccolo tool scritto in Assembly - come la gran parte del software che scrivevo allora - in grado di crittografare gli eseguibili Ms-Dos per evitarne il reverse engineering. All'epoca, infatti, la maggior parte dei gestionali erano scritti in Clipper, e vi erano sul mercato un paio di decompilatori in grado di ricostruire il sorgente originale, con tanti saluti alla proprietà intellettuale. L'altra caratteristica notevole di Noway era la possibilità di "legare" un eseguibile ad una macchina, in modo da proteggere dalla diffusione di copie pirata.

Come vedete, dopo una decina di anni il mondo del software ha fatto passi da gigante, ma alcune cose non sono cambiate. Anche oggi i linguaggi .NET permettono una produttività incredibile - come e più del Clipper negli anni '90 - e soffrono del medesimo difetto: chiunque può decompilare gli assembly .NET con strumenti come Reflector, e scoprire in pochi minuti un algoritmo che vi ha impegnato per settimane o mesi. Non è un caso che alcune software house hanno deciso di rinunciare ai benefici del .NET Framework pur di evitare che ciò accada.

Un paio di mesi fa stavo facendo qualche esperimento con il .NET Framework 2.0 e ho avuto una piccola illuminazione, ovvero una tecnica che permette di crittografare un assembly e di riportarlo in chiaro "al volo", al momento di caricarlo in memoria. Non intendo dilungarmi sui dettagli di questa tecnica, ma l'effetto è particolarmente interessante: poichè l'assembly su disco è sempre in forma cifrata, non è possibile decompilarlo in alcun modo, neanche con ILDASM. Insomma, mi sono detto, questo potrebbe diventare il Noway per .NET! Ecco la storia che si ripete :-)

Ovviamente, tra una idea e un prodotto finito passa del tempo. Prima di tutto occorreva risolvere alcuni punti potenzialmente deboli del meccanismo. Ad esempio, un hacker abbastanza in gamba potrebbe eseguire il trace del programma per capire cosa succede al caricamento, oppure potrebbe aspettare che l'assembly sia caricato "in chiaro" in memoria e salvarlo facendo un dump della memoria stessa. Sono tecniche forse fuori dalla portata di un programmatore .NET "medio" ma non di un hacker che si rispetti.

Io non sono un hacker e soprattutto non ho le conoscenze che servono per "blindare" un programma di questo tipo, quindi la cosa più saggia da fare era rivolgersi a qualcuno che queste cose le sa fare. E non credo di conoscere nessuno più indicato di Vito Plantamura, ovvero quello che io considero il "Mark Russinovich italiano". Vito non è un hacker nel senso deteriore del termine, ma conosce i meandri di Windows meglio di qualunque altro programmatore italiano io abbia conosciuto. Se volete avere una idea di cosa è in grado di fare, date una occhiata alla sua home page. Tra le tante cose che ha fatto, c'è anche BugChecker, un "clone" nientedimeno di SoftICE, un tool che fa sembrare un giocattolo il debugger di Visual Studio. Vito è nel team di Code Architects e tra non molto potrete leggerlo su questo blog o altrove su questo sito.

Comunque, in questi mesi le ricerche e gli esperimenti sono continuate ed ora siamo vicini alla fase beta vera e propria. Qualche ora fa abbiamo "battezzato" il nuovo prodotto come CodeWall.NET, seguendo il suggerimento di un gentilissimo partecipante alla WPC (dove avevamo mostrato la prima versione in anteprima). La versione 1.0 supporta soltanto applicazioni Windows Forms ed è in grado di comprimere tutti gli assembly di una solution in un unico file compresso, che quindi si puo' scaricare più velocemente. L'applicativo deve girare in modalità full-trust e quindi al momento non supportiamo ClickOnce. Se tutto va bene il prodotto finale dovrebbe essere disponibile verso i primi di gennaio.

Devo essere sincero: abbiamo rimuginato per mesi prima di decidere di rilasciare un prodotto per la protezione degli assembly .NET. Chiunque nel settore informatico sa perfettamente che non esiste la protezione inviolabile. Per quanto sia possibile blindare un eseguibile è sempre possibile arrivare a scardinare la protezione. E' una questione di abilità dell'hacker e di tempo necessario. Oppure, se si preferisce, di quanti soldi una azienda è disposta a spendere per appropriarsi dei segreti dei propri concorrenti, assoldando se necessario qualche ragazzino particolarmente versato per queste attività.

Se è vero che qualsiasi eseguibile può essere sprotetto - disponendo di tempo e denaro in abbondanza - ciò non significa che non dobbiamo neanche tentare di rendere la vita più complicata a chi vuole rubare il nostro lavoro. E' un po' lo stesso ragionamento che facciamo quando chiudiamo a chiave casa prima di uscire: sappiamo perfettamente che un malintenzionato davvero motivato - disponendo di tempo e della attrezzatura giusta - è in grado di fare saltare qualsiasi porta blindata, ma sappiamo anche che la nostra serratura potrebbe scoraggiarlo. E magari speriamo inconsciamente che il malintenzionato sposti la sua attenzione su qualche obiettivo più accessibile.

Il compito di strumenti come CodeWall.NET è di alzare la security bar tra il nostro codice e l'hacker di turno, o il cracker come sarebbe più corretto chiamare questi individui. Noi crediamo che con tutte le tecniche che abbiamo implementato - e che non possiamo discutere pubblicamente per ovvii motivi - renderemo la vita molto, ma molto difficile a questi figuri. Abbiamo passato ore e giorni a discutere su come il programma potrebbe essere attaccato e quindi siamo passati a realizzare una contromisura adeguata. In teoria anche gli assembly protetti con CodeWall.NET sono sproteggibili, ma in pratica servirà qualcuno molto bravo e con un bel po' di tempo a disposizione...

A questo punto, però, mi interessa davvero la vostra opinione. Quando realmente sentite l'esigenza di un prodotto come CodeWall.NET? e quanto i suoi limiti potrebbero indurvi a NON utilizzarlo? Lasciate pure i vostri commenti, please.

12/6/2005 7:44:11 PM (GMT Standard Time, UTC+00:00) #  | Comments [2] | 

Visual Studio 2005 è davvero pieno di piccole e piacevoli sorprese. Parlo di quelle piccole feature che rendono la vita più semplice a noi poveri programmatori, che spesso non sono neanche menzionate negli articoli che illustrano le novità di VS2005 ma che di sicuro fanno risparmiare un po' di tempo prezioso.

Copy to Output Dir: Se selezionate un file nella finestra Solution Explorer e premete F4 potete accedere alle proprietà di quel file. Una nuova proprietà, chiamata Copy to Output Dir, permette di copiare automaticamente questo file nella directory di output al termine della compilazione. In questo modo è possibile assicurarsi che il file EXE possa accedere a file di database .mdb, file XML di supporto, ecc. Lo si puo' fare anche con una macro o con un evento post-build, ma questo metodo è molto più semplice.

Unused References (VB): Una opzione della pagina References del designer My Project permette di scartare automaticamente tutti i riferimenti agli assembly esterni che non solo usati nel progetto corrente.

Test semplificato degli User Control : Se state lavorando a uno user control per Windows Forms, non occorre creare un progetto separato al solo scopo di testare il controllo. Basta rendere il progetto dello user control il progetto di startup e premere F5. Visual Studio crea al volo per voi un form che contiene il controllo in questione. Ovviamente, per un debug più completo occorre scrivere un progetto di test ad-hoc, ma questa scorciatoia è comunque molto utile nelle prime fasi dello sviluppo.

Autorecover dei sorgenti: per default, i file sorgenti sono salvati ogni 5 minuti e il backup è conservato per una settimana. E' possibile controllare le varie impostazioni di questa feature dalla pagina AutoRecover (sotto Environment) della dialog box Tools-Options.

Open Containing Folder: il menu di contesto che appare facendo right-click sulla tab del code editor permette di aprire Windows Explorer sulla directory che contiene il file in questione, oppure di copiare nella clipboard il percorso del file corrente (in modo da poterlo facilmente aprire con un altro editor).

Export and Import Settings: è finalmente possibile memorizzare e riapplicare le impostazioni correnti di Visual Studio, per non dover perdere tempo quando si installa il prodotto su un'altra macchina o quando si fa il login sulla stessa macchina ma con uno username differente.

Tracepoints: è possibile definire dei breakpoint che non bloccano l'esecuzione, ma si limitano a emettere un messaggio (che può mostrare la posizione corrente nel programma oppure il valore di variabili e proprietà della classe corrente) oppure eseguono una macro. Nel mio libro Programming Microsoft Visual Basic 2005 mostro come creare macro che interagiscono con tracepoint, creano un file di log, testano il valore di tutte le variabili locali al metodo corrente, ecc.

Custom Visualizers: Visual Studio 2005 inaugura il concetto di visualizer, ossia dei viewer custom per vedere il contenuto di oggetti in un formato appropriato. VS2005 include dei visualizer per mostrare il contenuto di un DataSet in una grid, oppure il contenuto di una stringa contenente HTML oppure XML. E' pero' possibile costruire visualizer personalizzati per altri tipi in modo relativamente facile. Nel mio libro mostro come creare un visualizer per un file di testo e per gli oggetti di tipo Bitmap e Image, e mostro anche come un visualizer può modificare il contenuto di una variabile del programma principale.

Prevedo di tornare su alcuni di questi argomenti in futuro - in particolare sui visualizer, che sono davvero interessanti. Nel frattempo potete giocare con queste piccole feature, se non le avevate già scoperte per conto vostro.

12/1/2005 3:29:02 PM (GMT Standard Time, UTC+00:00) #  | Comments [0] | 

Che settimana!

Sono tornato a casa venerdi sera, dopo cinque giorni pieni di WPC. Ero cosi cotto che ho dormito in aereo tutto il tempo, ma ovviamente ne valeva la pena. Nonostante qualche problema tecnico - dovuto al fatto che qualche giorno prima della conf il mio Dell mi ha abbandonato e ho dovuto portare tutto su un altro notebook - le mie sessioni sono andate bene, come pure tutte quelle del team di CA.

Stamattina finalmente (per modo di dire) si torna al lavoro. La prima cosa da fare è mandare i sorgenti delle demo allo staff della WPC, per farle mettere online. Provo a zippare il tutto e arrivo a 7-8 mega di roba. Il motivo è che nello zippone sono inclusi tutti i file prodotti dalla compilazione (exe, dll, obj, ecc.), di cui si puo' fare a meno quando si mandano i sorgenti. E' la solita storia, che si ripete ogni volta che devo mandare il codice per un articolo, una conferenza, o anche semplicemente quando devo passarlo a un amico via email.

Questa volta, però, invece di rimuovere manualmente ogni file, decido di investire 2 minuti (due minuti davvero, non è un modo di dire), per scrivere una piccola utility command line che fa il lavoro per me. Grazie a un overload di Directory.GetDirectories aggiunto a .NET 2.0, è possibile ottenere in un sol colpo tutte le directory in un albero di directory, quindi si tratta semplicemente di cancellare tutti i folder di nome "obj" e "bin". Se la cancellazione fallisce viene mostrato un messaggio di errore: questo puo' accadere se un eseguibile è in esecuzione e non può essere cancellato.

Imports System.IO

Module Module1
  
Sub Main(ByVal args() As String)
      ' Use current directory if no argument has been specified
     
Dim rootDir As String = Directory.GetCurrentDirectory()
     
If args.Length > 0 Then rootDir = args(0)
     
' Read all the folder names in the specified directory tree
     
Dim dirNames() As String = Directory.GetDirectories(rootDir, "*.*", SearchOption.AllDirectories)
     
Dim errors As Integer = 0

      ' Delete all the BIN and OBJ subdirectories
     
For Each dir As String In dirNames
        
Dim dirName As String = Path.GetFileName(dir).ToLower()
           
If dirName = "bin" OrElse dirName = "obj" Then
              
Try
                 
Console.Write("Deleting {0} ...", dir)
                  Directory.Delete(dir,
True)
                  Console.WriteLine(
"DONE")
              
Catch ex As Exception
                  Console.WriteLine()
                  Console.WriteLine(
" ERROR: {0}", ex.Message)
                  errors += 1
              
End Try
           
End If
        
Next

         Console.WriteLine()
        
If errors = 0 Then
           
Console.WriteLine("All directories were removed successfully")
        
Else
           
Console.WriteLine("{0} directories couldn't be removed", errors )
        
End If
    
End Sub
End
Module

Oltre ad essere usata dalla riga di comando, potete aggiungere questa utility al menu Tools di Visual Studio, per permettere di cancellare tutti i file prodotti dalla compilazione della soluzione corrente, usando il seguente comando

               DELETEBINPATH $(SolutionDir)

dove ovviamente si suppone che DeleteBinPath sia il nome con cui avete compilato la utility. 

11/21/2005 8:08:36 AM (GMT Standard Time, UTC+00:00) #  | Comments [0] | 

Visual Studio 2005 arriva con dozzine di code snippet già pronte all'uso. A dire il vero, potremmo discutere per un po' sulla qualità e l'utilità di qualcuna di queste, però ce ne sono molte davvero ben fatte. Io ad esempio uso in continuazione prop per creare proprietà pubbliche C# che "wrappano" variabili private.

La dialog box Code Snippet Manager (menu Tools) permette di ispezionare i vari snippets uno alla volta, ma stranamente non permette di creare una lista di tutti gli snippets installati, per cui uno deve guardarseli uno a uno e annotare nome, significato, e shortcut di tastiera. Mentre preparavo il capitolo 4 di Programming Microsoft Visual Basic 2005 ho scritto questo piccolo programmino usa-e-getta che decodifica l'indice degli snippets e gli elenca a video. Essendo una applicazion console, basta redirezionare l'output su file per creare un documento da usare come riferimento.

Il programma accetta in input il percorso del file SnippetIndex.xml (VB) o SnippetsIndex.xml (C#) che contiene l'indice degli snippet. (E' curioso che questo file abbia un nome differente nei due linguaggi.) Pero' lanciandolo senza argomenti utilizza il percorso di default dell'indice degli snippet VB. Un commento nel listato spiega come usare invece l'indice di default per C#.

L'output è molto spartano - solo il nome dello snippet e la sua shortcut, suddiviso in categorie - ma potete modificare facilmente il sorgente per estrarre e mostrare altri attributi. Anzi, se fate delle modifiche, mandatemele e le posto sul sito o sul blog.

Imports System.IO
Imports System.Xml
Imports System.Text.RegularExpressions

Module Module1
  
Dim snippetsPath As String
  
Dim catNames As New Dictionary(Of String, String)

   Sub Main(ByVal args() As String)
     
' If no argument has been provided, use default path for snippets.
     
If args.Length = 0 Then
        
args = New String() {"C:\Program Files\Microsoft Visual Studio 8\Vb\Snippets\1033\SnippetIndex.xml"}
        
' Uncomment next line to list C# snippets
        
' args = New String() {"C:\Program Files\Microsoft Visual Studio 8\VC#\Snippets\1033\SnippetsIndex.xml"}
     
End If

      Dim snippetsFile As String = args(0)
      snippetsPath = Path.GetDirectoryName(snippetsFile)
     
' Load the snippet index file.
     
Dim xmlIndex As New XmlDocument()
      xmlIndex.Load(snippetsFile)
     
' We need two passes, because dirs and subdirs use a different XML element.
     
ParseSnippetIndex(xmlIndex, "//SnippetDir")
      ParseSnippetIndex(xmlIndex,
"//SnippetSubDir")
     
' Iterate over all the directories in the main snippet directory.
     
For Each dir As String In Directory.GetDirectories(snippetsPath)
         ParseSnippetFolder(dir,
"")
     
Next
   End Sub

   Sub ParseSnippetIndex(ByVal xmlIndex As XmlDocument, ByVal searchKey As String)
     
' Create the correspondence between relative paths and localized categories
     
For Each xmlEl As XmlElement In xmlIndex.SelectNodes(searchKey)
        
Dim elPath As XmlElement = DirectCast(xmlEl.SelectSingleNode("DirPath"), XmlElement)
        
Dim elName As XmlElement = DirectCast(xmlEl.SelectSingleNode("LocalizedName"), XmlElement)
         catNames.Add(elPath.InnerText, elName.InnerText)
     
Next
   End Sub

   Sub ParseSnippetFolder(ByVal dir As String, ByVal parentCategory As String)
     
' Retrieve the relative name of this subdirectory.
     
Dim relPath As String = dir.Substring(snippetsPath.Length)
     
' The default name for this category
     
Dim categoryName As String = parentCategory & Path.GetFileNameWithoutExtension(dir)
     
' Search this relative path in the snippet index.
     
Dim searchPath As String = "%InstallRoot%\Vb\Snippets\%LCID%" + relPath + "\"
     
If catNames.ContainsKey(searchPath) Then
        
' If found, use the localized category as appears in the index file
        
categoryName = parentCategory & catNames(searchPath)
     
End If
     
Console.WriteLine(categoryName.ToUpper())
     
' Parse individual snippets in this directory.
     
For Each file As String In Directory.GetFiles(dir, "*.snippet")
         ParseSnippetFile(file)
     
Next
     
' Parse all sub-categories
     
For Each subdir As String In Directory.GetDirectories(dir)
         ParseSnippetFolder(subdir, categoryName &
" / ")
     
Next
   End Sub

   Dim reTitle As New Regex("<Title>(.+?)</Title>")
   Dim reShortcut As New Regex("<Shortcut>(.+?)</Shortcut>")

   Sub ParseSnippetFile(ByVal snippetFile As String)
     
Dim text As String = File.ReadAllText(snippetFile)
     
' We use regexes to extract information for individual snippet files.
     
Dim maTitle As Match = reTitle.Match(text)
     
Dim maShortcut As Match = reShortcut.Match(text)
     
Dim title As String = maTitle.Groups(1).Value
     
Dim shortcut As String = maShortcut.Groups(1).Value
      Console.WriteLine(
" {0} [{1}]", title, shortcut)
  
End Sub
End
Module

11/13/2005 7:11:07 AM (GMT Standard Time, UTC+00:00) #  | Comments [0] | 

Devo ammettere che a me un Watch window è sempre stata sufficiente, però per il codice che stavo debuggando oggi mi veniva comoda la possibilità di usare una seconda Watch window per un gruppo differente di valori da controllare. Nessun problema, mi sono detto: sia Visual Studio 2003 che 2005 supportano ben quattro Watch window!

Solo che nei vari menu non si trova il comando Add Watch 2, o un comando simile che crea un elemento in una Watch window differente da Watch 1. Insomma, che me ne faccio di quattro Watch window se tutti i comandi di Visual Studio agiscono sempre e soltanto sulla finestra Watch 1?

Vabbè, non ci vuole molto a capire che la cosa si puo' fare mediante drag-and-drop: basta evidenziare il nome di una variabile nel sorgente e spostarlo nella Watch window che interessa. Non solo: in questo modo è anche possible spostare le variabili anche dalle finestre Autos e Locals, e tra le varie finestre Watch. (Le operazioni di dragging effettuano sempre una copia della variabile, mai lo spostamento, quindi se necessario occorre cancellare la variabile dalla finestra Watch di provenienza.) Niente di eclatante, qundi, ma non mi ero mai accorto che il drag-and-drop funzionava anche con queste finestre.

Ecco qualche altra operazione valida con le Watch window. I primi due punti offrono due alternative al drag-and-drop per creare elementi in una Watch window qualsiasi:

  • Si può creare un elemento anche eseguendo il Copy del nome di una variabile e poi usando il comando Paste in una finestra Watch.
  • Facendo doppio click sulla colonna Name dell'ultima riga vuota si può inserire il nome di una variabile qualsiasi.
  • Oltre a inserire il nome di una variabile è anche possibile inserire una espressione, ad esempio n+1 oppure s.ToUpper().
  • Facendo doppio click sulla colonna "Value" si puo' modificare il valore.
    (Questo ovviamente non è possibile se avete inserito una espressione.)
  • Selezionando il comando Hexadecimal Display dal menu di contesto, è possibile visualizzare i valori esadecimali di tutte le variabili numeriche nella finestra Watch corrente.
    (Questo comando agisce su tutte le finestre Watch e anche sulle finestre Autos e Locals.)
11/4/2005 6:38:47 PM (GMT Standard Time, UTC+00:00) #  | Comments [0] | 

Circa una settimana fa ho pubblicato una macro per racchiudere la selezione corrente in un blocco If...Then...Else. In questi giorni ho generalizzato il concetto, per ottenere una serie di macro in grado di effettuare il wrapping con tutte le strutture utilizzate più comunemente, sia in VB che C#: cicli, try...catch, Select/switch, #if, #region, namespace, ecc.

Per utilizzare queste macro, aggiungete il modulo qui sotto nella IDE delle Macro, tornate in Visual Studio, evidenziate nel code editor il testo che vi interessa, aprite il modulo WrappingMacros in Macro Explorer, e fate doppio click sulla macro che vi interessa applicare. Ovviamente, dopo aver applicato la macro dovrete sicuramente scrivere del codice aggiuntivo, ad esempio per scrivere la condizione della If, il nome della region, ecc.

Se poi associate una shortcut di tastiera alle macro più utili, è tutto ancora più semplice e immediato. Per sapere a colpo d'occhio quali shortcut sono disponibili e quali sono già prese da Visual Studio, leggete qui.

Imports System
Imports EnvDTE

Public Module WrappinglMacros

   ' --------------------------------------------------------------------
   ' Wrap the selected code inside IF, TRY, etc.
   ' --------------------------------------------------------------------

   Public Sub WrapIf()
      WrapCode("WrapIf", "If True Then\n$sel$End If\n", "if ( true )\n{\n\t$sel$}\n")
   End Sub

   Public Sub WrapIfElse()
      WrapCode("WrapIfElse", "If True Then\n$sel$Else\n\nEnd If\n", "if ( true )\n{\n\t$sel$}\nelse\n{\n}\n")
   End Sub

   Public Sub WrapTryCatch()
      WrapCode("WrapTryCatch", "Try\n$sel$Catch ex As Exception\n\nEnd Try", _
         "try\n{\n$sel$}\ncatch (Exception ex)\n{\n}\n")
   End Sub

   Public Sub WrapTryFinally()
      WrapCode("WrapTryFinally", "Try\n$sel$Finally\n\nEnd Try", "try\n{\n$sel$}\nfinally\n{\n}\n")
   End Sub

   Public Sub WrapTryCatchFinally()
      WrapCode("WrapTryCatchFinally", "Try\n$sel$Catch ex As Exception\n\nFinally\n\nEnd Try", _
         "try\n{\n$sel$}\ncatch (Exception ex)\n{\n}\nfinally\n{\n}\n")
   End Sub

   Public Sub WrapRegion()
      WrapCode("WrapRegion", "#Region ""RegionName""\n\n$sel$\n#End Region", _
         "#region RegionaName\n\n$sel$\n#endregion")
   End Sub

   Public Sub WrapSharpIf()
      WrapCode("WrapSharpIf", "#IF True Then\n\n$sel$\n#End If", "#if true\n\n$sel$\n#endif")
   End Sub

   Public Sub WrapFor()
      WrapCode("WrapFor", "For index As Integer = startIndex To endIndex\n$sel$Next", _
         "for (int index = startIndex; i <= endIndex; index++)\n{\n$sel$}\n")
   End Sub

   Public Sub WrapForEach()
      WrapCode("WrapForEach", "For Each obj As Object In collection\n$sel$Next", _
         "foreach (object obj in collection)\n{\n$sel$}\n")
   End Sub

   Public Sub WrapWhile()
      WrapCode("WrapWhile", "Do While True\n$sel$Loop", "while (true)\n{\n$sel$}\n")
   End Sub

   Public Sub WrapDoWhile()
      WrapCode("WrapDoWhile", "Do\n$sel$Loop While True", "do\n{\n$sel$} while ( true );\n")
   End Sub

   Public Sub WrapNamespace()
      WrapCode("WrapNamespace", "Namespace NamespaceName\n$sel$End Namespace", _
         "namespace NamespaceName\n{\n$sel$} // end of namespace")
   End Sub

   Public Sub WrapSelect()
      WrapCode("WrapSelect", "Select Case expression\nCase 0\n$sel$Case 1\nCase Else\nEnd Select\n", _
        
"switch ( expression )\n{\n\tcase 0:\n\t\tbreak;\n\tcase 1:\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n}\n")
   End Sub

   Public Sub WrapSyncLock()
      WrapCode("WrapIf", "SyncLock lockObject\n$sel$End SyncLock\n", "lock ( lockObject )\n{\n\t$sel$}\n")
   End Sub

   ' Helper method that replaces the selection with the specified templated text.
   ' The template can include $sel$ (the selected code) and escape sequences such as \r\n, \t
  
Private Sub WrapCode(ByVal cmdName As String, ByVal vbTemplate As String, ByVal csTemplate As String)
     
' Determine the current language by looking at the extension of the current document.
     
Dim doc As Document = DTE.ActiveDocument
     
If doc Is Nothing Then Exit Sub
     
Dim docName As String = doc.Name.ToLower()
     
Dim sel As TextSelection = DirectCast(DTE.ActiveDocument.Selection, TextSelection)
     
If sel Is Nothing Then Exit Sub

      ' Open an undo context.
     
DTE.UndoContext.Open(cmdName)
     
' Retrieve the selected text, append a newline if necessary.
     
Dim selText As String = sel.Text
     
If Not selText.EndsWith(ControlChars.NewLine) Then selText &= ControlChars.NewLine

     
' Wrap the selected text, using either the VB or the C# command
     
Dim template As String
     
If docName.EndsWith(".vb") Then
        
template = vbTemplate
     
ElseIf docName.EndsWith(".cs") Then
        
template = csTemplate
     
End If

      ' Replace CR-LF, tabs, and the selected text
     
Dim newText As String = Regex.Unescape(template).Replace("$sel$", selText)
     
' Reselect the text just added and format it. (Doesn't work in C#.)
     
Dim ep As EditPoint = sel.TopPoint.CreateEditPoint()
      sel.Text = newText
      sel.MoveToPoint(ep,
True)
      DTE.ExecuteCommand("Edit.FormatSelection")
     
' Close the undo context.
     
DTE.UndoContext.Close()
   End Sub

End Module

11/2/2005 9:00:47 AM (GMT Standard Time, UTC+00:00) #  | Comments [0] | 

Un po' in anticipo rispetto alle previsioni, la versione Professional di Visual Studio 2005 e la versione Developer di SQL Server 2005 sono disponibili per il download a tutti gli abbonati MSDN. In teoria, almeno. Da un ora sto tentando di fare il download e ricevo sempre errore. Immagino che gli sviluppatori di tutto il mondo stiano facendo lo stesso tentativo, e una cosa del genere può mandare in tilt anche il sito meglio carrozzato.

http://msdn.microsoft.com/subscriptions/

UPDATE : al ventesimo o trentesimo tentativo il download è finalmente partito. Fra 13 o 14 ore dovrei avere il mio Visual Studio nuovo di zecca sul discone. :-)

10/28/2005 9:27:29 AM (GMT Daylight Time, UTC+01:00) #  | Comments [0] | 

Le nuove funzioni di refactoring di C# 2.0 sono sicuramente impressionanti, come pure lo sono quelle di Refactor!, che arriverà gratis agli sviluppatori VB 2005. Ci sono però alcune funzioni di refactoring, tra le più semplici, che sono perfettamente ottenibili con una macro anche in Visual Studio 2003. Ecco ad esempio una macro che racchiude la selezione corrente in un blocco If..Then...Else

Sub WrapInIfThenElse()
  
' Determine the current language by looking at the extension of the current document.
  
Dim doc As Document = DTE.ActiveDocument
  
If doc Is Nothing Then Exit Sub

   Dim docName As String = doc.Name.ToLower()
  
Dim sel As TextSelection = DirectCast(DTE.ActiveDocument.Selection, TextSelection)
  
If sel Is Nothing Then Exit Sub

   ' Open an undo context.
  
DTE.UndoContext.Open("WrapInIfThenElse")
  
' Retrieve the selected text, append a newline if necessary.
  
Dim selText As String = sel.Text
  
If Not selText.EndsWith(ControlChars.NewLine) Then selText &= ControlChars.NewLine

   ' Embed the selected text in an If...Then...Else block.
  
' Replace the selection with the new text and format the document.
  
Dim newText As String
  
If docName.EndsWith(".vb") Then
     
newText = String.Format("If True Then{0}{1}Else{0}{0}End If{0}", _
         ControlChars.NewLine, selText)
  
ElseIf docName.EndsWith(".cs") Then
     
' Replace the selection with 
     
newText = String.Format("if ( true ){0}{{ {0}{1} }}{0}else{0}{{ {0} }}{0}", _
         ControlChars.NewLine, selText)
  
End If

   ' Reselect the text just added and format it. (Doesn't work in C#.)
  
Dim ep As EditPoint = sel.TopPoint.CreateEditPoint()
   sel.Text = newText
   sel.MoveToPoint(ep,
True)
   DTE.ExecuteCommand("Edit.FormatSelection")
  
' Close the undo context.
  
DTE.UndoContext.Close()
End Sub

La macro funziona molto bene in Visual Basic, un po' meno bene in C#. In particolare, per qualche motivo il comando Edit.FormatSelection non funziona in questo linguaggio, quindi occorrerà riselezionare il testo e richiamarlo manualmente, dal sottomenu Advanced del menu Edit oppure premendo i tasti Ctrl+K, Ctrl+F.

Modificando solo un paio di righe è facile creare numerose macro con funzionalità simili, per eseguire il wrapping all'interno di strutture For, For Each, Try...Catch...Finally, e cosi' via.

Una piccola macro come questa certo non moltiplicherà la vostra produttività in modo esponenziale, ma avercela sempre a portata di mano fa risparmiare 3-4 secondi ogni volta. Usatela 15-20 volte al giorno e alla fine della giornata potrete rimanere un minuto in più alla macchina del caffè, senza troppi sensi di colpa :-)

10/26/2005 5:47:14 PM (GMT Daylight Time, UTC+01:00) #  | Comments [0] | 

Oggi ho speso un po' di tempo per fare il porting dalla beta2 alla RC1 di un sito ASP.NET 2.0. Non ci sono stati grandi problemi: se cambia il nome di una proprietà si stà veramente presto a capire qual è il nome nuovo, anche consultanto questa pagina. Ci sono però un paio di cambiamenti significativi la cui soluzione potrebbe non essere immediata, e che quindi ho deciso valesse la pena riportare nel blog. Io mi sono accorto di questi 2 cambiamenti solo ora, perchè le CTP intermedie tra la beta2 e la RC1 non sono mai riuscito ad installarle completamente con successo. Quindi può darsi che le cose fossero cambiate già prima.

Del primo cambiamento me ne sono accorto ricevendo la seguente eccezione all'esecuzione del codice per ottenere il profilo di un utente (quello corrente o un altro): {"Procedure or Function 'aspnet_Profile_GetProperties' expects parameter '@TimeZoneAdjustment', which was not supplied."}. Dopo aver cercato su Google per qualche minuto senza esito, mi sono creato un nuovo progettino web con la RC1 e ho controllato che i profili funzionassero. Il responso è stato ovviamente positivo. Allora sono andato a controllare la sproc incriminata, aspnet_Profile_GetProperties, nel DB che avevo nel progetto beta2, e nel progetto creato ex-novo con la RC1. Nel primo caso la sproc ha un terzo parametro chiamato @TimeZoneAdjustment, nel secondo caso il terzo parametro è @CurrentTimeUtc. E' quindi stato ovvio che il motivo dell'eccezione è che il codice della classe SqlProfileProvider aggiornata chiamava la stored procedure passando @CurrentTimeUtc, mentre la vecchia sproc presente nel DB si aspettava @TimeZoneAdjustment. La soluzione consiste nell'eseguire il wizard di aspnet_regsql.exe, selezionando l'opzione per eliminare il supporto ai servizi di ASP.NET 2.0 (membership, profili, logging, caching, personalization) dal database in questione. Fatto ciò si esegue nuovamente il tool riaggiungendo il supporto per tutti quelle funzionalità, e quindi di fatto ricreando tutti gli oggetti necessari per il loro funzionamento (tabelle, sproc, trigger), con lo schema aggiornato. A parte aver perso le informazioni sui pochi account di test che avevo creato (erano 4 record, quindi non valeva neanche la pena fare il backup) il risultato è stato perfetto.

Il secondo problema è che tutti i controlli ObjectDataSource collegati a un domain object e usati come data source dai vari controlli GridView e DetailsView sollevavano eccezione all'esecuzione dei comandi Update e Delete. Il problema qui era che nella beta2 i valori originali del record da cancellare/aggiornare (quindi ad esempio l'ID che identifica il record) venivano passati a parametri del metodo chiamati original_{NomeFieldQui}, ad esempio original_ID. Il prefisso poteva anche essere cambiato tramite la proprieta OldValuesParameterFormatString dell'ObjectDataSource, che per default era appunto impostata su "original_{0}". Tutte le mie classi usavano quindi questa convenzione per ricevere correttamente l'ID chiave nei metodi Update e Delete. Ora nella RC1 il valore di default di questa proprietà è {0}, ovvero il nome del campo senza più nessun prefisso. Ne consegue che l'ObjectDataSource non cercherà più un parametro nominato original_ID nella firma del metodo, ma ID e basta. Non lo trova e solleva eccezione. Le soluzioni possibili sono 2: o rinominate i parametri dei vostri metodi togliendo il prefisso (e lasciando quindi "id" o "ID", il controllo non è case-sensitive) oppure ripristinate il valore di OldValuesParameterFormatString su "original_{0}". Io ho preferito seguire la prima opzione per essere in linea con quanto fa di default VS.NET, e la cosa non ha preso comunque troppo tempo. Questa modifica al controllo ObjectDataSource è stata suggerita dal feedback di molti sviluppatori che hanno lavorato con le beta2, e che trovavano poco intuitivo dover nominare i parametri dei loro metodi con quel prefisso. Ecco l'annuncio semi-ufficiale da uno dei componenti del team di ASP.NET. Io sono in effetti d'accordo! Se dovesse comunque esserci la necessità di ottenere in input sia il valore corrente che quello originale di un campo, sarà comunque sempre possibile reimpostare un qualche prefisso per OldValuesParameterFormatString e avere il meccanismo funzionante esattamente come prima.

Tutto sommato il porting dalla beta2 alla RC1 è stato molto semplice, non ho fatto neanche in tempo a cominciare ad imprecare :-) Sempre che non ora spuntino sorprese poco piacevoli...ma sono ottimista!

10/25/2005 1:32:25 AM (GMT Daylight Time, UTC+01:00) #  | Comments [0] | 

Mi capita spesso di dover copiare pezzi di un testo sotto forma di commento nei miei sorgenti. Non è una operazione difficile, però è pur sempre una perdita di tempo, perchè spesso non si può semplicemente eseguire il copia e incolla e poi il comando Edit-Comment Selection perchè - almeno in VB - l'editor di Visual Studio cerca di interpretare il testo come codice e quindi ne rovina la formattazione. A questo si aggiunge il fatto che spesso sono costretto a rivedere tutte le andate-a-capo nel testo per evitare righe troppo lunghe, perchè i listati che appaiono nei libri non possono essere più lunghi di N caratteri (tipicamente 92 caratteri, per i libri Microsoft Press). Insomma, una bella seccatura.

 

Oggi ho deciso di porre fine a questa perdita di tempo, scrivendo una macro che faccia questo lavoro al mio posto. E' un piccolo modo per aumentare la produttività e concentrarsi sulle cose davvero importanti. Se anche a voi piacciono i listati ordinati, sono certo che la troverete utile.

 

La prima difficoltà in cui mi sono imbattuto è il fatto che, per qualche arcano motivo, il metodo Clipboard.GetObjectData restituisce sempre Nothing quando lo si chiama da una macro, quindi non è possibile usare quel metodo per leggere il contenuto della clipboard. Per qualche minuto ho accarezzato l'idea di lavorare direttamente con le API oppure di scrivere una DLL che eseguisse questo compito, poi ho trovato la classica soluzione a-ha!:

 

' Retrieve the text in the clipboard

Dim tb As New TextBox

tb.Multiline = True

tb.WordWrap = False

tb.ScrollBars = ScrollBars.Both

tb.Paste()

Dim text As String = tb.Text

 

Questo codice funziona quasi sempre bene. Ogni tanto, però, ho notato che il metodo Paste fallisce con un messaggio di errore un po' criptico: "Class already exists". Non riesco a spiegarmi il motivo...e tutto sommato non voglio neanche saperlo. Ho notato però che tutto funziona bene se l'editor delle macro è chiuso, quindi nell'uso normale non ci dovrebbero essere problemi. Se però incappate una volta in questo errore, da quel momento in poi la macro andrà sempre in errore. L'unico modo per evitarlo è uscire e rientrare Visual Studio. Come ho detto, accade solo durante lo sviluppo delle macro, quindi non è un problema davvero fastidioso.

 

Ecco il sorgente completo della macro che esegui il paste del contenuto corrente della clipboard sotto forma di commento:

 

Imports System.Text.RegularExpressions

 

Module UsefulMacros

 

   Public Sub PasteAsComment()

      PasteAsComment("80")

   End Sub

 

   Public Sub PasteAsComment(ByVal lineLength As String)

      Dim maxLength As Integer = CInt(lineLength)

      ' Determine the language by looking at the extension of the current document.

      Dim doc As Document = DTE.ActiveDocument

      If doc Is Nothing Then Exit Sub

      Dim docName As String = doc.Name.ToLower()

 

      ' Determine the caret position

      Dim sel As TextSelection = DirectCast(DTE.ActiveDocument.Selection, TextSelection)

      Dim ep As EditPoint = sel.ActivePoint.CreateEditPoint()

 

      ' Determine the comment prefix

      Dim commentPrefix As String = "" 'Space(ep.DisplayColumn)

      If docName.EndsWith(".vb") Then

         commentPrefix &= "' "

      ElseIf docName.EndsWith(".cs") Then

         commentPrefix &= "// "

      Else

         ' Unsupported language

         Return

      End If

 

      ' Retrieve the text in the clipboard

      Dim tb As New TextBox

      tb.Multiline = True

      tb.WordWrap = False

      tb.ScrollBars = ScrollBars.Both

      tb.Paste()

      Dim text As String = tb.Text

 

      ' Split in lines not longer than MaxLength

      Dim result As String = commentPrefix

      Dim currLineLength As Integer = commentPrefix.Length

      For Each m As Match In Regex.Matches(text, "\S+\s*")

         If currLineLength + m.Length > maxLength Then

            result &= ControlChars.CrLf

            result &= commentPrefix

            currLineLength = commentPrefix.Length

         End If

         result &= m.Value

         currLineLength += m.Length

         If m.Value.IndexOf(ControlChars.CrLf) > 0 Then

            result &= commentPrefix

            currLineLength = commentPrefix.Length

         End If

      Next

      result &= ControlChars.CrLf

      sel.Insert(result)

   End Sub

End Module

 

Come vedete le macro sono in realtà due. La versione senza argomenti crea righe di commento lunghe 80 caratteri: presumibilmente è la versione che userete probabilmente più spesso e vi conviene associarla a uno shortcut di tastiera. Al contrario, la versione con argomento può essere usata solo dalla finestra di comando per eseguire il paste con word wrapping alla colonna che desiderate voi. Ecco come potete eseguire il seguente comando dalla finestra Immediate per eseguire il paste con righe non più lunghe di 60 caratteri

 

Macros.MyMacros.UsefulMacros.PasteAsComment 60

 

Molto probabilmente vi stuferete molto presto di scrivere tutto questi caratteri. In tal caso, vi conviene associare un alias - ad esempio PasteCom - al comando, in questo modo:

 

alias PasteCom Macros.MyMacros.UsefulMacros.PasteAsComment

 

Dopo la creazione dell'alias, i caratteri da digitare sono molti di meno:

 

PasteCom 60

 

Questo è tutto per oggi. Ci vediamo alla prossima macro :-)

 


 

UPDATE: Andrea Ferendeles ha suggerito un modo per leggere il contenuto della clipboard da una macro meno macchinoso del mio e che non soffre dei problemi citati. (vedere i commenti). Ho ritoccato il codice di Andrea per adeguarlo alla macro, che ora è diventata

 

Public Sub PasteAsComment()
 
PasteAsComment("80")
End Sub

Public Sub PasteAsComment(ByVal lineLength As String)
  
Dim maxLength As Integer = CInt(lineLength)
  
' Read the text in the clipbard, through the Selection.Paste method.
  
' (Thanks to Andrea Ferendeles for the suggestion.)
  
Dim sel As TextSelection = DirectCast(DTE.ActiveDocument.Selection, TextSelection)
  
Dim sp As EditPoint = sel.ActivePoint.CreateEditPoint()
  
sel.Paste()
  
sel.MoveToPoint(sp,
True)

   ' Read this text, then delete it
  
Dim text As String = sel.Text
  
sel.Delete()

   ' Split in lines not longer than MaxLength
  
Dim result As String = ""
  
Dim currLineLength As Integer = 0
  
For Each m As Match In Regex.Matches(text, "\S+\s*")
     
If currLineLength + m.Length > maxLength Then
        
result &= ControlChars.CrLf
        
currLineLength = 0
     
End If
     
result &= m.Value
     
currLineLength += m.Length
     
If m.Value.IndexOf(ControlChars.CrLf) > 0 Then
        
currLineLength = 0
     
End If
   Next
 
  result &= ControlChars.CrLf

   ' Paste the text in the code editor
  
sp = sel.ActivePoint.CreateEditPoint()
  
sel.Insert(result)
  
sel.MoveToPoint(sp,
True)

   ' Comment and reformat it
   DTE.ExecuteCommand("Edit.CommentSelection")
  
sel.SmartFormat()
End Sub

Ho anche preso da Andrea l'idea di usare il comando DTE.ExecuteCommand per creare i commenti, che funziona con tutti i linguaggi supportati da Visual Studio. In questo modo, però, i caratteri dei commenti non sono considerati ai fini della larghezza massima delle righe, a differenza della macro originale, ma non credo sia un gran problema, no? 

10/17/2005 9:09:01 PM (GMT Daylight Time, UTC+01:00) #  | Comments [0] | 

Ieri ho postato una macro che converte i field in properties. Qualche minuto fa ho rivisto il codice per supportare anche i campi marcati con readonly. Se applicate la macro a tali campi, otterrete (ovviamente!) delle proprietà a sola lettura. In VS2005 potreste anche modificare la stringa assegnata alla variabile repPatternReadonly per generare proprietà con un blocco set privato, in modo da avere una proprietà a sola lettura se vista dall'esterno della classe ma assegnabile dal codice che gira nella classe stessa.

10/11/2005 8:55:34 PM (GMT Daylight Time, UTC+01:00) #  | Comments [0] | 

Una cosa che mi stupisce sempre molto è vedere come molti programmatori passino ore a discutere se sia più produttivo VB o C# o Java - magari discettando sulle feature relativamente "minori" del proprio linguaggio - e poi non conoscano a fondo proprio il tool con cui passano la maggior parte del tempo, ovvero l'IDE in cui scrivono e testano il codice. In particolare, ci sono decine e decine di feature di Visual Studio che nella maggior parte dei casi sono sconosciute ai più. Dopo tanti anni, capita anche a me di scoprirne continuamente di nuove.

Il modo migliore per aumentare la produttività con Visual Studio è imparare a scrivere delle macro per automatizzare i compiti più ripetitivi. Certo, ci sono sul mercato tantissimi add-in - anche freeware - che permettono di automatizzare dei compiti ripetitivi, ma capita raramente che facciano esattamente quello che vogliamo. Se volete davvero padroneggiare Visual Studio, occorre proprio rimboccarsi le maniche e imparare a scrivere un po' di macro. Nei casi più semplici, potete semplicemente usare il registratore di macro e modificare poi il codice generato.

Ad esempio, io spesso nei miei prototipi di classe uso dei campi pubblici, ma quando poi occorre scrivere del codice "di produzione" passo un sacco di tempo a convertire il campo in una variabile privata "wrappata" da una proprietà pubblica. Per capirci, parto con questo codice nel prototipo:

' The name of the element
Public Name As String = "Francesco"

e arrivo a questa proprietà nella applicazione finita

' The name of the element
Private
m_Name As String = "Francesco"

Public Property Name() As String
   Get
      Return m_Name 
   End Get
   Set(ByVal Value As String)
      m_Name = Value
   End Set
End Property

Un giorno finalmente ho deciso di scrivere una macro che automatizza questo lavoro. Ci ho perso una mezz'ora, ma il tempo che mi ha fatto risparmiare da allora è davvero incalcolabile. Per risparmiarvi anche quella mezz'ora (che potrebbe essere un po' più di mezz'ora se non avete dimestichezza con il modello a oggetti di Visual Studio e con le regular expression), ecco a voi la macro.

Imports EnvDTE
Imports System.Text.RegularExpressions

Public Module CodeArchitectsMacros
  
Dim repPattern As String
   Dim repPatternReadOnly As String

   Sub ConvertVariables()
     
' Determine current language by looking at the extension of the current document.
     
Dim doc As
Document = DTE.ActiveDocument
     
If doc Is Nothing Then Exit
Sub
     
Dim docName As String
= doc.Name.ToLower()

      ' Read all the text lines touched by the selection.
     
Dim sel As TextSelection = CType
(DTE.ActiveDocument.Selection, TextSelection)
     
Dim ed1 As
EditPoint = sel.AnchorPoint.CreateEditPoint()
     
ed1.EndOfLine() : ed1.StartOfLine() : ed1.StartOfLine()
     
Dim ed2 As
EditPoint = sel.BottomPoint.CreateEditPoint()
      ed2.EndOfLine()
     
Dim text As String
= ed1.GetText(ed2)

      ' The find and replacement pattern depend on the current language.
     
Dim findPattern As
String
     
If docName.EndsWith(".vb")
Then
         findPattern = "(?<indent>[\t ]+)Public\s+(?<static>Shared\s+)?(?<readonly>ReadOnly\s+)?" _
            & "(?<name>\w+)\s+As\s+(?<type>\S+)(?<init>.*?)\n"
         ' {0}=property name, {1}=property type, {2}=static keyword, {3} initvalue,
         ' {4}=CR-LF, {5}=Tab, {6}=indent
        
repPattern = "{6}Private {2}m_{0} As {1}{3}{4}" _
            & "{6}Public {2}Property {0}() As {1}{4}" _
            & "{6}{5}Get{4}" _
            & "{6}{5}{5}Return m_{0}{4}" _
            & "{6}{5}End Get{4}" _
            & "{6}{5}Set(ByVal Value As {1}){4}" _
            & "{6}{5}{5}m_{0} = Value{4}" _
            & "{6}{5}End Set{4}" _
            & "{6}End Property{4}{4}"
         repPatternReadOnly = "{6}Private {2}ReadOnly m_{0} As {1}{3}{4}" _
            & "{6}Public ReadOnly {2}Property {0}() As {1}{4}" _
            & "{6}{5}Get{4}" _
            
& "{6}{5}{5}Return m_{0}{4}" _
            & "{6}{5}End Get{4}" _
            & "{6}End Property{4}{4}"

     
ElseIf docName.EndsWith(".cs")
Then
         ' Notice the (?.*;) element is needed to ensure that public fields are matched,
         ' but public properties aren't
         
findPattern = "(?<indent>[\t ]+)public\s+(?<static>static\s+)?(?<readonly>readonly\s+)?" _
            "(?<type>\S+)\s+(?<name>\w+)(?=.*;)(?<init>.*?)\n"

        
' {0}=property name, {1}=property type, {2}=static keyword, {3} initvalue,
         ' {4}=CR-LF, {5}=Tab, {6}=indent
        
repPattern = "{6}private {2}{1} m_{0}{3}{4}" _
            & "{6}public {2}{1} {0}{4}" _
            & "{6}{{{4}" _
           
& "{6}{5}get {{ return m_{0}; }}{4}" _
            & "{6}{5}set {{ m_{0} = value; }}{4}" _
            & "{6}}}{4}{4}"
         repPatternReadOnly = "{6}private {2}readonly {1} m_{0}{3}{4}" _
            & "{6}public {2}{1} {0}{4}" _
            & "{6}{{{4}" _
            & "{6}{5}get {{ return m_{0}; }}{4}" _
            & "{6}}}{4}{4}"

     
End If

     ' Replace the text. Add a trailing CR-LF but remove it later.
    
Dim replaceText As String = Regex.Replace(text + ControlChars.CrLf, findPattern, _
       
AddressOf ReplaceWithProperty)
     ed1.ReplaceText(ed2, replaceText.Substring(0, replaceText.Length - 2), 0)

   End Sub

   ' Private callback function for the Replace method
  
Private
Function ReplaceWithProperty(ByVal m As Match) As
String
      Dim pattern As String = repPattern
     
If m.Groups("readonly").Length > 0 Then pattern = repPatternReadOnly
     
Return String.Format(pattern, m.Groups("name").Value, m.Groups("type").Value, _
         m.Groups("static").Value, m.Groups("init").Value, ControlChars.CrLf, _
         ControlChars.Tab, m.Groups("indent").Value)

   End
Function

End Module

Non fatevi ingannare dal numero limitato di istruzioni in questa macro. Infatti, grazie alle regular expression usate a dovere, la macro funziona sia in VB che in C#, permette di convertire una o più variabili in un solo colpo, preserva il valore iniziale e l'attributo Shared/static, e preserva anche qualsiasi istruzione che dovesse trovarsi in mezzo alle dichiarazioni. In pratica, quindi potete anche evidenziare il codice di una intera classe e convertire tutte le sue variabili pubbliche in proprietà, con un solo click del mouse! :-)

Notate che la macro crea automaticamente una variabile che si chiama m_propname; ovviamente potete modificare l'assegnazione a repPattern per utilizzare la naming convention che preferite. Oppure i programmatori C# possono modificare il codice per generare i blocchi get/set spalmati su più righe. (A me non piace sprecare spazio nell'editor se non è realmente necessario.)

UPDATE: Ho aggiornato la macro per tenere conto delle variabili marcate con le keyword ReadOnly, che ovviamente generano proprietà a sola lettura. In VS2005 si può modificare il codice assegnato a repPatternReadonly per generare proprietà con un blocco set di tipo privato.

Per installare e usare questa macro seguite i passi seguenti:

1) usate il comando Tools-Macros-Macro IDE (oppure premete Alt+F11) per mostrare l'IDE per le macro
2) Nel macro IDE, selezionate il progetto MyMacros, poi usate il comando Projects-Add Module per creare un nuovo modulo chiamato CodeArchitectsMacros, poi incollate il codice precedente nel nuovo modulo
3) tornate in Visual Studio, e mostrare il Macro Explorer usando il comando Tools-Macros-Macro Explorer (oppure premete Alt+F8); nella finestra Macro Explorer, espandete il nodo MyMacros e poi espandete il modulo CodeArchitectsMacros
4) evidenziate nel codice VB o C# una o più dichiarazioni di variabili pubbliche, poi fate doppio click sul metodo ConvertVariables per trasformarle in proprietà.
5) opzionalmente, nella dialog Tools-Options assegnate una shortcut di tastiera alla macro, in modo da poterla richiamare senza dover prima aprire il Macro Explorer. Ovviamente potete anche assegnare la macro a un pulsante sulla toolbar di Visual Studio.

Se lavorate molto con classi e proprietà, fatemi un favore: installate la macro e fra un mese fatemi sapere quanto tempo vi ha fatto risparmiare. E fatemi anche sapere se vi piacerebbe che pubblicassi altri productivity enhancement di questo tipo.

10/10/2005 11:33:16 AM (GMT Daylight Time, UTC+01:00) #  | Comments [0] | 

J

 

Problema: docking di controlli .NET direttamente nella client area di Outlook con VSTO per Visual Studio 2005

 

Il file contenente il codice sorgente: HtmlViewModified.zip (256.37 KB)

 

Impegnato per conto di un cliente che sta ricrivendo una serie di applicazioni direttamente dentro Office System con .NET 2.0 e VS2005 – e Outlook 2003 in particolare - mi sono imbattuto in un “piccolissimo” problema non di poco conto. Benche’ con gli altri      prodotti di Office sia particolarmente semplice mostrare e usare form di ogni genere preparate in .NET, con Outlook 2003 ci siamo subito imbattuti in una serie di limiti che inizialmente sembravano insuperabili e decisamente antipatici. Per la precisione, a prima vista sembra che sia impossibile “dockare” le proprie form .NET all’interno dell’area client principale di lavoro di Outlook (l’area della inbox per intenderci) per scrivere applicazioni di produttivita’ utilizzando .NET, direttamente dentro Outlook. Pero’ ... con un po’ di fortuna e di intuito posso ora dire che l’empasse e’ durata relativamente poco. Infatti scaricato il preziosissimo esempio HtmlView disponibile sul sito di MSDN, che mostra come fare un po’ di report con del HTML plasmato via .NET, l’idea di ospitare le form (controlli windows form) .NET all’interno del HTML viewer (e’ il controllo del browser IE embedded dentro outlook) si e’ fatta subito strada. Devo ammettere che la mia bassissima conoscenza di HTML (che proprio non mi entra in testa - che ci volete fare) e’ stata, alla fine, la parte piu’ complessa del tutto. Per il resto il lavoro di inserimento delle form (controlli .NET) dentro Outlook e’ stato tutto in discesa e senza troppi problemi o intoppi. Descrivo brevemente per punti una serie di considerazioni e consigli da seguire per studiare il materiale allegato che mostra la soluzione del problema:

 

-          aggiornate la connection string nel .config che fa una piccola query sul db northwind per mostrare un diagramma a torte dentro un report embedded nella form schiacciando uno dei due pulsanti che troverete in testa alla vista dentro Outlook dopo aver lanciato l’applicazione e selezionata la cartella HtmlView figlia della cartella Inbox.

-          la classe BaseActiveXContainer (che ho copiato da qualche parte e modificato – l’autore mi perdoni per questo)  contiene del pratico codice per registrare i controlli .net come componenti COM

-          Generate tutte le volte un nuovo GUID per ogni nuovo controllo. Non usate la tecnica CCP (Cut Copy e Paste J - Copyright del mio amico Pierpaolo R.) con gli occhi bendati. Brutte sorprese vi aspettano.

-          Nel file AssemblyInfo.cs (aggiunto a manina perche’ in disuso con vs2005 – decideremo una best practice spero tra pochissimo ...) ho inserito degli attributi assembly wide per evitare problemi di security per caricare dll che non sopportano di essere chiamate da assembly “partially trusted”.Senza, molte LoadAssembly dinamiche rischiano di dare parecchi problemi.

 

[assembly: AllowPartiallyTrustedCallers]

[assembly: FileIOPermission(SecurityAction.RequestMinimum, Unrestricted = true)]

[assembly: SecurityPermission(SecurityAction.RequestMinimum, Execution = true)]

[assembly: RegistryPermission(SecurityAction.RequestMinimum, Unrestricted = true)]

[assembly: UIPermission(SecurityAction.RequestMinimum, Unrestricted = true)]

 

-          In testa alla classe HtmlViewContainer trovate una serie di attributi che vi permetteranno di riesporre attravers COM i vostri controlli .NET da “agganciare” dentro Office. La stessa classe contiene una serie di proprieta’ statiche che espongono globalmente nella applicazione i puntatori alle istanze di alcuni oggetti importanti di Outlook per interoperare con lo stesso con interfacce duali COM per supportare Automation.

-          Il file “.htm” del progetto allegato mostra come invocare metodi dell’oggetto ospitato dal HTML, che ospita il controllo .NET nella pagina, via jscript e automation.

-     Preparatevi mentalmente ad usare caspol o l'utility presente negli administrative tools per dare full trust ad eventuali DLL esterne bisognose di essere trattate in tal modo. Sappiate che il vostro assembly dentro la sandbox di Outlook e partially trusted. Vi consiglio di firmare digitalmente le vostre DLL e fare full trust su machine con le strong name degli assembly o direttamente un certificato buono. 

 

- altre ed eventuali ... prossimamente :) stay tuned

 

Una piccola schermata che mostra quello che si puo' fare dentro Outlook con .NET e VSTO per VS2005 completato in treno mentre tornavo a casa :). Nel codice trovate tutto quello che serve per far parlare il vostro controllo con Outlook dall'interno dell'explorer html di Outlook.

Enjoy !

Sto lavorando su dei proof of concept per Microsoft per Office Word ed Excel avanzatissimi. Spero di condividere al piu' presto con voi il materiale che sto producendo.

Giuseppe

10/7/2005 8:29:27 AM (GMT Daylight Time, UTC+01:00) #  | Comments [0] | 

In un post del maggio scorso mi lamentavo del fatto che in Visual Studio 2005, scrivendo Implements IDisposable nella dichiarazione di una classe si otteneva il pattern Dispose-Finalize completo. Un sacco di codice, ma soprattutto un codice sbagliato, in quanto una classe disposable in genere NON deve implementare il metodo Finalize, in quanto il metodo Finalize serve solo se la classe crea e utilizza risorse unmanaged.

Le classi dotate di Finalize richiedono qualche ciclo di CPU in più durante l'istanziazione, ma questo non era il problema principale a mio avviso. Il fatto che Visual Studio creasse automaticamente codice errato mi dava un fastidio quasi fisico: chi avrà mai preso questo decisione a Redmond?

Beh, evidentemente non sono stato l'unico a lamentarsi, perchè ho appena scoperto - con molto piacere - che questa "feature" è stata rimossa nella CTP di Agosto (e immagino anche nella RC). Ora quando digitate Implements IDisposable, VS2005 risponde creando il codice seguente:

Class FooBar
  
Implements IDisposable

   Private disposedValue As Boolean = False ' To detect redundant calls

   ' IDisposable
  
Protected Overridable Sub Dispose(ByVal disposing As Boolean)
      If Not Me.disposedValue
Then
        
If disposing
Then
           
' TODO: free unmanaged resources when explicitly called
        
End
If
        
' TODO: free shared unmanaged resources
     
End
If
     
Me.disposedValue =
True
  
End Sub

#Region " IDisposable Support "

   ' This code added by Visual Basic to correctly implement the disposable pattern.
  
Public Sub Dispose() Implements IDisposable.Dispose
     
' Do not change this code. Put cleanup code in Dispose(ByVal disposing As Boolean) above.
     
Dispose(True)
      GC.SuppressFinalize(Me)
   End Sub

#End Region
End
Class

E' bello, una volta tanto, poter dire a posteriori "lo avevo detto io!" :-)

10/5/2005 7:35:35 PM (GMT Daylight Time, UTC+01:00) #  | Comments [0] | 

Sto correggendo il capitolo dedicato al namespace My, e sto aggiungendo alcuni dettagli che avevo tralasciato nella prima stesura, come la creazione di custom setting provider (per salvare i settings su altri medium oltre al classico file di configurazione, ad esempio un database) e la possibilità di eseguire il binding delle proprietà di un controllo con le impostazioni utente. I custom setting provider sono abbastanza complessi e interessano un numero relativamente ristretto di programmatori, mentre il binding dei setting è un argomento decisamente molto più semplice e che non mancherà di suscitare interesse di chiunque lavori con i Windows Forms. Insomma, l'argomento ideale per un post sul blog di dotnet2themax.

Ho trovato molti articoli e post sull'oggetto My.Settings di Visual Basic 2005 (nonchè dell'omologo Settings di C#), ma quasi tutti omettono di sottolineare il fatto che è possibile eseguire il binding di uno user setting con una proprietà di un form, incluso le proprietà Size e Location. Questa feature permette - tra le tante cose - di ritrovare ciascun form della applicazione nella posizione e nella forma in cui l'utente lo aveva lasciato la volta precedente (nella stessa sessione di lavoro o in una sessione precedente). L'infrastruttura di .NET 2.0 si prende carico di assegnare la proprietà quando il form viene caricato e di salvarla nel file di configurazione quando la proprietà è modificata.

Figura 1: Il designer di Visual Studio 2005 permette di definire settings a livello utente e applicazione.

 

Figura 2: Eseguire il binding di una proprietà con una impostazione utente.

 

Il bello è che non dovete scrivere neanche mezza riga di codice. Infatti, è sufficiente definire le proprietà in questione nella pagina Settings del designer My Project (Visual Basic) oppure Properties (C#), ad esempio MainFormLocation e MainFormSize (Figura 1); è essenziale che lo scope di queste impostazioni sia User, poichè le impostazioni di tipo Application non sono modificabili. A questo punto potete selezionare il form, passare alla finestra Properties, aprire la sezione (Application Settings), cliccare sulla freccia accanto al nome delle proprietà ClientSize e Location, e selezionare lo user setting da mettere in binding. Se non avete ancora create il setting in questione, basta cliccare sull'elemento New. (Figure 2)

Come ho già accennato, il dettaglio notevole è che il valore di queste impostazioni viene aggiornato automaticamente quando spostate e ridimensionate il form. Ovviamente si possono mettere in binding altre proprietà, ad esempio Text, BackColor, ecc. Facendo la stessa cosa per tutti i form della applicazione abbiamo a disposizione un meccanismo di persistenza delle impostazioni dei form semplice e potente, ancora una volta senza scrivere codice!

Ovviamente è possibile fare lo stesso con le proprietà dei singoli controlli. Non tutte le proprietà sono in grado però di notificare a .NET il fatto che sono state modificate. Perchè ciò avvenga il controllo deve implementare l'interfaccia IBindableComponent e deve implementare un evento XxxxChanged per la singola proprietà, oppure implementare la nuova interface INotifyPropertyChanged. La maggior parte dei controlli Windows Forms implementano queste interfacce, ma non tutti. Ad esempio, il controllo ToolStripItem non la implementa. In questo caso la proprietà sarà impostata correttamente quando il form viene mostrato, ma il valore del setting deve essere aggiornato via codice. 

10/3/2005 3:44:50 PM (GMT Daylight Time, UTC+01:00) #  | Comments [0] | 

Alcuni sample recuperabili via Internet per provare e studiare VSTO (Visual Studio Tools for Office - pronunciato VISTO) per Outlook potrebbero dare problemi di compilazione. Io ho scaricato ad esempio questi sample da msdn.microsoft.com e ho dovuto apportare le seguenti modifiche nell'evento di startup utilizzando il metodo protetto GetPrimaryControl per sistemare il tutto:

Prima della modifica:

private void ThisApplication_Startup(object sender, System.EventArgs e)
{
   
// Initialize the event tracker object.
 
_eventTracker = new EventTracker(this);
 
// Create custom menu items.
 
CreateMenu();
}

Dopo la modifica, utilizzando il metodo GetPrimaryControl:

private void ThisApplication_Startup(object sender, System.EventArgs e)
{
   
// Initialize the event tracker object.
 
_eventTracker = new EventTracker(this.GetPrimaryControl());
 
// Create custom menu items.
 
CreateMenu();
}

Vi consiglio vivamente di dare una occhiata a questa tecnologia perchè i risultati sono veramente notevoli. Ovviamente le stesse modifiche valgono anche per gli esempi in VB.

10/1/2005 9:35:02 PM (GMT Daylight Time, UTC+01:00) #  | Comments [0] | 

Oggi riflettevo sul fatto che ho cominciato la mia carriera di speaker a conferenze per programmatori esattamente 10 anni fa, il 27 ottobre 1995 al VB Forum organizzato da Edizioni Infomedia e la rivista Visual Basic Journal, che avevo da poco fondato. Come tutte le prime volte, ero super-emozionato, parlavo troppo velocemente, avevo un volume discontinuo, non sapevo dove tenere le mani, ecc. Dopo dieci anni sono sicuramente migliorato un bel po' (ma ho ancora margini per migliorare!) e credo di poter passare qualche consiglio a chi comincia solo ora.

In questo post, però, non mi preme offrire una lista di consigli per diventare un bravo oratore e mi limiterò invece a dare qualche spunto di riflessione su come preparare al meglio le demo con Visual Studio. Ci sono tante piccole cose che è possibile fare in questo campo e che relativamente pochi speaker fanno. Inoltre, si tratta di piccoli consigli che possono essere utili anche a coloro che conducono corsi di programmazione in aula, che sono certamente un numero maggiore di coloro che hanno la fortuna di salire su un palco a parlare davanti a centinaia di persone.

Avevo preparato questa lista qualche anno fa, ad uso e consume del team di Code Architects, che oramai sono speaker collaudati e non ne hanno più bisogno. La lista è divisa in due parti: la prima dedicata alla fase di preparazione della sessione, la seconda con consigli su come condurre le demo durante la sessione.


Prima della sessione

Impostare font leggibili per l'editor di codice
Non tutti gli spettatori siedono in prima file o hanno la vista delle aquile. È preferibile impostare la dimensione del font ad almeno 14-16 punti. Per aumentare la quantità di codice visibile sullo schermo si puo' utilizzare un font proporzionale, ad esempio Arial Black.

Modificare il colore del testo selezionato
Di default Visual Studio mostra il testo selezionato con caratteri bianchi su sfondo blu scuro, ma il risultato è molto poco leggibile durante le presentazioni. È molto meglio mantenere il colore nero per il testo e usare un colore chiaro (ad esempio ciano) per lo sfondo.

Mantenere i colori di default
A parte le modifiche indicate nei punti precedenti, evitare di modificare i colori di default usati da Visual Studio. La stragrande maggioranza degli sviluppatori si aspetta che i breakpoint siano marcati in rosso, l'istruzione attualmente in esecuzione dovrebbe essere in giallo, e così via. E' anche opportuno usare lo schema di default per i colori di Windows, quello con la barra dei titoli in blu (o al massimo il Silver, quello con la barra dei titoli in grigio).

Usare font più grandi per l'interfaccia utente
Nel creare una applicazione Windows Form o Web Form, utilizzare un font più grande del solito per i vari elementi della interfaccia utente, sia nelle applicazioni demo preparate in anticipo, sia per quelle create al volo. È sufficiente ricordare di modificare il font del form subito dopo averlo creato, in modo che tutti i controlli sul form si adattino alla nuova dimensione.

Lunghezza delle righe di codice
Negli esempi di codice che si intende mostrare durante la sessione è opportuno spezzare le righe ad una lunghezza tale da non rendere necessario l'uso della scrollbar orizzontale per mostrare il testo nascosto dal bordo destro.

Larghezza della tabulazione
Un modo semplice per aumentare la quantità di testo visibile sullo schermo è impostare la larghezza di tabulazione a 3 o persino 2 caratteri, soprattutto se usate loop e strutture indentate di tre o quattro livelli.

Contrassegnare i punti di interesse nel codice
Non c'è nulla di più penoso che vedere uno speaker alla ricerca affannosa dei pezzi di codice che intende mostrare al pubblico. Per rintracciare velocemente i punti di interesse basta marcarli nel codice con un commento speciale riconosciuto dalla finestra Task List. Ecco come fare.
Per prima cosa, usare la pagina Task List della finestra Tools-Options di Visual Studio per definire un comment token personalizzato di nome SHOW e con priorità low. A questo punto è sufficiente marcare il codice con commenti opportuni, usando opzionalmente un numero dopo il commento per indicare l'ordine con cui i vari punti devono essere mostrati, ad es.
     // SHOW: 1. Il costruttore della classe
A questo punto si possono visualizzare questi commenti speciali scegliendo il comando All dal sottomenu Show Tasks del menu contestuale della finestra Task List. Cliccando sull'intestazione della colonna Description si raggruppando tutti i commenti di tipo SHOW e li si ordina in base al numero progressivo. (Se ci sono più di 9 punti, usare numeri a due cifre, ad es. 01, 02, ecc.). Se si clicca sulla intestazione della colonne File, i commenti ritornano ad essere ordinati per posizione.

Per ritrovare velocemente punti nel codice che non sono segnalati da commenti, potete anche usare dei bookmark. Questi bookmark possono apparire nella finestra Task List se usate il comando Add Task List bookmark dal sottomenu Bookmarks del menu Edit, corrispondente alla combinazione Ctrl+K, Ctrl+H.

Stile uniforme del codice
Usare uno stile il più standard possibile per il codice usato nelle demo preparate in anticipo: rispettare le indentazioni, commentare ogni blocco di righe, usare nomi significativi per campi, proprietà, e metodi, e così via.

Aggiungere un file Readme.txt al progetto
Tutti i progetti demo di una certa complessità dovrebbero essere accompagnate da un file di testo che contiene le istruzioni per il setup. Se questo file è aggiunto al progetto in questione sarà visualizzato nella finestra del Solution Explorer e quindi sarà più facilmente notato quando i partecipanti installeranno gli esempi sul proprio computer. Se non vi fidate della vostra memoria, stampate il contenuto di questo file e tenetelo accanto alla tastiera, per essere sicuri di non omettere alcun passo importante.

Controllate le demo alla risoluzione 1024x768
Ecco un errore fin troppo frequente: tutte le demo sono preparate per la risoluzione che usate durante lo sviluppo (spesso 1280x1024 o superiori) e poi l'interfaccia utente funziona male alla risoluzione usata durante la sessione, che non supera mai 1024x768. Quindi accertatevi che tutte le vostre demo funzionino a questa risoluzione. Se vi sembra un grande sacrificio, pensate che fino a non molti anni fa la maggior parte dei proiettori che si trovavano anche nei migliori centri congressi supportava al massimo 800x600.

Scorciatorie nel menu Tools
Aggiungere al menu Tools i comandi per richiamare le utility che usate più frequentemente durante le demo, ad esempio
• lanciare ILDASM per dissassemblare l'eseguibile corrente
• lanciare GACUTIL per registrare l'assembly corrente nella GAC
• aprire il Window Explorer nella cartella del progetto. (In VS2005 questo comando esiste già, basta fare click con il tasto destro sulla tab nella parte superiore dell'editor di codice.)
• lanciare il prompt di Visual Studio .NET nella cartella contenente gli eseguibili, in modo da poter poi lanciare con facilità gli altri tool di .NET

Macro e toolbar custom per altre azioni comuni
E' buona norma preparare delle macro per le azioni comuni che prendono più tempo, ad esempio per creare una proprietà con tanto di blocco get/set oppure per attivare e disattivare i numeri di riga. Queste macro si possono richiamare dal Macro Explorer o meglio ancora mediante una toolbar custom costruita per l'occasione. Una toolbar del genere può essere utile anche per richiamare alcuni comandi di Visual Studio che sono annidati in menù particolarmente profondi o che corrispondono a modifiche delle impostazioni della dialog Tools-Options. Se usate Visual Studio 2005, non dimenticate delle expansions.


Durante le demo

Operazioni di editing mediante mouse e menu di contesto
Usate preferibilmente il mouse per invocare un comando invece della combinazione di tasti che corrisponde al comando. In questo modo gli spettatori possono rendersi conto di quello che sta avvenendo, senza dover tirare a indovinare quali tasti state premendo. Questo suggerimento vale anche per i comuni comandi di editing come Copy, Cut, Paste, Delete, ecc. Se non usate il mouse, almeno dichiarate a voce quello che state facendo in ogni momento.

Evidenziare la posizione del mouse
Attivare l'opzione "Show location of pointer when I press the CTRL key" che si trova nella pagina Pointer Options della dialog box con le proprietà del mouse (raggiungibile da Control Panel). In questo modo è possibile attirare l'attenzione degli spettatori sulla posizione del mouse semplicemente premendo il tasto Control.

Editor a pieno schermo
Quando si analizza un pezzo di codice istruzione per istruzione, è opportuno usare la modalità full-screen dell'editor di Visual Studio .NET, attivabile e disattivabile con la combinazione Shift+Alt+Enter, in modo da massimizzare l'area a disposizione e ridurre la necessità di eseguire lo scrolling orizzontale per mostrare le righe troppo lunghe.

Evitare lo scrolling orizzontale
Come indicato in precedenza, è opportuno che le istruzioni nelle demo non siano tanto lunghe da richiedere l'uso della scrollbar orizzontale per mostrare il testo nascosto. Un sistema alternativo consiste nell'attivare e disattivare velocemente il line wrapping delle righe con la combinazione di tasti Ctrl+R, Ctrl+R. (Oppure Ctrl+E, Ctrl+W in Visual Studio 2005.) Visual Studio viene anche fornito con le macro predefinite TurnOnWordWrap e TurnOffWordWrap che modificano questa impostazione per tutti i linguaggi supportati.

Outlining del codice
Utilizzare le capacità di outlining di Visual Studio per collassare ed espandere le single classi e i singoli metodi, in modo da poter attirare l'attenzione degli spettatori sulle porzioni di codici realmente importanti. E' anche opportuno usare la direttiva #region per creare porzioni di codice che possono essere facilmente collassate, ad esempio l'insieme dei metodi che implementano una interfaccia.

Numeri di riga
Se accettate che gli spettatori possano fare delle domande sul codice che state mostrando, attivate la visualizzazione dei numeri di riga in modo da metterli in grado di indicare velocemente a quale porzione del codice si riferiscono. Visto che questa opzione si attiva dalla dialog Tools-Options, conviene creare una macro e assegnarvi una shortcut. Visual Studio viene fornito con le macro TurnOnLineNumbers e TurnOffLineNumbers che attivano e disattivano questa feature per tutti i linguaggi supportati.

Confrontare due porzioni di codice
Se occorre mostrare due porzioni di codice allo stesso tempo, ad esempio per permettere di apprezzare piccole differenze, si dovrebbe dividere a metà la finestra dell'editor. Ci sono due modi di farlo, a seconda che il codice si trova nello stesso file sorgente oppure in due file differenti.
Nel primo caso, si divide in due la finestra dell'editor trascinando verso il basso il piccolo rettangolino grigio che si trova immediatamente sopra la scrollbar verticale. Nel secondo caso è invece necessario creare un nuovo Tab Group orizzontale (cliccare con il tasto destro su una delle tab mostrate nella parte alta della finestra dell'editor) e accertarsi che i due file in questione appartengono a due gruppi distinti (se non lo sono, basta eseguire il drag-and-drop di una linguetta su un altro gruppo).

Navigazione veloce nel codice
Usate tutti i mezzi e i comandi che Visual Studio mette a disposizione per muoversi velocemente nel codice. Eccone alcuni, non tutti noti come meriterebbero:
• Evidenziare il nome di una variabile, metodo, o classe e premere F12 per saltare alla definizione della classe
• navigare direttamente alla definizione di una classe o metodo mediante l'Object Browser o il Class Viewer
• premere il tasto Ctrl+- (meno) per tornare alla posizione precedente (questa la sanno in pochi!!! :-) )
• creare uno o più bookmark per poi rintracciarli velocemente mediante la finestra Bookmarks.
• usare il comando Find per cercare velocemente tutte le occorrenze delle variabili, invocazioni a metodi, ecc.

Porzioni di codice pre-confezionate
Scrivere codice "al volo" durante la sessione è molto efficace e dà agli spettatori l'impressione che lo speaker padroneggia davvero la materia, ma se si tratta di scrivere più di 4-5 righe di codice molti tendono a distrarsi e a confondersi, anche perché di solito scrivendo codice in questo modo non si aggiungono i commenti necessari. All'altro lato dello spettro vi sono le applicazioni demo preparate in anticipo e complete e funzionanti, che però di solito non mettono in evidenza il ragionamento che ha seguito lo sviluppatore per arrivare al risultato finale. Ecco alcune soluzioni "intermedie":
• Scrivete in precedenza il codice nel programma ma commentatelo, in modo da poterlo poi "scommentare" velocemente con Ctrl+K, Ctrl+U.
• Scrivete in precedenza il codice nel programma, ma usate una direttiva #if false per non farlo eseguire; se invece della costante false usate una variabile di compilazione, questo meccanismo permette di attivare o nascondere più pezzi di codice con una modifica in un unico punto del codice (oppure nelle proprietà di progetto).
• Per le porzioni di codice di una certa dimensione, preparate un file di testo contenenti le varie porzioni di codice, caricatelo in Notepad, e durante la sessione usate il copia-e-incolla per inserire tali pezzi di codice in determinati punti del listato.
• Come sopra, ma prima della sessione copiate i pezzi di codice sulla Toolbox di VS come frammenti di testo, da dove potete riprenderli mediante un semplice drag-and-drop
• Usate un qualsiasi code librarian per conservare i frammenti di codice che vi interessano, ad esempio la versione freeware di CodeBox (www.dotnet2themax.com/codebox).
• Se la sessione verte su Visual Studio 2005, potete preparare delle porzioni di codice e inserirle con il comando Insert Snippet.

L'utility Magnifier
Se volete attirare l'attenzione su alcuni particoli della interfaccia utente del vostro programma (o di Visual Studio, o di un altro programma qualsiasi), usate l'utility Magnifier (che trovate nel menu Accessibility del menu Accessories di Windows). Su Internet trovate una marea di utility simili e anche più potenti.

ATTENZIONE AL TEMPO CHE PASSA !!!
L'ultimo consiglio è in realtà il più importante: ricordatevi di indossare l'orologio e soprattutto di guardarlo in continuazione. Non c'è nulla di peggio di vedere uno speaker che si affanna per finire le sue slide in tempo, perchè ha indugiato troppo all'inizio o ha accettato troppe domande dal pubblico. Se il vostro orologio ha un allarme, impostatelo a 15 minuti prima della fine della sessione. Se avete un palmare, usate l'allarme del palmare. Se non avete un palmare, portatevi una sveglia sul palco, oppure usate una qualsiasi utility sul computer che emetta un beep.

Se non trovate una di queste utility su Internet o non sapete scriverne una, mi chiedo che ci fate su un palco di una conferenza per programmatori :-) Ma anche in quel caso, almeno chiedete a un amico in prima file di tossire violentemente quando si accorge che state per "sforare" ....


Sono sicuro che molti di voi hanno qualcosa da aggiungere a questo elenco, sia perchè hanno avuto modo di parlare in pubblico sia perchè hanno visto altri speaker NON fare qualcosa che avrebbero dovuto fare (o fare qualcosa che NON avrebbero dovuto fare, il che è lo stesso). Se ne avete voglia, lasciate pure un commento e aggiornerò l'elenco.

10/1/2005 4:48:08 PM (GMT Daylight Time, UTC+01:00) #  | Comments [0] | 

O almeno questo è il titolo di questa pagina su MSDN :-) In realtà per ora io ne ho contate 47 di applicazioni di esempio, ma forse ne ne sono perse 3 perchè in fondo alla pagina c'è scritto "Coming soon - The remaining 51 samples are coming soon. Stay tuned." :-)

Ma non andiamo a pignolare, anche quello che c'è già è decisamente benvenuto! Le applicazioni sono fornite in C# o VB.NET e sono divise nelle seguenti categorie:

  • Base Class Libraries: come cambiare le ACL di un file, implementare animazioni su console, scaricare file da FTP, comprimere e decomprimere file, usare le generic collections, ricavare informazioni sui drive, usare la classe Stopwatch, eseguire il PING e qualche altra operazione di networking.
  • Data Access: query asincrone, uso e confronto di Datareader con DataSet, update batch, paginazione, bulk update, salvataggio e recupero di immagini dal DB, classi factory, uso di MARS, Notification Services, e UDT di SQL Server 2005.
  • Web Development: master pages, API e controlli di membership, profili, controlli menu e TreeView, web part, data binding con i vari componenti xxxDataSource.
  • Windows Forms: task asincroni, componenti BindingNavigator e BindingSource, estensioni alla DataGridView standard, i nuovi controlli MaskedTextBox, WebBrowser, StatusStrip, ToolStrip, SplitContainer e LayoutPanel.

Insomma, se ancora non avete dato un'occhiata a .NET 2.0 e VS.NET 2005, questi esempi pronti per essere eseguiti sono un'ottima occasione per vedere velocemente alcune delle novità introdotte. Complimenti per l'iniziativa, e come dice la pagina stessa: tenete d'occhio la pagina per gli ulteriori 51 (o 54?) esempi che hanno già promesso.

8/8/2005 1:12:48 PM (GMT Daylight Time, UTC+01:00) #  | Comments [0] | 
 
Feed di Blog2theMax
RSS 2.0 | Atom 0.2
Cerca nel blog
Archivio
<March 2010>
SunMonTueWedThuFriSat
28123456
78910111213
14151617181920
21222324252627
28293031123
45678910
Categorie

Powered by: newtelligence dasBlog 1.7.5016.2

 ©2004-2005 Code Architects S.r.l. - All rights reserved  Sign In