本文的目的是为启用ASP.NET网站创建一个光滑且易于使用的菜单。为了实现这一目标,我们将使用ASP.NET菜单Web控件的标准功能,并通过使用级联样式表(CSS)纯粹增强外观。
对于那些熟悉CSS的人,本文的标题肯定会敲响铃铛。用于增强ASP.NET菜单控件外观和感觉的CSS在列表中众所周知的文章中删除,标题为“ CSS的滑动门”。
在列表中的文章中,用来改善菜单外观和感觉的CSS进行了详尽的解释。信用何时到期。感谢道格拉斯·鲍曼(Douglas Bowman)向我们提供CSS。如果您不熟悉,请务必先阅读他的文章。
本文的重点是如何在ASP.NET环境中模拟此类菜单。让我们开始...
让我们启动Visual Studio 2008,并使用ASP.NET Web应用程序项目模板创建一个新项目。此模板将自动添加默认的Web单是适当命名为“ Default.aspx”。在编辑器中打开它,然后从工具箱中添加菜单控件,您可以在“导航”选项卡下找到。如果您现在运行该网站,您将盯着空白页。要使菜单显示首先必须绑定到某些数据的所有内容。
定义菜单控件的数据的最简单方法可以使用并使访问者可以通过网站导航将其绑定到站点地图。使用建议的“ web.sitemap”的名称添加一个新的站点地图项目。站点映射是一个XML文件,该文件以层次结构方式组织站点的页面。另一个优势是SiteMapDatasource控件会自动引用它。
接下来,在以下列表中显示的站点地图中添加一些页面。
清单1- web.sitemap
<? xml version = " 1.0 " encoding = " utf-8 " ?>
< siteMap xmlns = " http://schemas.microsoft.com/AspNet/SiteMap-File-1.0 " >
< siteMapNode url = " " title = " " description = " " >
< siteMapNode url = " Default.aspx " title = " Home " description = " Take me back to the dasboard " />
< siteMapNode url = " Products.aspx " title = " Products " description = " Browse our catalog " />
< siteMapNode url = " Download.aspx " title = " Download " description = " Download neat stuff " />
< siteMapNode url = " Forum.aspx " title = " Forum " description = " Ask questions on our forum " />
< siteMapNode url = " Contact.aspx " title = " Contact " description = " Contact us " />
</ siteMapNode >
</ siteMap >现在,将SiteMapDataSource控件添加到页面上,并将菜单控件的DataSourceID属性设置为数据源的ID。还将菜单控件的方向属性设置为水平,因为它默认为垂直。最后但并非最不重要的一点将STAWEMAPDATASOURCE的ShowStartingNode属性设置为false。如果您不这样做,则将显示根节点,我们不想在菜单中包含此基本节点。您的代码现在应该类似于清单2中显示的代码。
列表2 -Default.aspx
< asp:Menu
ID =" Menu1 "
runat =" server "
DataSourceID =" SitemapDataSource1 "
Orientation =" Horizontal "
> </ asp:Menu >
< asp:SiteMapDataSource
ID =" SiteMapDataSource1 "
runat =" server "
ShowStartingNode =" False "
/>如果您现在在浏览器中查看页面,则应看到功能性但无聊的菜单。
图1-平原水平菜单

