Custom Controls con supporto per l'Intellisense

di Stefano Mostarda, in ASP.NET,

Nel primo e secondo articolo, abbiamo visto come sviluppare feature di design-time visuali quali Property Editor, Component Editor e visualizzazione a Design-Time. In quest'ultimo articolo vedremo come aggiungere funzionalità dedicate alla semplificazione della scrittura di codice, sia markup che C#. La prima è l'autocompletion nella versione HTML di una pagina (come avviene per i controlli web di base del .NET Framework). La seconda è la finestra di help quando si richiama l'autocompletion nel code-behind (anche questa già sviluppata per i controlli di base).

Autocompletion HTML

L'editor visuale di pagine web di VS.NET è poco "simpatico" perché, ad ogni switch tra visualizzazione in "Progettazione" e "HTML", formatta il codice HTML a suo piacimento modificando ogni nostra impostazione. Per questo motivo, molti scelgono di lavorare esclusivamente in modalità HTML. In questi casi, torna utile l'autocompletion, ovvero la completazione del codice , soprattutto quando ci sono molte proprietà che possono essere settate.

Per fare questo, il motore di VS utilizza i file di definizioni xml (xsd) contenuti all'interno della cartella '$VSInstallDir$\Common7\Packages\schemas\xml'. In particolare, per i controlli web, viene utilizzato il file asp.xsd.

Di conseguenza, abbiamo 2 possibilità per aggiungere tale supporto per i nostri controlli:

  • Modificare il file asp.xsd e aggiungere le nostre definizioni. Questa soluzione, anche se funzionante, presenta degli inconvenienti. Innanzitutto è scomoda in fase di deployment (scrivere un modulo che modifica il file asp.xsd complica il setup), e soprattutto qualunque hotfix o service pack che agisce sul file può sovrascrivere tutte le nostre modifiche eliminando così l' IntelliSense .
  • Aggiungere un nostro file di definizioni nella directory. Questa soluzione è ottimale in quanto consente di superare i problemi della soluzione precedente, e aggiunge la possibilità di personalizzare le intestazioni dello schema.

Vediamo ora come scrivere il file contenente lo schema:

 <xsd:schema 
   xmlns:vs="http://schemas.microsoft.com/Visual-Studio-Intellisense"
   xmlns="urn:http://schemas.miosito.com/WebControls"
   elementFormDefault="qualified"
   targetNamespace="urn:http://schemas.miosito.com/WebControls"
   vs:friendlyName="SM Web controls Display Name" 
   vs:ishtmlschema="false"
   vs:iscasesensitive="false"
   vs:requireattributequotes="false"
   vs:htmlflavor="4.0"
   xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <xsd:annotation>
     <xsd:documentation>Descrizione dello schema</xsd:documentation>
   </xsd:annotation>

La prima parte è dedicata all'intestazione dello schema. Si importano i namespace per l'Intellisense di Visual Studio, il TargetNamespace e la validazione dello schema xsd. Particolare attenzione meritano le annotazioni per VS.

  • friendlyName contiene una descrizione associata allo schema;
  • ishtmlschema indica se questo schema si riferisce ad un documento HTML o XML rispettivamente true e false;
  • iscasesensitive indica se viene fatta distinzione tra maiuscole e minuscole nella validazione del controllo.
  • requireattributequotes indica l'obbligatorietà degli apici per gli attributi del controllo.
  • htmlflavor indica la versione di html che il browser che visualizza la pagina deve supportare. Es. 3.2 o 4.0. A seconda del TargetSchema selezionato per la pagina valida o meno il controllo.
 <xsd:element name="SMDiv" type="SMDivDef" />
<xsd:complexType name="SMDivDef">
   <xsd:attribute name="Text" type="xsd:string" />
   <xsd:attribute name="Height" type="intex" />
   <xsd:attribute name="Width" type=" intex " />
   <xsd:attribute name="Date" type="xsd:string" />
   <xsd:attributeGroup ref="attributeGroup" />
   <xsd:attribute name="Runat">
    <xsd:simpleType>
      <xsd:restriction base="xsd:string">
        <xsd:enumeration value="server" />
      </xsd:restriction>
    </xsd:simpleType>
   </xsd:attribute>
</xsd:complexType>

<xsd:simpleType name="intex">
   <xsd:restriction base="xsd:nonNegativeInteger">
    <xsd:minInclusive value="0" />
    <xsd:maxInclusive value="4294967295" />
   </xsd:restriction>
</xsd:simpleType>

