Technology Experience

Contenuti gestiti da Igor Damiani
posts - 949, comments - 2741, trackbacks - 15120

My Links

News

  • Questo blog si propone di raccogliere riflessioni, teoriche e pratiche, su tutto quello che riguarda il world-computing che mi sta attorno: programmazione in .NET, software attuale e futuro, notizie provenienti dal web, tecnologia in generale, open-source.

    L'idea è quella di lasciare una sorta di patrimonio personale, una raccolta di idee che un giorno potrebbe farmi sorridere, al pensiero di dov'ero e cosa stavo facendo.

    10/05/2005,
    Milano

Archives

Post Categories

Generale

[70-536, #17] Un custom visualizer per una classe (XmlWriterSettings e dintorni)

Non lo nego: questa volta ci sono andato un po' pesante. Sto scrivendo questa prima linea del mio post, ma so già fin dal principio che sarà bello lungo. Sì, Lorenzo, non sgridarmi, appena clicco Salva e Pubblica del mio IMHO, vado subito in .TEXT e vedo di sistemare le cose, ok? Promesso!

A parte gli scherzi, credo che questa sia la prima volta in cui credo che non riuscirò ad essere chiaro fino in fondo, soprattutto perchè bisognerebbe avere davanti il codice di cui si parla e, per dirla tutta, anche metterci un po' le mani, capire perchè ho fatto così, come metter giù un modo migliore (c'è sempre un modo migliore) e così via.

Obiettivo - Creare un custom visualizer per la mia XMLSerialization
Internet è piena di tutorial che spiegano come creare un custom visualizer, ovvero un viewer specifico per una classe che ci possa aiutare e semplificare il lavoro durante il debug della classe stessa. Ripeto il concetto, con parole diverse: se stiamo debuggando del codice con alcuni oggetti string, questi oggetti li vediamo nelle finestre Locals, Watch, etc. Inoltre, VS2005 ci mette a disposizione un tooltip che ci mostra il valore della variabile string. Una string è semplice, non ci sono grossi problemi. Ora, immaginatevi la stessa situazione con un oggetto molto più complesso (uno qualsiasi del FX, o una classe scritta da noi): in queste condizioni, VS2005 non può fare altro che farci vedere una UI standard: una sorta di treeview che possiamo espandere ad n livelli per leggerne i membri a cascata. Tutto molto bello, non c'è dubbio, ma VS2005 - non contento di ciò - ci dà una marcia in più: il custom visualizer.

I tutorial sul Web creano tutti un custom visualizer per le stringhe: tale visualizer mostra la stringa in una MessageBox, o al massimo in una Windows Form dedicata. Io ho voluto spingermi un po' più in là, e lavorare invece sulla mia classe XMLSerialization, che contiene un oggetto di tipo XmlWriterSettings. Quest'ultima classe espone alcune proprietà che regolano il modo con cui il FX serializza una classe: indentazione dei tag, encoding, checking dei caratteri, quando andare a capo, e così via.
Tale classe è sealed e per default non è serializzabile (lo sottolineo perchè è importante).

