Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
I processori di direttiva funzionano aggiungendo codice alla classe di trasformazione generata. Se si chiama una direttiva da un modello di testo, il resto del codice scritto nel modello di testo può basarsi sulla funzionalità fornita dalla direttiva.
È possibile scrivere processori di direttive personalizzati propri. In questo modo è possibile personalizzare i propri modelli di testo. Per creare un processore di direttiva personalizzato, si crea una classe che eredita da DirectiveProcessor o RequiresProvidesDirectiveProcessor.
Di seguito vengono illustrate le attività incluse nella procedura dettagliata:
Creare un processore di direttiva personalizzato
Registrare il processore di direttiva
Testare il processore di direttiva
Creare un processore di direttiva personalizzato
In questa procedura dettagliata viene creato un processore di direttiva personalizzato. Si aggiunge una direttiva personalizzata che legge un file XML, lo archivia in una variabile XmlDocument e lo espone tramite una proprietà. Nella sezione "Test del processore di direttiva", si utilizza questa proprietà in un modello di testo per accedere al file XML.
La chiamata alla direttiva personalizzata presenta la sintassi seguente:
<#@ CoolDirective Processor="CustomDirectiveProcessor" FileName="<Your Path>DocFile.xml" #>
Il processore di direttiva personalizzato aggiunge la variabile e la proprietà alla classe della trasformazione generata. La direttiva che si scrive utilizza le classi System.CodeDom per creare il codice che il motore aggiunge alla classe della trasformazione generata. Le System.CodeDom classi creano codice in Visual C# o Visual Basic, a seconda del linguaggio specificato nel language parametro della template direttiva . Il linguaggio del processore di direttiva e il linguaggio del modello di testo che accede al processore di direttiva non devono essere uguali.
Il codice creato dalla direttiva è il seguente:
private System.Xml.XmlDocument document0Value;
public virtual System.Xml.XmlDocument Document0
{
get
{
if ((this.document0Value == null))
{
this.document0Value = XmlReaderHelper.ReadXml(<FileNameParameterValue>);
}
return this.document0Value;
}
}
Per creare un processore di direttiva personalizzato
In Visual Studio, creare un progetto Libreria di classi in C# o in Visual Basic denominato CustomDP.
Nota
Se si vuole installare il processore di direttiva in più computer, è preferibile usare un progetto di estensione VSIX (Visual Studio Extension) e includere un file con estensione pkgdef nell'estensione. Per altre informazioni, vedere Distribuzione di un processore di direttive personalizzato.
Aggiungere riferimenti a questi assembly:
Microsoft.VisualStudio.TextTemplating.*.0
Microsoft.VisualStudio.TextTemplating.Interfaces.*.0
Sostituire il codice in Class1 con il codice seguente. Questo codice definisce una classe CustomDirectiveProcessor che eredita dalla classe DirectiveProcessor e implementa i metodi necessari.
using System; using System.CodeDom; using System.CodeDom.Compiler; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Text; using System.Xml; using System.Xml.Serialization; using Microsoft.VisualStudio.TextTemplating; namespace CustomDP { public class CustomDirectiveProcessor : DirectiveProcessor { // This buffer stores the code that is added to the // generated transformation class after all the processing is done. // --------------------------------------------------------------------- private StringBuilder codeBuffer; // Using a Code Dom Provider creates code for the // generated transformation class in either Visual Basic or C#. // If you want your directive processor to support only one language, you // can hard code the code you add to the generated transformation class. // In that case, you do not need this field. // -------------------------------------------------------------------------- private CodeDomProvider codeDomProvider; // This stores the full contents of the text template that is being processed. // -------------------------------------------------------------------------- private String templateContents; // These are the errors that occur during processing. The engine passes // the errors to the host, and the host can decide how to display them, // for example the host can display the errors in the UI // or write them to a file. // --------------------------------------------------------------------- private CompilerErrorCollection errorsValue; public new CompilerErrorCollection Errors { get { return errorsValue; } } // Each time this directive processor is called, it creates a new property. // We count how many times we are called, and append "n" to each new // property name. The property names are therefore unique. // ----------------------------------------------------------------------------- private int directiveCount = 0; public override void Initialize(ITextTemplatingEngineHost host) { // We do not need to do any initialization work. } public override void StartProcessingRun(CodeDomProvider languageProvider, String templateContents, CompilerErrorCollection errors) { // The engine has passed us the language of the text template // we will use that language to generate code later. // ---------------------------------------------------------- this.codeDomProvider = languageProvider; this.templateContents = templateContents; this.errorsValue = errors; this.codeBuffer = new StringBuilder(); } // Before calling the ProcessDirective method for a directive, the // engine calls this function to see whether the directive is supported. // Notice that one directive processor might support many directives. // --------------------------------------------------------------------- public override bool IsDirectiveSupported(string directiveName) { if (string.Compare(directiveName, "CoolDirective", StringComparison.OrdinalIgnoreCase) == 0) { return true; } if (string.Compare(directiveName, "SuperCoolDirective", StringComparison.OrdinalIgnoreCase) == 0) { return true; } return false; } public override void ProcessDirective(string directiveName, IDictionary<string, string> arguments) { if (string.Compare(directiveName, "CoolDirective", StringComparison.OrdinalIgnoreCase) == 0) { string fileName; if (!arguments.TryGetValue("FileName", out fileName)) { throw new DirectiveProcessorException("Required argument 'FileName' not specified."); } if (string.IsNullOrEmpty(fileName)) { throw new DirectiveProcessorException("Argument 'FileName' is null or empty."); } // Now we add code to the generated transformation class. // This directive supports either Visual Basic or C#, so we must use the // System.CodeDom to create the code. // If a directive supports only one language, you can hard code the code. // -------------------------------------------------------------------------- CodeMemberField documentField = new CodeMemberField(); documentField.Name = "document" + directiveCount + "Value"; documentField.Type = new CodeTypeReference(typeof(XmlDocument)); documentField.Attributes = MemberAttributes.Private; CodeMemberProperty documentProperty = new CodeMemberProperty(); documentProperty.Name = "Document" + directiveCount; documentProperty.Type = new CodeTypeReference(typeof(XmlDocument)); documentProperty.Attributes = MemberAttributes.Public; documentProperty.HasSet = false; documentProperty.HasGet = true; CodeExpression fieldName = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), documentField.Name); CodeExpression booleanTest = new CodeBinaryOperatorExpression(fieldName, CodeBinaryOperatorType.IdentityEquality, new CodePrimitiveExpression(null)); CodeExpression rightSide = new CodeMethodInvokeExpression(new CodeTypeReferenceExpression("XmlReaderHelper"), "ReadXml", new CodePrimitiveExpression(fileName)); CodeStatement[] thenSteps = new CodeStatement[] { new CodeAssignStatement(fieldName, rightSide) }; CodeConditionStatement ifThen = new CodeConditionStatement(booleanTest, thenSteps); documentProperty.GetStatements.Add(ifThen); CodeStatement s = new CodeMethodReturnStatement(fieldName); documentProperty.GetStatements.Add(s); CodeGeneratorOptions options = new CodeGeneratorOptions(); options.BlankLinesBetweenMembers = true; options.IndentString = " "; options.VerbatimOrder = true; options.BracingStyle = "C"; using (StringWriter writer = new StringWriter(codeBuffer, CultureInfo.InvariantCulture)) { codeDomProvider.GenerateCodeFromMember(documentField, writer, options); codeDomProvider.GenerateCodeFromMember(documentProperty, writer, options); } } // One directive processor can contain many directives. // If you want to support more directives, the code goes here... // ----------------------------------------------------------------- if (string.Compare(directiveName, "supercooldirective", StringComparison.OrdinalIgnoreCase) == 0) { // Code for SuperCoolDirective goes here... } // Track how many times the processor has been called. // ----------------------------------------------------------------- directiveCount++; } public override void FinishProcessingRun() { this.codeDomProvider = null; // Important: do not do this: // The get methods below are called after this method // and the get methods can access this field. // ----------------------------------------------------------------- // this.codeBuffer = null; } public override string GetPreInitializationCodeForProcessingRun() { // Use this method to add code to the start of the // Initialize() method of the generated transformation class. // We do not need any pre-initialization, so we will just return "". // ----------------------------------------------------------------- // GetPreInitializationCodeForProcessingRun runs before the // Initialize() method of the base class. // ----------------------------------------------------------------- return String.Empty; } public override string GetPostInitializationCodeForProcessingRun() { // Use this method to add code to the end of the // Initialize() method of the generated transformation class. // We do not need any post-initialization, so we will just return "". // ------------------------------------------------------------------ // GetPostInitializationCodeForProcessingRun runs after the // Initialize() method of the base class. // ----------------------------------------------------------------- return String.Empty; } public override string GetClassCodeForProcessingRun() { //Return the code to add to the generated transformation class. // ----------------------------------------------------------------- return codeBuffer.ToString(); } public override string[] GetReferencesForProcessingRun() { // This returns the references that we want to use when // compiling the generated transformation class. // ----------------------------------------------------------------- // We need a reference to this assembly to be able to call // XmlReaderHelper.ReadXml from the generated transformation class. // ----------------------------------------------------------------- return new string[] { "System.Xml", this.GetType().Assembly.Location }; } public override string[] GetImportsForProcessingRun() { //This returns the imports or using statements that we want to //add to the generated transformation class. // ----------------------------------------------------------------- //We need CustomDP to be able to call XmlReaderHelper.ReadXml //from the generated transformation class. // ----------------------------------------------------------------- return new string[] { "System.Xml", "CustomDP" }; } } // ------------------------------------------------------------------------- // The code that we are adding to the generated transformation class // will call this method. // ------------------------------------------------------------------------- public static class XmlReaderHelper { public static XmlDocument ReadXml(string fileName) { XmlDocument d = new XmlDocument(); using (XmlReader reader = XmlReader.Create(fileName)) { try { d.Load(reader); } catch (System.Xml.XmlException e) { throw new DirectiveProcessorException("Unable to read the XML file.", e); } } return d; } } }Solo per Visual Basic aprire il menu Progetto e fare clic su Proprietà CustomDP. Nella scheda Applicazione , nello spazio dei nomi Radice, eliminare il valore predefinito ,
CustomDP.Scegliere Save All (Salva tutti) dal menu File.
Nel menu Compila scegliere Compila soluzione.
Compilare il progetto
Compilare il progetto. Nel menu Compila scegliere Compila soluzione.
Registrare il processore di direttiva
Prima di poter chiamare una direttiva da un modello di testo in Visual Studio, è necessario aggiungere una chiave del Registro di sistema per il processore di direttiva.
Nota
Se si vuole installare il processore di direttiva in più computer, è preferibile definire un'estensione di Visual Studio (VSIX) che includa un file con estensione pkgdef insieme all'assembly. Per altre informazioni, vedere Distribuzione di un processore di direttive personalizzato.
Nel Registro di sistema esistono delle chiavi per i processori di direttiva nella posizione seguente:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\*.0\TextTemplating\DirectiveProcessors
Per i sistemi a 64 bit, il percorso del Registro di sistema è:
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\*.0\TextTemplating\DirectiveProcessors
In questa sezione, si aggiunge una chiave per il processore di direttiva personalizzato al Registro di sistema nella stessa posizione.
Attenzione
È possibile che eventuali modifiche non corrette del Registro di sistema danneggino gravemente il sistema. Prima di apportare modifiche al Registro di sistema, eseguire il backup dei dati importanti presenti nel computer.
Per aggiungere una chiave del Registro di sistema per il processore di direttiva
Eseguire il
regeditcomando usando il menu Start o la riga di comando.Passare al percorso HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\*.0\TextTemplating\DirectiveProcessors e fare clic sul nodo.
Nei sistemi a 64 bit usare HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\*.0\TextTemplating\DirectiveProcessors
Aggiungere una nuova chiave denominata CustomDirectiveProcessor.
Nota
Si tratta del nome che si utilizzerà nel campo Processore delle direttive personalizzate. Questo nome non deve necessariamente essere uguale al nome della direttiva, al nome della classe del processore di direttiva o allo spazio dei nomi del processore di direttiva.
Aggiungere un nuovo valore stringa denominato Class che dispone di un valore CustomDP.CustomDirectiveProcessor per il nome della nuova stringa.
Aggiungere un nuovo valore stringa denominato Codebase che dispone di un valore uguale al percorso di CustomDP.dll creato precedentemente in questa procedura dettagliata.
Ad esempio, il percorso potrebbe essere simile
C:\UserFiles\CustomDP\bin\Debug\CustomDP.dlla .La chiave del Registro di sistema deve contenere i valori seguenti:
Nome Type Dati (impostazione predefinita). REG_SZ (valore non impostato) Classe REG_SZ CustomDP.CustomDirectiveProcessor CodeBase REG_SZ <Percorso della soluzione>CustomDP\bin\Debug\CustomDP.dll Se si è inserito l'assembly nella GAC, i valori appariranno come indicato di seguito:
Nome Type Dati (impostazione predefinita). REG_SZ (valore non impostato) Classe REG_SZ CustomDP.CustomDirectiveProcessor Assemblaggio REG_SZ CustomDP.dll Riavviare Visual Studio.
Testare il processore di direttiva
Per testare il processore di direttiva, è necessario scrivere un modello di testo che lo chiami.
In questo esempio, il modello di testo chiama la direttiva e passa nel nome di un file XML che contiene documentazione per un file della classe. Il modello di testo usa la XmlDocument proprietà creata dalla direttiva per esplorare il codice XML e stampare i commenti della documentazione.
Per creare un file XML da utilizzare nel test del processore di direttiva
Creare un file denominato DocFile.xml usando qualsiasi editor di testo, ad esempio Blocco note.
Nota
È possibile creare questo file in qualsiasi percorso, ad esempio C:\Test\DocFile.xml.
Aggiungere quanto segue al file XML:
<?xml version="1.0"?> <doc> <assembly> <name>xmlsample</name> </assembly> <members> <member name="T:SomeClass"> <summary>Class level summary documentation goes here.</summary> <remarks>Longer comments can be associated with a type or member through the remarks tag</remarks> </member> <member name="F:SomeClass.m_Name"> <summary>Store for the name property</summary> </member> <member name="M:SomeClass.#ctor"> <summary>The class constructor.</summary> </member> <member name="M:SomeClass.SomeMethod(System.String)"> <summary>Description for SomeMethod.</summary> <param name="s">Parameter description for s goes here</param> <seealso cref="T:System.String">You can use the cref attribute on any tag to reference a type or member and the compiler will check that the reference exists.</seealso> </member> <member name="M:SomeClass.SomeOtherMethod"> <summary>Some other method.</summary> <returns>Return results are described through the returns tag.</returns> <seealso cref="M:SomeClass.SomeMethod(System.String)">Notice the use of the cref attribute to reference a specific method</seealso> </member> <member name="M:SomeClass.Main(System.String[])"> <summary>The entry point for the application.</summary> <param name="args">A list of command line arguments</param> </member> <member name="P:SomeClass.Name"> <summary>Name property</summary> <value>A value tag is used to describe the property value</value> </member> </members> </doc>Salva e chiude il file.
Per creare un modello di testo per testare il processore di direttiva
In Visual Studio, creare un progetto Libreria di classi in C# o in Visual Basic denominato TemplateTest.
Aggiungere un nuovo file modello di testo denominato TestDP.tt.
Assicurarsi che la proprietà Strumento personalizzato di TestDP.tt sia impostata su
TextTemplatingFileGenerator.Modificare il contenuto di TestDP.tt nel testo seguente.
Nota
Sostituire la stringa
<YOUR PATH>con il percorso del file DocFile.xml .Il linguaggio del modello di testo non deve necessariamente essere uguale al linguaggio del processore di direttiva.
<#@ assembly name="System.Xml" #> <#@ template debug="true" #> <#@ output extension=".txt" #> <# // This will call the custom directive processor. #> <#@ CoolDirective Processor="CustomDirectiveProcessor" FileName="<YOUR PATH>\DocFile.xml" #> <# // Uncomment this line if you want to see the generated transformation class. #> <# // System.Diagnostics.Debugger.Break(); #> <# // This will use the results of the directive processor. #> <# // The directive processor has read the XML and stored it in Document0. #> <# XmlNode node = Document0.DocumentElement.SelectSingleNode("members"); foreach (XmlNode member in node.ChildNodes) { XmlNode name = member.Attributes.GetNamedItem("name"); WriteLine("{0,7}: {1}", "Name", name.Value); foreach (XmlNode comment in member.ChildNodes) { WriteLine("{0,7}: {1}", comment.Name, comment.InnerText); } WriteLine(""); } #> <# // You can call the directive processor again and pass it a different file. #> <# // @ CoolDirective Processor="CustomDirectiveProcessor" FileName="<YOUR PATH>\<Your Second File>" #> <# // To use the results of the second directive call, use Document1. #> <# // XmlNode node2 = Document1.DocumentElement.SelectSingleNode("members"); // ... #>Nota
In questo esempio, il valore del parametro
ProcessorèCustomDirectiveProcessor. Il valore del parametroProcessordeve corrispondere al nome della chiave del Registro di sistema per il processore.Scegliere Salva tutto dal menu File.
Per testare il processore di direttiva
In Esplora soluzioni fare clic con il pulsante destro del mouse su TestDP.tt e quindi scegliere Esegui strumento personalizzato.
Per gli utenti di Visual Basic, TestDP.txt potrebbe non essere visualizzato in Esplora soluzioni per impostazione predefinita. Per visualizzare tutti i file assegnati al progetto, aprire il menu Progetto e fare clic su Mostra tutti i file.
In Esplora soluzioni espandere il nodo TestDP.txt e quindi fare doppio clic su TestDP.txt per aprirlo nell'editor.
Verrà visualizzato l'output di testo generato. L'output sarà simile al seguente:
Name: T:SomeClass summary: Class level summary documentation goes here. remarks: Longer comments can be associated with a type or member through the remarks tag Name: F:SomeClass.m_Name summary: Store for the name property Name: M:SomeClass.#ctor summary: The class constructor. Name: M:SomeClass.SomeMethod(System.String) summary: Description for SomeMethod. param: Parameter description for s goes here seealso: You can use the cref attribute on any tag to reference a type or member and the compiler will check that the reference exists. Name: M:SomeClass.SomeOtherMethod summary: Some other method. returns: Return results are described through the returns tag. seealso: Notice the use of the cref attribute to reference a specific method Name: M:SomeClass.Main(System.String[]) summary: The entry point for the application. param: A list of command line arguments Name: P:SomeClass.Name summary: Name property value: A value tag is used to describe the property value
Aggiungere CODICE HTML al testo generato
Dopo avere testato il processore di direttiva personalizzato, è possibile che si desideri aggiungere del codice HTML al testo generato.
Per aggiungere codice HTML al testo generato
Sostituire il codice in TestDP.tt con il codice seguente. Il codice HTML è evidenziato. Assicurarsi di sostituire la stringa
YOUR PATHcon il percorso del file DocFile.xml .Nota
Altri tag open <# e close #> separano il codice dell'istruzione dai tag HTML.
<#@ assembly name="System.Xml" #> <#@ template debug="true" #> <#@ output extension=".htm" #> <# // This will call the custom directive processor #> <#@ CoolDirective Processor="CustomDirectiveProcessor" FileName="<YOUR PATH>\DocFile.xml" #> <# // Uncomment this line if you want to see the generated transformation class #> <# // System.Diagnostics.Debugger.Break(); #> <html><body> <# // This will use the results of the directive processor #>. <# // The directive processor has read the XML and stored it in Document0#>. <# XmlNode node = Document0.DocumentElement.SelectSingleNode("members"); foreach (XmlNode member in node.ChildNodes) { #> <h3> <# XmlNode name = member.Attributes.GetNamedItem("name"); WriteLine("{0,7}: {1}", "Name", name.Value); #> </h3> <# foreach (XmlNode comment in member.ChildNodes) { WriteLine("{0,7}: {1}", comment.Name, comment.InnerText); #> <br/> <# } } #> </body></html>Scegliere Salva TestDP.txt dal menu File.
Per visualizzare l'output in un browser, in Esplora soluzioni fare clic con il pulsante destro del mouse su TestDP.htm e scegliere Visualizza nel browser.
L'output deve corrispondere al testo originale, ad eccezione del formato HTML applicato. Ogni nome di elemento viene visualizzato in grassetto.