16.9 Menu控件
与TreeView控件一样,Menu控件是另一个支持层次化数据的导航控件。在日常使用中,可以把Menu控件绑定到数据源,或者手工(声明性地或者通过编程)使用MenuItem对象来填充它。
与TreeView控件不同的是,MenuItem对象并不支持复选框,也不能够通过编程设置它们的折叠/展开状态。不过,它们还是有很多相似的属性,包括那些用于设置图片、确定项目是否可选以及指定目标链接的属性。
16.9.1 Menu控件结构
Menu控件由菜单项(由MenuItem对象表示)树组成。顶级(级别0)菜单项称为根菜单项。具有父菜单项的菜单项称为子菜单项。所有根菜单项都存储在Items集合中。子菜单项存储在父菜单项的ChildItems集合中。
与TreeView控件相似,如表16-16所示,每个菜单项都具有Text属性和Value属性。Text属性的值显示在Menu控件中,而Value属性则用于存储菜单项的任何其他数据。如果设置了Text属性,但是未设置Value属性,则Value属性会自动设置为与Text属性相同的值。反之亦然。如果设置了Value属性,但是未设置Text属性,则Text属性会自动设置为Value属性的值。
单击菜单项可导航到NavigateUrl属性指示的另一个网页。如果菜单项未设置NavigateUrl属性,则单击该菜单项时,Menu控件只是将页提交给服务器进行处理。其中,MenuItem属性如表16-16所示。
下面的示例演示Menu控件的声明性标记。该控件有三个菜单项,每个菜单项有两个子项:
<asp:Menu ID="Menu1"runat="server">
<Items>
<asp:MenuItem Text="File"Value="File">
<asp:MenuItem Text="New"Value="New"/>
<asp:MenuItem Text="Open"Value="Open"/>
</asp:MenuItem>
<asp:MenuItem Text="Edit"Value="Edit">
<asp:MenuItem Text="Copy"Value="Copy"/>
<asp:MenuItem Text="Paste"Value="Paste"/>
</asp:MenuItem>
<asp:MenuItem Text="View"Value="View">
<asp:MenuItem Text="Normal"Value="Normal"/>
<asp:MenuItem Text="Preview"Value="Preview"/>
</asp:MenuItem>
</Items>
</asp:Menu>
上面的示例代码运行结果如图16-41所示。
图 16-41 Menu控件运行示例
在上面的示例中,还可以通过Menu控件的Orientation属性来改变菜单的显示样式。其中,Orientation属性有两个值:Horizontal与Vertical。如果将上面的Menu控件修改为:
<asp:Menu ID="Menu1"runat="server"
Orientation="Horizontal">
则运行结果如图16-42所示。
16.9.2 Menu控件显示模式
Menu控件具有两种显示模式:静态模式和动态模式。
1.静态模式
静态显示意味着Menu控件始终是完全展开的。整个结构都是可视的,用户可以单击任何部位。使用Menu控件的StaticDisplayLevels属性可控制静态显示行为。StaticDisplayLevels属性指示从根菜单算起静态显示的菜单的层数。例如,如果将StaticDisplayLevels设置为3,菜单将以静态显示的方式展开其前三层。静态显示的最小层数为1,如果将该值设置为0或负数,该控件将会引发异常。
例如,将上面示例中的Menu控件声明如下:
<asp:Menu ID="Menu1"runat="server"StaticDisplayLevels="2">
运行结果如图16-43所示。
图 16-42 Orientation="Horizontal"的运行结果
图 16-43 StaticDisplayLevels="2"的运行结果
2.动态模式
动态显示的菜单中,只有指定的部分是静态的,而只有用户将鼠标指针放置在父节点上时才会显示其子菜单项。MaximumDynamicDisplayLevels属性指定在静态显示层后应显示的动态显示菜单节点层数。例如,如果菜单有3个静态层和2个动态层,则菜单的前三层静态显示,后两层动态显示。如果将MaximumDynamicDisplayLevels设置为0,则不会动态显示任何菜单节点。如果将MaximumDynamicDisplayLevels设置为负数,则会引发异常。
16.9.3 从数据库动态绑定Menu控件
与TreeView控件一样,Menu控件也支持从数据库动态绑定的技术,其绑定方法与TreeView控件类似。下面的示例演示了这种动态绑定技术。同样,需要在页面声明一个Menu控件,如下面的代码所示:
<asp:Menu ID="Menu1"runat="server">
在页面声明了Menu控件之后,就需要在后台代码里来处理它了。在这里,通过两个方法来实现数据绑定。其中,BindMenuItem方法用于创建Menu控件的根节点,然后利用这些根节点在CreateChildNode方法里面寻找其相应的子节点。在CreateChildNode方法里采用递归的方式去一层一层寻找余下的子节点。如下面的代码所示:
public partial class TestMenu:System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if(!Page.IsPostBack)
{
BindMenuItem(Menu1);
}
}
///<summary>
///创建Menu的根节点
///</summary>
///<param name="menu">Menu实例</param>
public void BindMenuItem(Menu menu)
{
string sql="select*from SiteMap";
DataTable dt=GetData(sql);
//查找根节点
DataRow[]row=dt.Select("id=0");
if(row.Length>0)
{
MenuItem menuItem=new MenuItem(
row[0]["title"].ToString(),
row[0]["ID"].ToString());
menu.Items.Add(menuItem);
CreateChildNode(menuItem, dt);
}
}
///<summary>
///使用递归创建子节点
///</summary>
///<param name="menuItem">根节点</param>
///<param name="dt">SiteMap表数据</param>
private void CreateChildNode(MenuItem menuItem, DataTable dt)
{
//在所有的节点中,选择父节点ID为某个值的节点
DataRow[]rows=dt.Select("ParentID="+menuItem.Value);
foreach(DataRow row in rows)
{
MenuItem childIenuItem=new MenuItem(
row["title"].ToString(),
row["ID"].ToString());
menuItem.ChildItems.Add(childIenuItem);
//递归,将生成的childIenuItem作为根节点,继续插入它的子节点
CreateChildNode(childIenuItem, dt);
}
}
private DataTable GetData(string sqlString)
{
SqlConnection sqlConn=new SqlConnection(
ConfigurationManager.ConnectionStrings
["SiteMapConnectionString"].ToString());
SqlCommand sqlCmd=new SqlCommand(sqlString, sqlConn);
DataSet dataSet=new DataSet();
SqlDataAdapter sqlDataAdapter=
new SqlDataAdapter(sqlCmd);
sqlDataAdapter.Fill(dataSet);
DataTable dt=dataSet.Tables[0];
return dt;
}
}
运行上面的示例,结果如图16-44所示。
图 16-44 动态绑定Menu控件的示例
由上面的示例可知,虽然Menu控件和TreeView控件的呈现方式不同,但它们却暴露了非常相似的编程模型。同时,它们还有相似的基于样式的格式化模型,并且具有一些显著的差异:
1)Menu控件每次显示一个子菜单,而TreeView控件可以一次展开任意多个节点。
2)Menu控件在页面里显示第一层的链接,所有其他项以上下文菜单的方式显示在页面其他内容的上方,而TreeView控件显示在页面上内联的所有项。
3)TreeView控件支持按需填充以及客户端回调,而Menu控件不支持。
4)Menu控件支持模板,而TreeView控件不支持。
5)TreeView控件的所有节点都支持复选框,而Menu控件不支持。
6)根据Orientation属性,Menu控件支持水平和垂直布局,而TreeView控件只支持垂直布局。
16.9.4 Menu样式
Menu控件与TreeView控件相似,Menu控件也从Style基类派生了自定义类,即MenuStyle类和MenuItemStyle类,它支持位于不同层级的菜单定义不同的菜单样式。不过,它们之间主要的区别是Menu控件鼓励区分静态项(菜单刚创建时就显示在页面上的第一层项目)和动态项(当把鼠标移动到菜单某个区域时被添加的弹出的菜单项)。对于大多数网站,这两个元素具有明显的区别。为了支持这些,它定义了两组平行的样式,一组定义静态项(以Static开头的样式属性),另一组定义动态项(以Dynamic开头的样式属性),如表16-17所示。
对于这些样式,可以使用内联样式进行设置,也可以使用一个单独的CSS文件进行设置。如下面的示例所示:
<asp:Menu ID="Menu1"runat="server"
DisappearAfter="2000"StaticDisplayLevels="2"
StaticSubMenuIndent="10"Orientation="Vertical"
Font-Names="Arial">
<StaticMenuItemStyle BackColor="LightSteelBlue"
ForeColor="Black"/>
<StaticHoverStyle BackColor="LightSkyBlue"/>
<DynamicMenuItemStyle BackColor="Black"ForeColor="Silver"/>
<DynamicHoverStyle BackColor="LightSkyBlue"
ForeColor="Black"/>
</asp:Menu>
添加样式后的运行结果如图16-45所示。
除了设置各样式属性之外,还可以根据菜单项的级别。使用下列样式集合指定应用于菜单项的样式,如表16-18所示。
集合的第一个样式对应于菜单树第一个深度级别的菜单项的样式,集合的第二个样式对应于菜单树第二个深度级别的菜单项的样式,依此类推。此集合最常用于生成目录风格的导航菜单;在这种导航菜单中,某个深度的菜单项不管是否具有子菜单,都有相同的外观。如下面的示例所示:
<asp:Menu ID="Menu1"runat="server"StaticDisplayLevels="3"
StaticSubMenuIndent="10"Orientation="Vertical">
<LevelMenuItemStyles>
<asp:MenuItemStyle BackColor="LightSteelBlue"
ForeColor="Black"/>
<asp:MenuItemStyle BackColor="SkyBlue"
ForeColor="Black"/>
<asp:MenuItemStyle BackColor="LightSkyBlue"
ForeColor="Black"/>
</LevelMenuItemStyles>
<LevelSelectedStyles>
<asp:MenuItemStyle BackColor="Cyan"ForeColor="Blue"/>
<asp:MenuItemStyle BackColor="LightCyan"
ForeColor="Blue"/>
<asp:MenuItemStyle BackColor="PaleTurquoise"
ForeColor="Blue"/>
</LevelSelectedStyles>
</asp:Menu>
添加样式后的运行结果如图16-46所示。
图 16-45 Menu控件添加样式的示例
图 16-46 Menu控件添加样式的示例
与TreeView控件一样,同样可以自定义显示在Menu控件中的图像。通过设置表16-19所示的属性,可以为控件各部分指定自己的自定义图像。
16.9.5 Menu模板
若要完全控制用户界面((U),可以使用模板属性DynamicItemTemplate和StaticItemTemplate为Menu控件定义自己的自定义模板。其中:
1)DynamicItemTemplate属性包含动态菜单项的自定义呈现内容的模板。
2)StaticItemTemplate属性包含静态菜单项的自定义呈现内容的模板。
无论以声明的方式还是以编程的方式填充Menu,你都能够使用模板。从模板的角度来说,你总是在绑定到MenuItem对象。也就是说,模板总是必须从MenuItem.Text属性抓取菜单项的值。如下面的代码所示:
<asp:Menu ID="Menu1"runat="server">
<StaticItemTemplate>
<strong>
<%#Eval("text")%>
</strong>
</StaticItemTemplate>
</asp:Menu>
除了从MenuItem.Text属性抓取菜单项的值以外,还可以在模板里面绑定自己在后台定义的方法,以满足个性化需要。如下面在后台定义这样一个方法:
public string GetString(string text)
{
return"模板数据:"+text;
}
定义好GetString()方法之后,就可以在模板里引用了。如下面的代码所示:
<asp:Menu ID="Menu1"runat="server">
<StaticItemTemplate>
<small>
<%#GetString((((MnuItem)Container.DataItem).Text)%>
</small>
</StaticItemTemplate>
</asp:Menu>