Ma arriviamo al dunque, una sorta di step-by-step, per creare un custom visualizer:

  1. Occorre innanzitutto creare un nuovo progetto di tipo Class Library alla nostra soluzione
  2. A questo nuovo progetto, occorre aggiungere una reference a Microsoft.VisualStudio.DebuggerVisualizers e - più in generale - a qualsiasi altro assembly di cui abbiamo bisogno
  3. Scrivere una classe (che io seguendo i tutorial ho preso l'abitudine di chiamare DebuggerSide) che eredita da DialogDebuggerVisualizer
  4. In questa classe, fare l'overloading del metodo Show:
protected override void Show(IDialogVisualizerService windowService, IVisualizerObjectProvider objectProvider)
{ }

In ultimo, firmare l'assembly con uno strong-name. Teoricamente, dovremmo decorare la classe con l'attributo DebuggerVisualizer, ma in realtà non è veramente necessario, e vedremo il perchè. Seguendo le indicazioni qui sopra, ho creato un progetto XMLSerializationVisualizer , il cui codice è il seguente (mi rendo conto di essere un po' frettoloso, per cui vi rimando a questo ottimo walkthrough su MSDN):

using System;
using System.Windows.Forms;
using Microsoft.VisualStudio.DebuggerVisualizers;

namespace XMLSerializationVisualizer
{
    
public class DebuggerSide : DialogDebuggerVisualizer
    {
        
protected override void Show(IDialogVisualizerService windowService, IVisualizerObjectProvider objectProvider)
        {
            XMLSerialization objSer = (XMLSerialization) objectProvider.GetObject();
            
string msg = string.Format("XMLSerialization Visualizer...{0}", objSer.ToString());
            MessageBox.Show(msg);
        }

        
public static void TestShowVisualizer(object objectToVisualize)
        {
            VisualizerDevelopmentHost vHost = 
new VisualizerDevelopmentHost(objectToVisualize, typeof(DebuggerSide));
            vHost.ShowVisualizer();
        }
    }
}

Nel metodo Show() possiamo scrivere tutto il codice che vogliamo. Notare la chiamata a objectProvider.GetObject(), che non fa altro che reperire l'oggetto dall'engine del debugger di VS2005, castandolo al tipo corretto che vogliamo gestire. Il metodo statico TestShowVisualizer() è molto comodo per debuggare il custom visualizer dal processo chiamante.

Il processo chiamante, ovvero il codice che stiamo debuggando, attiverà l'istanza del nostro custom visualizer in modo molto semplice, sfruttando proprio il metodo statico di cui abbiamo appena parlato.
Ecco il codice del mio Program.cs:

XMLSerialization ser = new XMLSerialization();
XMLSerializationVisualizer.DebuggerSide.TestShowVisualizer(ser);

Il codice qui sopra provoca l'esecuzione del custom visualizer, ovvero il suo metodo Show() che non fa nient'altro che recuperare l'istanza di XMLSerialization che abbiamo passato ed elaborarla come gli abbiamo detto (adesso non è molto utile, mostro in una MessageBox la ToString() senza fare nulla di particolare).

Il vero problema è che il debugger di VS2005 usa la serializzazione per passare i dati dal suo IDE ad un custom visualizer , per cui dobbiamo obbligatoriamente fare in modo che la classe che vogliamo gestire nel custom visualizer sia, per l'appunto, serializzabile. Questo ha richiesto un po' di refactoring del codice.

Il vero problema è che...XMLSerialization non è serializzabile!!!
Più che creare il custom visualizer in sè, ho dovuto aggirare un altro ostacolo: la mia classe XMLSerialization non era serializzabile, e tantomeno la XmlWriterSettings del FX. Per risolvere, ho deciso di implementare l'interfaccia ISerializable di cui abbiamo già parlato. Tale interfaccia ci obbliga a scrivere il codice del metodo GetObjectData(), che riporto qui sotto:

void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
    info.AddValue("Indent", regole.Indent);
    info.AddValue("IndentChars", regole.IndentChars);
    info.AddValue("ConformanceLevel", regole.ConformanceLevel);
    info.AddValue("NewLineOnAttributes", regole.NewLineOnAttributes);
    info.AddValue("Encoding", regole.Encoding);
}

Il codice qui sopra viene consumato dal FX ogni volta che chiediamo la serializzazione binaria della classe (quindi usando BinaryFormatter piuttosto che XmlSerializer). Il FX intercetta la richiesta di serializzazione ed esegue GetObjectData() che è a nostra completa disposizione: nel nostro caso, aggiungo a SerializationInfo tutti i valori che voglio serializzare (l'oggetto regole che vedete nel codice è un'istanza di XmlWriterSettings).
Ma non finisce qui, perchè ad ogni serializzazione corrisponde sempre una deserializzazione.
Di conseguenza, come vuole il FX, ho dovuto creare un nuovo costruttore chiamato automaticamente per creare un'istanza dell'oggetto in fase di serializzazione. Il costruttore è il seguente:

// Costruttore chiamato durante il deserialize
public XMLSerialization(SerializationInfo info, StreamingContext context)
{
    regole = 
new XmlWriterSettings();
    regole.Indent = info.GetBoolean("Indent");
    regole.IndentChars = info.GetString("IndentChars");
    regole.ConformanceLevel = (ConformanceLevel)info.GetInt32("ConformanceLevel");
    regole.NewLineOnAttributes = info.GetBoolean("NewLineOnAttributes");
    regole.Encoding = (System.Text.Encoding)info.GetValue("Encoding", 
typeof(System.Text.Encoding));
}

Concludendo...
Fatto questo, fatto tutto. La cosa davvero interessante è che, decorando la classe XMLSerializationVisualizer con l'attributo DebuggerVisualizer nel modo seguente....

[assembly: System.Diagnostics.DebuggerVisualizer(
    
typeof(XMLSerializationVisualizer.DebuggerSide),
    
typeof(VisualizerObjectSource),
    Target = 
typeof(XMLSerialization),
    Description = "Visualizer per XMLSerialization")]

....otteniamo un bel assembly DLL che possiamo copiare in MyDocuments\Visual Studio 2005\Visualizers per abilitare il custom visualizer senza avere il progetto nella nostra soluzione. Questo provoca la comparsa di una lente d'ingrandimento nelle varie finestre dell'IDE di VS2005 che apre il custom visualizer: di fatto, non abbiamo più bisogno di inserire nel nostro codice la chiamata al metodo statico TestShowVisualizer() che abbiamo visto prima.

Download del codice
Ho intenzione di rilasciare il codice C# che ho scritto finora. Contempla 3 assembly totali: BusinessObjects, MCPD e XMLSerializationVisualizer. Il codice riassume un po' quello fatto fino ad oggi: classi, serializzazione, interfacce, ed anche il custom visualizer descritto in questo post. Aspettatevi quindi un post dedicato, magari già oggi pomeriggio, con tutto il materiale.

powered by IMHO 1.2

Print | posted on martedì 21 febbraio 2006 15:24 | Filed Under [ Esame 70-536 ]

Feedback

Gravatar

# re: [70-536, #17] Un custom visualizer per una classe (XmlWriterSettings e dintorni)

Ciao Igor, bel post.
Ti segnalo un buon XML visualizer che ho menzionato nel mio post:
http://blogs.ugidotnet.org/puntorete/archive/2005/12/01/30851.aspx
21/02/2006 17:23 | Michele Bersani
Comments have been closed on this topic.

Powered by:
Powered By Subtext Powered By ASP.NET