DropDownList控件在直接绑定XmlDataSource的时候会出现错误提示,这个问题把我折腾了好几宿都没睡安稳。MSDN上没有对这个问题的详细解答,CSDN上大家的解决办法也都是五花八门。终归离不开一个思路:以DataSet为跳转,先创建本地数据集从XmlDataSource里把数据取出来,再作为DropDownList的数据源进行数据绑定。这种做法不错,但是写代码的时候难免会出错,并且我对这样做的效率深表怀疑。究其根源,DropDownList不能直接绑定XmlDataSource不是VS的Bug,我也是看了国外一个大牛写的文章,才恍然大悟。
首先创建一个名为XMLDataBing的网站,在App_Data下添加新项,VS会自动添加一个数据库项。选择XML文件,如图。XML的内容就随便写一点吧:
<?xml version="1.0" encoding="utf-8" ?>
<Texts>
<Text>
<Name>Text1</Name>
<Id>1</Id>
</Text>
<Text>
<Name>Text2</Name>
<Id>2</Id>
</Text>
<Text>
<Name>Text3</Name>
<Id>2</Id>
</Text>
</Texts>
这是最简单也是最基本的。现在看Default.aspx的设计视图,拖一个DropDownList、一个XmlDataSource出来。一般人都会顺手写DropDownList的数据绑定:
<asp:DropDownList ID="DropDownList1" runat="server" DataSourceID="XmlDataSource1"
DataTextField='XPath("Name")' DataValueField='XPath("Id")'>
</asp:DropDownList>
<asp:XmlDataSource ID="XmlDataSource1" runat="server" DataFile="~/App_Data/XMLFile.xml"
XPath="/Texts/Text">
</asp:XmlDataSource>
然后编译通过,却在IE里很诧异地得到一个错误:
DataBinding:“System.Web.UI.WebControls.XmlDataSourceNodeDescriptor”不包含名为“XPath(“Name”)”的属性。
很醒目!错误说得很清楚,但是查MSDN死活也找不到XmlDataSourceNodeDescriptor的类定义。Google一下,查到Microsoft Online Community Support上某大牛的解释:
System.Web.UI.WebControls.XmlDataSourceNodeDescriptor is an undocumented internal class which functions like a TypeDescriptor. It is used during data binding when the data source is XmlDataSource. Since it is an internal class, we are not having any public document on it.
大意是:System.Web.UI.WebControls.XmlDataSourceNodeDescriptor是一个未归档的内部类,它的工作原理就像TypeDescriptor。它应用在数据源是XmlDataSource的数据绑定时。由于是个内部类,我们还没有任何关于它的公开文档。
不知各位看了是个什么心情,反正我是万念俱灰了。。。。。。
MS把XmlDataSourceNodeDescriptor作为XmlDataSource数据源进行绑定的重要内部类,出很低级的Bug让DropDownList不能直接绑定XmlDataSource的可能性很小,只是我们忽略了什么重要的东西。再回头想想刚才的整个过程,表面上看不出任何问题。我们不妨重新写一个XML文件,作个小改动:
<?xml version="1.0" encoding="utf-8" ?>
<Texts>
<Text Name="Text1" Id="1" />
<Text Name="Text2" Id="2" />
<Text Name="Text3" Id="3" />
</Texts>
在Default.aspx上新加一个XmlDataSource,DropDownList选择数据源时会有明显的变化!数据源选择XmlDataSource2之后DataTextField和DataValueField选项会自动提示选择Name还是Id。注意,这说明DropDownList能正确读取XML文档了!
问题解决了,但原因还没找到。CSDN里有人说直接绑定就OK,显然是用第二种方法写的XML。这两种写法的区别在于
<Text Name="Text1" Id="1" />
中,Name和Id是Text元素的属性,而
<Text>
<Name>Text1</Name>
<Id>1</Id>
</Text>
中,Name和Id并非Text元素的属性,而是一个元素节点。当数据绑定时,元素的属性会被提升为XmlDataSourceNodeDescriptor的属性,通过XmlDataSource暴露出来,但这里的元素节点却不会,导致出错。所以最简单的办法,把你的XML按第二种方法写,就可以零代码绑定了!
不过有人跟我一样喜欢犯贱,非得按第一种方法写。其实照样可以成功,只不过要引入一个XLST文件。不多废话,MSDN对XLST的解释如下:
XSL 转换 (XSLT) 样式表(.xslt 或 .xsl 文件)用于将源 XML 文档的内容转换为专门适合于特定用户、媒介或客户端的表现形式。
说白了,XSLT就是XML的模板。XmlDataSource会按照XSLT规定的模板读取并转换XML文档里的数据,而不考虑XML文档里规定的数据结构。在站点上点右键选添加新项,如图会有XLST文件的模板出现。恩,写一下前面例子的XSLT吧:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="Texts">
<Texts>
<xsl:apply-templates select="Text"/>
</Texts>
</xsl:template>
<xsl:template match="Text">
<Text>
<xsl:attribute name="Name">
<xsl:value-of select="Name"/>
</xsl:attribute>
<xsl:attribute name="Id">
<xsl:value-of select="Id"/>
</xsl:attribute>
</Text>
</xsl:template>
</xsl:stylesheet>
OK,别忘了在XmlDataSource1里说明使用刚才的XSLT文件
<asp:XmlDataSource ID="XmlDataSource1" runat="server" DataFile="~/App_Data/XMLFile.xml"
XPath="/Texts/Text" TransformFile="~/XSLTFile.xsl"></asp:XmlDataSource>
大功告成!现在得到了正确结果,大家不用再失眠了。使用XSLT还能让DataTextField和DataValueField同时绑定多个元素属性,具体方法大家可以自行Google。只有一个可考虑可忽略的问题:按照第一种写法,使用DataList或者其他某些控件时,用XPath是有效的;DropDownList为什么不能用XPath,还得多研究。