在我们开始将CSS应用于菜单之前,还有另一个问题需要首先解决。如果您查看当您请求默认值时生成的生成的HTML代码。ASPX页面时,您会注意到菜单控件不会生成最灵活的HTML代码。默认情况下,它将每个菜单项包装在表中。这并不适合轻松应用CSS。如果菜单控件生成一个包含所有菜单项的Unordererd列表,那就更好了。
幸运的是,可以使用控制适配器调整生成的HTML。控制适配器允许您渲染您喜欢的HTML。值得庆幸的是,这种控制适配器在CodePlex上很容易获得。 CSS友好控制适配器套件提供了预制的控制适配器,其中包括ASP.NET菜单控件。
使用CSS友好控制适配器遵循以下步骤:
有必要使用源代码并编译CSSFriffly.DLL组件,因为我们需要调整适配器后来在本文中使用的一些CSS。
在将CSS友好项目添加到您的解决方案中时,Visual Studio转换向导将弹出。只需执行转换,一切都应该顺利进行。转换完成后,剩下的所有内容就是从ASP.NET网站项目中添加对CSSFriendly项目的参考。
只需运行网站并立即查看生成的HTML代码即可。
清单3 -CSS友好的HTML代码
< div class =" AspNet-Menu-Horizontal " id =" Menu1 " >
< ul class =" AspNet-Menu " >
< li class =" AspNet-Menu-Leaf AspNet-Menu-Selected " >
< a
href =" /Default.aspx "
class =" AspNet-Menu-Link AspNet-Menu-Selected "
title =" Take me back to the dasboard "
> Home </ a
>
</ li >
< li class =" AspNet-Menu-Leaf " >
< a
href =" /Products.aspx "
class =" AspNet-Menu-Link "
title =" Browse our catalog "
> Products </ a
>
</ li >
< li class =" AspNet-Menu-Leaf " >
< a
href =" /Download.aspx "
class =" AspNet-Menu-Link "
title =" Download neat stuff "
> Download </ a
>
</ li >
< li class =" AspNet-Menu-Leaf " >
< a
href =" /Forum.aspx "
class =" AspNet-Menu-Link "
title =" Ask questions on our forum "
> Forum </ a
>
</ li >
< li class =" AspNet-Menu-Leaf " >
< a href =" /Contact.aspx " class =" AspNet-Menu-Link " title =" Contact us "
> Contact </ a
>
</ li >
</ ul >
</ div >事情正在抬头。
注释:CSSFriendlyAdapters.Browser文件允许您指定应使用哪些CSS友好控制适配器。除了我要使用的适配器外,我还可以评论所有适配器。这样,没有其他控件是“适应”的,它们将继续生成默认的HTML。
从您可以从适配器上方显示的代码中看到的菜单控件中显示的代码会自动为<ul> , <li>和<a>标签注入必要的CSS类。这为我们节省了不得不定义这些问题的麻烦。
单页包含多个适配器控件(例如菜单)也很常见。如果您想为每个控件具有独特的外观和感觉,请为适应的控件设置CSSSELECTORCLASS。例如,您可以设置CSSSELECTORCLASS属性的值,如下所示:
清单4 -CSSSELECTORCLASS属性
< asp:Menu
ID =" Menu1 "
runat =" server "
DataSourceID =" SitemapDataSource1 "
Orientation =" Horizontal "
CssSelectorClass =" PrettyMenu "
> </ asp:Menu >结果是由改编的控件生成的HTML代码将包含在新层中( <div> )中。
清单5-包装生成的HTML代码
< div class =" PrettyMenu " id =" Menu1 " >
<!-- Other HTML code -->
</ div >请注意,此属性是特定于CSS友好适配器的。这是一个自定义(Expando)属性,您可以为此库支持的控件设置它。您需要有关友好适配器工作方式的更多信息,请咨询以下链接:
随着CSSSELECTORCLASS周围生成单独的图层( <div> ),并自动注入CSS类,我们终于准备好开始菜单。
使用的CSS与A列表的文章相似。如前所述,本文的重点不是CSS的结构,而是将其应用于ASP.NET菜单控件以获取整洁的表格菜单。完成的结果可以在图2中看到。
图2-完成的结果

在下载页面上,您可以找到本文的源代码。它包含样式表和必要的图像。只需执行以下步骤即可重新创建完成的结果:
<Pages>节点的主题属性。 (请参阅清单6)清单6- web.config摘录
< system .web>
<!-- ... -->
< pages theme = " Default " >
<!-- ... -->
</ pages >
<!-- ... -->
</ system .web>我的解决方案中使用的CSS与列表中的文章相似,但是有一些更改。主要的变化与CSS友好适配器应用的CSS结构相关。
为了应用CSS,正确的结构需要使用。在通过友好适配器调整HTML的控件时,我发现使用白皮书中提到的图表很方便。这些图清楚地向您展示了CSS的结构。
您可以在此处找到菜单控件的图。
我经常在许多论坛上提出的一个问题是如何实现水平子菜单,当用户从顶级菜单中选择其他选项卡时会发生变化。为了创建这个问题,我们首先需要调整我们的站点图。
清单7显示了调整后的站点图。
清单7- web.sitemap带有“子菜单项”
<? xml version = " 1.0 " encoding = " utf-8 " ?>
< siteMap xmlns = " http://schemas.microsoft.com/AspNet/SiteMap-File-1.0 " >
< siteMapNode url = " " title = " " description = " " >
< siteMapNode url = " Default.aspx " title = " Home " description = " Take me back to the dasboard " >
< siteMapNode url = " About.aspx " title = " About us " description = " " />
< siteMapNode url = " Foo.aspx " title = " Foo " description = " " />
< siteMapNode url = " Bar.aspx " title = " Bar " description = " " />
</ siteMapNode >
<!-- ... -->
</ siteMap >为了简化的清单,仅显示家庭节点的子菜单项(节点)。在源代码中查看Web.sitemap的完整版本。现在,当您使用此网站图浏览网站时,您会看到图3中显示的效果。
图3-子菜单项

