网站导航(Menu 控件)

       Menu 是另一个支持层次化数据的富控件。它可以绑定到数据源(声明性的)或编程使用 MenuItem 对象来填充。

       MenuItem 类不像 TreeNode 类那样丰富,它不支持复选框,也不能通过编程设置它们的 折叠/展开 状态。不过,它们也有很多相似的属性,包括那些用于设置图片、确定条目是否可选以及指定目标链接的属性。

MenuItem 的属性:

Text 菜单中显示的文字
TooTip 鼠标停留菜单项时的提示文字
Value 保存不显示的额外数据(比如某些程序需要用到的 ID)
NavigateUrl 如果设置了值,单击节点会前进至此 Url。否则,需要响应 Menu.MenuItemClick事件确定要执行的活动
Target 它设置了链接的目标窗口或框架。Menu 自身也暴露了 Target 属性设置所有的 MenuItem 实例的默认目标
Selectable 如果为 false,菜单项不可选。通常只在菜单项有一些可选的子菜单项时,才设为 false
ImageUrl 菜单项旁边的图片
PopOutImageUrl 菜单项包含子项时现在在菜单项旁的图片,默认是一个小的实心箭头
SeparatorImageUrl 菜单项下面显示的图片,用于分隔菜单项

       和遍历 TreeView 结构的方式相同,Menu 控件仅做很小的改动,几乎就可以重用先前 TreeView 的代码:

protected void Page_Load(object sender, EventArgs e)
{
    if (!Page.IsPostBack)
    {
        DataSet ds = GetProductsAndCategories();
 
        foreach (DataRow row in ds.Tables["Categories"].Rows)
        {
            MenuItem itemCategory = new MenuItem(
                row["CategoryName"].ToString(),
                row["CategoryID"].ToString());
            Menu1.Items.Add(itemCategory);
 
            DataRow[] childRows = row.GetChildRows(ds.Relations["CatProds"]);
            foreach (DataRow childRow in childRows)
            {
                MenuItem itemProduct = new MenuItem(
                    childRow["ProductName"].ToString(),
                    childRow["ProductID"].ToString());
                itemCategory.ChildItems.Add(itemProduct);
            }
        }
    }
 
}
 
protected void Menu1_MenuItemClick(object sender, MenuEventArgs e)
{
    if (Menu1.SelectedItem.Depth == 0)
    {
        lblInfo.Text = "You selected Category ID: ";
    }
    else if (Menu1.SelectedItem.Depth == 1)
    {
        lblInfo.Text = "You selected Product ID: ";
    }
    lblInfo.Text += Menu1.SelectedItem.Value;
}

image

       虽然 Menu 和 TreeView 的呈现方式非常不同,但它们暴露了非常相似的编程模型。它们还有相似的基于样式的格式化模型。

       不过,它们还是有一些显著的差异:

  • Menu 每次显示一个子菜单;TreeView 可以一次展开任意多个节点。
  • Menu 在页面里显示第一层的链接;TreeView 显示页面上内联的所有项。
  • Menu 不支持按需填充及客户端回调;TreeView 支持。
  • Menu 支持模板;TreeView 不支持。
  • Menu 支持水平和垂直布局;TreeView 只支持垂直布局。

Menu 样式

       Menu 控件提供了数量惊人的样式。和 TreeView 一样,Menu 从 Style 基类派生了自定义类(实际上,它派生了 MenuStyle类 和 MenuItemStyle类)。

       这些样式增加了间距属性 ItemSpacing、HorizontalPadding、VerticalPadding;但不能通过样式设置菜单项的图片,因为它没有 ImageUrl 属性。

       Menu 在很大程度上和 TreeView 相似,它支持为位于不同层级的菜单定义不同的菜单样式。不过,一个主要的区别是 Menu 控件鼓励你区分静态项(菜单刚创建时就显示在页面上的第一层条目)和动态项(鼠标移动到菜单某个区域时被添加的弹出的菜单项)。

       对于大多网站,这两个元素具有明显的区别。为了支持这些,Menu 定义了两组平行样式,如下:

StaticMenuStyle DynamicMenuStyle 设置总体“盒子”的外观,所有的菜单项出现在这里
StaticMenuItemStyle DynamicMenuItemStyle 设置单个菜单项的外观
StaticSelectedStyle DynamicSelectedStyle 设置选择项的外观,选择项指的是前一个被单击的项(且触发上一次回发的项)
StaticHoverStyle DynamicHoverStyle 设置鼠标停留时项的外观

       还可以设置层级特定的样式,这样每层的菜单和子菜单都不一样。可通过 3 个集合来设置:LevelMenuItemStylesLevelSubMenuStylesLevelSelectedStyles。这些集合分别作用于普通的菜单项,包含其他菜单项的菜单项以及被选择的菜单项。

       可能你会觉得不必做那么多工作区分动态样式和静态样式。但考虑到 Menu 控件另一个非同寻常的功能,这一模型就很有意义:它允许设置静态层次的数目。在默认情况下,只有一个静态层,其他所有的项只有把鼠标停留在相应的父菜单上时才会弹出。不过,如果设置了 Menu.StaticDisplayLevels 属性,就可改变这一切。例如,如果设为 2 ,前两层菜单将使用静态样式呈现在页面上,还可用 StaticSubMenuIdent 属性控制每层的缩进。

       在调整特定呈现方面,Menu 控件会暴露更多顶层属性。例如,可以设置菜单消失前的延时(DisappearAfter)、展开图标和分隔符的默认图片和滚动行为等。具体可以参考 Visual Studio 帮助文档获取属性的完整列表。

模板

       通过 StaticMenuItemTemplate 和 DynamicMenuItemTemplate 属性,Menu 控件也能够支持模板。这些模板能让你完全控制每个菜单项要呈现的 HTML。

       有趣的是,无论是以声明的方式还是编程的方式填充 Menu 类,都能够使用模板。从模板的角度来看,你总是绑定到 MenuItem 对象。也就是说,模板总是必须从 MenuItem.Text 属性抓取菜单项的值,如下所示:

<asp:Menu ID="Menu1" runat="server" >
    <StaticItemTemplate>
        <%# Eval("Text") %>
    </StaticItemTemplate>
</asp:Menu>

       你想使用 Menu 模板功能的另一个原因可能是显示来自数据对象的多个信息。例如,你可能希望同时在带单项上显示 SiteMapNode 的标题和描述。遗憾的是,这不可能。问题在于 Menu 直接绑定到 MenuItem 对象。MenuItem 确实暴露了 DataItem 属性,但当它被添加到菜单时,DataItem 不再引用当初用来填充它的 SiteMapNode。

       如果确实非常希望这样显示,可以在类里写一个基于 URL 查找 SiteMapNode 的自定义方法。这本来不是必要的工作,不过它确实能够使菜单项模板获得描述信息:

private string matchingDescription;
 
protected string GetDescriptionFromTitle(string title)
{
    SiteMapNode node = SiteMap.RootNode;
    SearchNodes(node, title);
    return matchingDescription;
}
 
private void SearchNodes(SiteMapNode node, string title)
{
    if (node.Title == title)
    {
        matchingDescription = node.Description;
        return;
    }
    else
    {
        foreach (SiteMapNode child in node.ChildNodes)
        {
            SearchNodes(child, title);
        }
    }
}
<asp:Menu ID="Menu1" runat="server" DataSourceID="SiteMapDataSource1" StaticDisplayLevels="2">
    <StaticItemTemplate>
        <%# Eval("Text") %><br />
        <small>
            <%# GetDescriptionFromTitle(((MenuItem)Container.DataItem).Text)%>
        </small>
    </StaticItemTemplate>
</asp:Menu>

       你还可以为 Menu 控件声明一个数据绑定,它指定绑定对象中用于 MenuItem 文本的那个属性。不过,它只接受一个字段。尽管如此,它还是可以很方便的把标题显示为文本而把描述作为提示文本:

<asp:Menu ID="Menu1" runat="server" DataSourceID="SiteMapDataSource1" StaticDisplayLevels="2">
    <DataBindings>
        <asp:MenuItemBinding DataMember="SiteMapNode" TextField="Title" ToolTipField="Description" />
    </DataBindings>
</asp:Menu>

注解:

       在 ASP.NET 4 里,Menu 控件不再使用 HTML 表格来呈现自己,而是把自己呈现为一组无序的条目(使用 <ul> 和 <li> 元素),并通过样式规则来创建正确的格式。

       Menu 控件在页面的顶部以样式块的形式呈现其所有样式,而不与呈现的 HTML 内联。但是,可以把 Menu.IncludeStyleBlock 属性设为 fasle 以告知 Menu 不要呈现其样式,这样能让你完全控制 Menu 样式,甚至可以采用外部样式表的样式。

       (如果你需要一个起点,可以先把该属性设为 true 运行页面,复制呈现的 HTML 样式代码,根据需要做调整)

原文地址:https://www.cnblogs.com/SkySoot/p/2768185.html