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所示。

figure_0585_0469

figure_0586_0470

下面的示例演示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所示。

figure_0586_0471

图 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所示。

figure_0587_0472

图 16-42 Orientation="Horizontal"的运行结果

figure_0587_0473

图 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所示。

figure_0589_0474

图 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所示。

figure_0589_0475

对于这些样式,可以使用内联样式进行设置,也可以使用一个单独的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所示。

figure_0590_0476

集合的第一个样式对应于菜单树第一个深度级别的菜单项的样式,集合的第二个样式对应于菜单树第二个深度级别的菜单项的样式,依此类推。此集合最常用于生成目录风格的导航菜单;在这种导航菜单中,某个深度的菜单项不管是否具有子菜单,都有相同的外观。如下面的示例所示:


<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所示。

figure_0590_0477

图 16-45 Menu控件添加样式的示例

figure_0590_0478

图 16-46 Menu控件添加样式的示例

与TreeView控件一样,同样可以自定义显示在Menu控件中的图像。通过设置表16-19所示的属性,可以为控件各部分指定自己的自定义图像。

figure_0591_0479

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>