为了禁用此效果,如列表8所示,将菜单控件的最大dynamicDisplaylevels属性设置为零。
清单8-最大dynamicdisplaylevels属性
< asp:Menu
ID =" Menu1 "
runat =" server "
DataSourceID =" SitemapDataSource1 "
Orientation =" Horizontal "
CssSelectorClass =" PrettyMenu "
MaximumDynamicDisplayLevels =" 0 "
> </ asp:Menu >接下来,添加第二个菜单和SiteMapDataSource控件,并设置其属性,如列表9所示。
清单9-子菜单控件
< asp:Menu
ID =" Menu1 "
runat =" server "
DataSourceID =" SitemapDataSource1 "
Orientation =" Horizontal "
CssSelectorClass =" PrettyMenu "
MaximumDynamicDisplayLevels =" 0 "
>
</ asp:Menu >
< asp:Menu
ID =" Menu2 "
runat =" server "
DataSourceID =" SitemapDataSource2 "
Orientation =" Horizontal "
CssSelectorClass =" PrettySubMenu "
>
</ asp:Menu >
< asp:SiteMapDataSource
ID =" SiteMapDataSource1 "
runat =" server "
ShowStartingNode =" False "
/>
< asp:SiteMapDataSource
ID =" SiteMapDataSource2 "
runat =" server "
StartingNodeOffset =" 1 "
ShowStartingNode =" False "
/>我们基本上告诉第二个菜单,它应该显示站点映射中的第二级节点,并且不应显示与第一个菜单所选选项卡相对应的启动节点。
另外,第二个菜单的CSSselectorClass设置为PrettySubMenu。此菜单的CSS与第一个菜单相似。如果要检查出来,请下载源代码。现在运行该网站将为您带来这个柔软的结果。
图4-样式的子菜单项

最后一个问题需要解决。为了说明这个问题,需要重新组织项目。
首先,让我们将主页添加到名为site.master的项目中。将代码,菜单和数据源(从默认为aspx页面)移至主页上,然后将其放在体内的第一个内容座位上。之后,您可以安全地删除default.aspx页面。
现在,让我们在项目中添加一些Web内容表单项目。添加与网站图及其子节点的第一个节点的项目相对应的Web内容表单,即:
请确保选择site.master作为这些Web内容表单中的每一个的主页。网络中提到的其他页面纯粹是出于说明目的。
再次启动网站,然后导航到FOO页面。这将揭示问题。
图5-选择问题

因为我们已经导航到网站映射的底层,所以顶级菜单不知道它应该标记为选定的菜单项。为了解决此问题,我们将需要在主页的主页上进行一些编码。
清单10显示了您必要的代码。
清单10-选择正确的顶级菜单项
namespace MenuWebApplication
{
public partial class Site : System . Web . UI . MasterPage
{
private static string ExtractBaseUrl ( string url )
{
return url . Contains ( "?" ) ? url . Remove ( url . IndexOf ( '?' ) ) : url ;
}
protected void Page_Load ( object sender , EventArgs e )
{
Menu1 . DataBind ( ) ;
// Which node in the site map is currently selected?
SiteMapNode currentNode = SiteMap . CurrentNode ;
if ( currentNode != null )
{
// Obtain the Url of the currently selected node's parent node.
string parentUrl = String . Empty ;
SiteMapNode parentNode = currentNode . ParentNode ;
if ( parentNode != null )
{
parentUrl = ExtractBaseUrl ( parentNode . Url ) ;
}
// Obtain the Url of the currently selected node.
string currentUrl = ExtractBaseUrl ( currentNode . Url ) ;
// Iterate the top level menu tier.
foreach ( MenuItem menuItem in Menu1 . Items )
{
// Compare the menu item's Url against the currently
// selected node's Url or the Url of its parent.
string menuItemUrl = ExtractBaseUrl ( menuItem . NavigateUrl ) ;
if ( ( currentUrl == menuItemUrl ) || ( parentUrl == menuItemUrl ) )
{
// If either matches then mark the top level menu item as selected.
Menu1 . Items [ Menu1 . Items . IndexOf ( menuItem ) ] . Selected = true ;
break ;
}
}
}
}
}
}现在,您可以自由选择任何子菜单项。它是相应的顶级菜单项,将继续选择,以使访客有一个视觉线索,了解他目前位于网站中的位置。
图6-固定的选择问题

建立一个带有上下文敏感的水平子菜单的精美表格菜单是很多工作,但最终您会以光滑的外观和用户友好的菜单结束。
我建议您在用户控件中实现此类菜单,并在网站的主页上包含该用户控件。在大多数情况下,将在整个网站中使用一种这样的菜单。