<xsd:attributeGroup name="attributeGroup">
   <xsd:attribute name="Font-Size" type="intex" />
   <xsd:attribute name="Font-Overline" type="xsd:boolean" />
   <xsd:attribute name="Font-Underline" type="xsd:boolean" />
   <xsd:attribute name="Font-Names" type="xsd:string" />
   <xsd:attribute name="Font-Italic" type="xsd:boolean" />
   <xsd:attribute name="Font-Name" type="xsd:string" />
   <xsd:attribute name="Font-Bold" type="xsd:boolean" />
   <xsd:attribute name="Font-Strikeout" type="xsd:boolean" />
   <xsd:attribute name="ID" type="xsd:string" />
   <xsd:attribute name="EnableViewState" type="xsd:boolean" />
   <xsd:attribute name="Visible" type="xsd:boolean" />
   <xsd:attribute vs:omtype="event" name="OnDisposed" />
   <xsd:attribute vs:omtype="event" name="OnDataBinding" />
   <xsd:attribute vs:omtype="event" name="OnInit" />
   <xsd:attribute vs:omtype="event" name="OnLoad" />
   <xsd:attribute vs:omtype="event" name="OnPreRender" />
   <xsd:attribute vs:omtype="event" name="OnUnload" />
</xsd:attributeGroup>

La seconda parte dello schema contiene la vera definizione del nostro controllo. Si parte dalla definizione di un elemento che ha un attributo name, che specifica il nome del controllo, e uno type , che rappresenta il tipo che viene definito successivamente. L'ordine in cui si inseriscono le definizioni non è importante, quindi il tipo del nostro controllo può essere definito indifferentemente sia prima che dopo la dichiarazione dell'elemento.

La definizione del tipo comprende tanti nodi quante sono le proprietà e gli eventi del controllo. Le proprietà semplici (string, int, bool, ecc) sono dichiarate tramite un nodo dotato dell'attributo name , impostato sul nome della proprietà, e dell'attributo type, sul suo tipo.

Ad esempio una proprietà di tipo string viene mappata sul tipo "xsd:string". Per le proprietà complesse il discorso è molto simile, la sola differenza sta nel fatto che la proprietà type va impostata su un tipo custom definito all'interno del file. Ne sono un esempio le proprietà Height e Width di tipo intex .

Quando si sviluppano più controlli, può capitare di avere alcune proprietà comuni tra questi; in questo caso torna utile creare un tipo e poi dichiarare un nodo di quel tipo per raggruppare così in un unico punto le proprietà in comune. Ne è un esempio il tipo attributeGroup : questo sistema è adottato dai controlli di base del framework per le proprietà come Visibile, o gli eventi come Onload, OnInit, ecc.

Il meccanismo per l'implementazione degli eventi è molto simile a quello delle proprietà, ma ha una sintassi diversa. L'attributo name è impostato sul nome dell'evento, mentre l'attributo type è sostituito dalla notazione vs:omtype="event" , che indica a VS.NET di indicare questo elemento come un evento nel popup.

La segnalazione degli eventi introduce una delle notazioni di VS.NET a livello di proprietà, ovvero quelle che si applicano solo ad una proprietà e non ad un intero schema.. Le principali notazioni di questo tipo sono:

  • vs:builder contiene le indicazioni per l'editor associato al tipo della proprietà. I valori sono "color", "style", e "url";
  • vs:deprecated indica se il metodo o la proprietà è obsoleto. In caso il valore sia true, la proprietà non viene più visualizzata nella lista per l'autocompletion, e segnalata come obsoleta quando inserita tramite la classica sottolineatura rossa di VS;
  • vs:noambientcontentmodel indica, in caso di controlli che possono avere dei tag all'interno (Datagrid, Table, ecc), se questi possono contenere solo i figli definiti nello schema o tutti i tag dello schema;
  • vs:nonbrowsable indica se la proprietà deve apparire nelle popup di autocompletion. Non tutte le proprietà infatti sono settabili a design-time, basti pensare alla proprietà SelectedIndex di un DropDownList;
  • vs:omtype settato ad "event", indica che l'attributo si riferisce ad un evento (facendo apparire, nel popup, il simbolo dell'evento invece che quello della proprietà);
  • vs:readonly indica se la proprietà può essere modificata nel Property Editor.

Per ottenere l'autocompletion nelle nostre pagine mancano solo due ultimi accorgimenti.

Nel file AssembllyInfo.cs del controllo web si deve aggiungere l'attributo TagPrefix che specifica quale è il prefisso che il tag deve utilizzare in VS. I controlli di base hanno questo valore impostato su "asp";

[assembly: TagPrefix("SMControls.SMDivNS", "SM")]

Il primo parametro specifica il namespace del controllo, mentre il secondo il prefisso da utilizzare.

Nelle pagine HTML bisogna importare il namespace che contiene le definizioni aggiungendo un attributo xmlns al tag body della pagina.

<body xmlns:SM="urn:http://schemas.miosito.com/WebControls">

Subito dopo lo statement per importare il namespace, viene definito il prefisso del controllo e il TargetNamespace definito nel file xsd.

2 pagine in totale: 1 2

Attenzione: Questo articolo contiene un allegato.

Contenuti dell'articolo

Commenti

Visualizza/aggiungi commenti

| Condividi su: Twitter, Facebook, LinkedIn

Per inserire un commento, devi avere un account.

Fai il login e torna a questa pagina, oppure registrati alla nostra community.

Approfondimenti