LA.Net
Reflexões sobre C#, .Net e programação em geral

Ontem decidi efectuar alguns testes com o novo controlo XmlDataSource. Uma vez que precisava de obter todos os dados, achei por bem utilizar o controlo TreeView pois este controlo permite estabelecer relacionamentos de data binding com fontes de dados hierárquicas. O primeiro passo que dei consistiu em construir um documento de XML simples:

    1 <?xml version="1.0" encoding="UTF-8"?>

    2 <Alunos xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="Alunos.xsd">

    3   <Aluno>

    4     <Nome>João</Nome>

    5     <Morada>Funchal</Morada>

    6   </Aluno>

    7   <Aluno>

    8     <Nome>Rita</Nome>

    9     <Morada>Lisboa</Morada>

   10   </Aluno>

   11   <Aluno>

   12     <Nome>Pedro</Nome>

   13     <Morada>Porto</Morada>

   14   </Aluno>

   15 </Alunos>

Como é possível verificar através do excerto anterior, estamos na presença de um documento muito simples de XML. O próximo passo foi construir um form simples com dois controlos: uma TreeView e um controlo do tipo XmlDataSource:

   14 <form id="form1" runat="server">

   15         <asp:TreeView runat="server" DataSourceID="source"  ID="tree">           

   16         </asp:TreeView>

   17         <asp:XmlDataSource ID="source" runat="server" DataFile="Alunos.xml"/>

   18     </form>

Ao  utilizar o browser, reparei que não era bem isto que eu queria. A TreeView mostrava todos os nós contidos no documento; contudo, todos os nós de XML eram representados pelo respectivo nome. Achei por bem dar uma olhadela à documentação e foi aí que descobri o elemento TreeNodeBinding. Parecia ser a solução para o meu problema. Modifiquei então o código para o seguinte:

<asp:TreeView runat="server" DataSourceID="source"  ID="tree">

            <DataBindings>

                <asp:TreeNodeBinding DataMember="Nome" TextField="innertext" Depth="2"  />

                <asp:TreeNodeBinding DataMember="Morada" TextField="innertext" Depth="2"  />

            </DataBindings>

        </asp:TreeView>

        <asp:XmlDataSource ID="source" runat="server" DataFile="Alunos.xml">

 

        </asp:XmlDataSource>

Ao abrir o IE, deparo-me com uma excepção! Após ler a mensagem de erro, sou levado a pensar que o controlo TreeView não consegue encontrar a propriedade InnerText. Ao ver isto fiquei, como seria de esperar, de boca aberta! Fui novamente à documentação e verifiquei que os elementos do tipo XmlNode possuem de facto a propriedade InnerText. Mas então o que se estará a passar? Voltei a ler a documentação sobre o elemento TreeNodeBinding. Ao fazer scroll, reparei que existia um exemplo em que era feito o mapeamento com um documento de XML. Perfeito (pensei eu na altura)! Ao tentar analisar o exemplo, reparei que o mapeamento estava a ser executado entre o atributo do nó de XML e a propriedade TextField do elemento TreeNodeBinding. Mal podia acreditar no que estava a ver! É que de, acordo com a documentação, isto não poderia estar a acontecer (recordar que segundo a documentação, os valores contidos na propriedade TextField são utilizados indicar a propriedade do elemento a partir da qual iria ser obtido o valor; se é assim, como é que eles estavam a colocar lá o nome do atributo que estava contido no nó). Resolvi experimentar o exemplo fornecido. Para grande espanto meu, funcionou. Mas então o que se estará a passar?Foi aí que tive de recorrer aquela grande ferramenta (.Net Reflector) para tentar perceber o que se está a passar. Ao analisar o código relativo à classe XmlDataSource, podemos comprovar que esta classe fornece dois tipos de views: XmlDataSourceView e XmlHierarchicalDataSourceView .Cada uma delas é utilizada em situações diferentes (a primeira é utilizada quando temos controlos de visualização que esperam os dados no formato tabular; a segunda é utilizada por parte de controlos que trabalham com dados hierárquicos). Nesta situação, a vista utilizada era a associada a dados hierárquicos (por outras palavras, estavamos a utilizar a classe XmlHierarchicalDataSourceView).O procedimento de obtenção de dados baseia-se na execução do método Select desta classe. Ao observar o método Select, cheguei à conclusão que este retorna um elemento do tipo XmlHierarchicalEnumerable (a partir do nome já dá para perceber que esta classe implementa o interface IEnumerable - na verdade, implementa o interface IHierachicalEnumerable, que herda de IEnumerable...enfim, pormenores). É aqui que começa a ocorrer a "magia" responsável pelo comportamento descrito acima. Em vez de retornar a lista de elementos do tipo XmlNode que a classe contém quando é evocado o método GetEnumerator, esta classe opta por construir uma nova lista (ArrayList) com elementos do tipo XmlHierarchicalData (cada elemento deste tipo é responsável por conter um nó do tipo XmlNode). Mas então porquê utilizar esta classe (XmlHierarchicalData)? aha! Ainda bem que perguntam...Na verdade, esta classe implementa o interface ICustomTypeDescriptor. A implementação deste interface permite personalizar o processo de obtenção de propriedades através da utilização de um TypeDescriptor (sim, é verdade! Internamente, a classe TreeView tenta obter as propriedades contidas em cada nó de XML através da utilização de um TypeDescriptor).  Assim, basta olharmos para a implementação do método ICustomTypeDescriptor.GetProperties para termos a noção real do que se está passar: esta classe está modificar as propriedades associadas a cada nó! Em vez das tradicionais propriedades descritas na documentação associada ao XmlNode, a classe está a devolver os nomes dos atributos contidos no nó (por isso é que o sample da MSDN funciona!). Para além disso, a classe adicionar três valores especiais: #InnertText, #Value e #Name.aha! Agora estamos a chegar mais perto da verdade :)Claro que a classe recorre a uma classe interna (XmlHierarchyDataPropertyDescriptor) para encapsular cada PropertyDescritpor definido desta forma. Aliás, convém referir que é esta classe que será responsável por devolver o valor da propriedade especificada no campo TextField do exemplo anterior. Ao analizar esta classe, pude verificar que os valores especiais #InnertText, #Name e #Value são mapeados nas propriedades InnerText, Name e Value da classe XmlNode. E cá está a solução para o problema inicial (como especificar que o binding deve obter o texto contido no nó!). Basta utilizar o valor especial #innertext, conforme o sample seguinte ilustra:

       <asp:TreeView runat="server" DataSourceID="source"  ID="tree">

            <DataBindings>

                <asp:TreeNodeBinding DataMember="Nome" TextField="#innertext" Depth="2"  />

                <asp:TreeNodeBinding DataMember="Morada" TextField="#innertext" Depth="2"  />

            </DataBindings>

        </asp:TreeView>

        <asp:XmlDataSource ID="source" runat="server" DataFile="Alunos.xml"/>

É óbvio que para descobrir este tipo de informação levei algum tempo. Será que era mesmo necessário perder tanto tempo a perceber o que se estava a passar? Bem, eu sei que não iria resistir a tentar perceber o mecanismo interno de binding...contudo, desta vez queria apenas utilizar o controlo XmlDataSource associado a um controlo TreeView. A minha questão é a seguinte: Porque é que a documentação não fala nestes valores especiais? Devemos ser bruxos ou a Microsoft está interessada em promover o .Net Reflector?

posted on Saturday, February 12, 2005 6:30 PM
Comments
Title  
Name  
Url
Box Code
Protected by FormShield
Comments