Ho utilizzato questa tecnica per generare dinamicamente un accordion menù multilivello …Consideramo il seguente diagramma ad albero, rappresentazione della struttura di un possibile menù di navigazione:
- Root (nodeID=1, parentID=0) - Item 1 (nodeID=2, parentID=1) - item 1.1 (nodeID=3, parentID=2) - item 1.2 (nodeID=4, parentID=2) - item 1.2.1 (nodeID=5, parentID=4) - item 1.3 (nodeID=6, parentID=2) - Item 2 (nodeID=7, parentID=1)
La seguente struttura può essere memorizzata nella seguente tabella di un database: MenuTable(nodeID,parentID,title).A partire dalla tabella MenùTable è possibile generare facilmente la rappresentazione XML del grafo ad albero utilizzando il seguente codice:
Dim sqlString As String = "SELECT NodeID, ParentID, title FROM MenuTable ORDER BY NodeID" Dim cmd As SqlCommand = New SqlCommand(sqlString) Dim conn As SqlConnection conn = New SqlConnection(DBConnection) Dim ds As DataSet = New DataSet Dim da As SqlDataAdapter = New SqlDataAdapter da.SelectCommand = cmd da.Fill(ds) Dim dt As DataTable = ds.Tables(0) ' create an XmlDocument (with an XML declaration) Dim XDoc As New XmlDocument() Dim XDec As XmlDeclaration = XDoc.CreateXmlDeclaration("1.0", Nothing, Nothing) XDoc.AppendChild(XDec) ' iterate through the sorted data ' and build the XML document For Each Row As DataRow In dt.Rows ' create an element node to insert ' note: Element names may not have spaces so use ID ' note: Element names may not start with a digit so add underscore Dim NewNode As XmlElement = XDoc.CreateElement("_" & Row("NodeID").ToString()) NewNode.SetAttribute("NodeID", Row("NodeID").ToString()) NewNode.SetAttribute("ParentID", Row("ParentID").ToString()) NewNode.SetAttribute("title", Row("title").ToString()) ' special case for top level node If IsDBNull(Row("ParentID")) Then XDoc.AppendChild(NewNode) Else ' root node ' use XPath to find the parent node in the tree Dim SearchString As [String] SearchString = [String].Format("//*[@NodeID=""{0}""] ", Row("ParentID").ToString()) Dim Parent As XmlNode = XDoc.SelectSingleNode(SearchString) If Parent IsNot Nothing Then Parent.AppendChild(NewNode) Else ' Handle Error: Employee with no boss End If End If Next XDoc.Save(Server.MapPath("~/menu/menu.xml"))
In tal modo nella cartella menu della nostra applicazione verrà generato il file menu.xml così fatto:
<?xml version="1.0"?> <_1 NodeID="1" ParentID="0" title="Root"> <_2 NodeID="2" ParentID="1" title="item 1"> <_3 NodeID="3" ParentID="2" title="item 1.1" /> <_4 NodeID="4" ParentID="2" title="item 1.2" > <_5 NodeID="5" ParentID="5" title="item 1.2.1" /> </_4> <_6 NodeID="6" ParentID="2" title="1.3" /> </_2> <_7 NodeID="7" ParentID="1" title="item 2"/> </_1>
Vogliamo ora trasformare la struttura XML in una stringa HML così fatta:
<li><a name="_2" href="#">item 1</a> <ul> <li><a name="_3" href="page.aspx?nodeID=3">item 1.1</a></li> <li><a name="_4" href="#">item 1.2</a> <ul> <li><a name="_5" href="page.aspx?nodeID=5">item 1.2.1</a> </ul> </li> <li><a name="_6" href="page.aspx?nodeID=6">item 1.3</a></li> </ul> </li> <li><a name="_7" href="page.aspx?nodeID=7">item 2</a></li>
Definiamo quindi il seguente file XSLT di trasformazione e memorizzaiamolo nella cartella “menu” con il nome menu.xslt
<?xml version="1.0" encoding="UTF-8" ?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output omit-xml-declaration="yes"/> <!--The <xsl:template> element contains rules to apply when a specified node is matc--> <xsl:template match="node()"> <!--calcola il numero di nodi figli dell'elemento corrente--> <xsl:variable name="total-el"> <xsl:value-of select="count(descendant::*)"/> </xsl:variable> <xsl:variable name="name"> <xsl:value-of select="name()"/> </xsl:variable> <!--se il nodo nonè la root ed il numero di nodi figlio è >0 allora applica il seguente modello ai nodi figlio dell'elemento corrente..--> <xsl:if test="$total-el > 0 and $name != '_1'"> <li> <a name="{name()}" href="#" rel="self"> <xsl:value-of select="@NodeHTML"/> </a> <ul> <xsl:apply-templates /> </ul> </li> </xsl:if> <xsl:if test="$total-el > 0 and $name = '_1'"> <ul class="topnav"> <xsl:apply-templates /> </ul> </xsl:if> <!--altrimenti ...--> <xsl:if test="$total-el = 0"> <li> <a name="{name()}" href="articles.aspx?NodeID={name()}"> <xsl:value-of select="@NodeHTML"/> </a> </li> </xsl:if> </xsl:template> </xsl:stylesheet>
Non ci resta infine che applicare la trasformazione mediante il segunte codice:
Dim xDoc As New XmlDocument() xDoc.Load(Server.MapPath("~/menu/menu.xml")) Dim sw As New System.IO.StringWriter() Dim xslTrans As New XslCompiledTransform() xslTrans.Load(Server.MapPath("~/menu/menu.xslt")) xslTrans.Transform(xDoc.CreateNavigator(), New XsltArgumentList(), sw) Using writer As System.IO.StreamWriter = System.IO.File.AppendText(HttpRuntime.AppDomainAppPath & "menu\menu.txt") writer.WriteLine(sw.ToString()) End Using
producendo nel file menu.txt nella cartella menu il codice html desiderato.