【问题标题】:Hierarchical menu in view based on parent child class基于父子类的视图中的分层菜单
【发布时间】:2010-09-03 23:41:55
【问题描述】:

我有一个 ViewModel:

public class Page
{
    public int Id { get; set; }
    public Page Parent { get; set; }
    public string Name { get; set; }
    public string Title { get; set; }
}

我正在将该模型传递给视图,并且我需要基于该模型创建一个分层菜单:

<ul>
    <li>Page
        <ul>
            <li>Sub Page</li>
        </ul>
    </li>

尽管我不遗余力,但我无法弄清楚我将如何在视图中执行此递归循环。

我可以在我的控制器中创建标记,但这不利于测试。

有什么想法吗?

【问题讨论】:

  • 只是我还是你的递归走错了路?每个节点不应该有一个子节点列表,而不是每个节点都有对父节点的引用吗?
  • 好点,它会让事情变得更容易,但是,它并不能完全回答我的问题。
  • .... 在使用 ViewModel 和生成分层菜单时,我仍然会遇到同样的问题。

标签: c# html asp.net-mvc-2


【解决方案1】:

有很多方法可以显示动态生成的菜单,我将发布我的做法,我有两个类 Menu 和 MenuItem(Menu 只是 MenuItems 列表的包装器,它们是实际的>“真实”链接) 然后我有一个 NavigationViewModel 来包装所有内容。

public class MenuItem
{
    public int MenuID { get; set; }
    public int ID { get; set; }
    public String Label { get; set; }
    public String Link { get; set; }
    public Boolean Show { get; set; }

    public MenuItem(int menuId, int id, string label, string link, Boolean show)
    {
        this.MenuID = menuId;
        this.ID = id;
        this.Label = label;
        this.Link = link;
        this.Show = show;
    }
}

//

public class Menu
{
    public int ID { get; set; }
    public string Name { get; set; }
    public List<MenuItem> MenuItems { get; set; }

    public Menu(int id, string name)
    {
        this.ID = id;
        this.Name = name;
        this.MenuItems = new List<MenuItem>();
    }

}

// 这是包裹一切的 NavigationModel。

public class NavigationModel
{
    public int currentMenuID { get; set; }   
    // used to determine the current displayed Menu to add
    // the "current" class to it. (to be set in the controller)

    public int currentMenuItemID { get; set; } 
    // used to determine the current displayed MenuItem to add
    // the "current" class to it. (to be set in the controller)

    public List<Menu> Menus { get; set; }


    public NavigationModel()
    {
        this.Menus = new List<Menu>();
        // Set Default Menu ( Menu 1 )
        this.currentMenuID = 1;
        // Set Default Menau Item ( None )
        this.currentMenuItemID = 0;
    }
}

// 这是构建 NavigationModel 的方法示例。

private static NavigationModel BuildNavigationMenu(User currentUser, string rootURL)
    {
        string loginURL = rootURL + "Account/LogOn";

        // Main Menu
        Menu MainMenu = new Menu(1, "Home");
        MainMenu.MenuItems.Add(new MenuItem(1, 1, "Welcome", rootURL, true));
        MainMenu.MenuItems.Add(new MenuItem(1, 2, "How It Works", rootURL + "Home/HowDoesItWork", true));

        // Work Menu
        Menu WorkMenu = new Menu(2, "Work");
        WorkMenu.MenuItems.Add(new MenuItem(2, 1, "Profile", rootURL + "User/Profile/" + currentUser.ID , true));
        WorkMenu.MenuItems.Add(new MenuItem(2, 2, "Customers", "#", true));

        // Add Menus To Navigation Model
        NavigationModel navigationMenu = new NavigationModel();
        navigationMenu.Menus.Add(MainMenu);
        navigationMenu.Menus.Add(HireMenu);

        return navigationMenu;
}

// 我有一个 NavigationHelper 负责使用以下两种方法输出 HTML。

注意:(这是一个简化版本,因为在我的实现中,我使用 Menu 和 MenuItem 的 id 来定位正在显示的当前菜单并向其添加“当前”CSS 类”

public static string DisplayMenu(this HtmlHelper helper, NavigationModel navigationMenu)
{ 
    public static string DisplayMenu(this HtmlHelper helper, NavigationModel navigationMenu)
    {            
        String result = "<ul id='main-nav'>\n";
        foreach(Menu menu in navigationMenu.Menus)
        {
            result +=     "<li>\n";
            result +=         string.Format("<a href='#'> {0} </a>\n",helper.AttributeEncode(menu.Name));
            result +=         "<ul>\n";
            foreach(MenuItem item in menu.MenuItems)
            {
                result += NavigationHelper.ConvertToItem(helper, item);
            }
            result +=         "</ul>\n";
            result +=     "</li>\n";
        }
        result +=     "</ul>\n";

        return result;
    }

    private static string ConvertToItem(this HtmlHelper helper,MenuItem item)
    {
        if (item.Show)
        {
            return string.Format("<li><a href='{0}'>{1}</a></li>\n", helper.AttributeEncode(item.Link), helper.AttributeEncode(item.Label));
        }
        else { return ""; }   
    }
}

最后我在我的 Master 中有一个如下调用来显示菜单。

<!-- Navigation -->
<%= Html.DisplayMenu(Model.NavigationMenu) %>

注意:我有一个强类型 MasterPage,其类型 (BaseViewModel) 包含 NavigationModel 类型的属性 NavigationMenu

将一切连接在一起。

在我的示例中,我必须在每个操作方法中为我的视图提供一个 ViewModel(继承我的 BaseViewModel),然后构建菜单(使用完成大部分工作的生成器方法,因此我不必重新在每个操作方法中键入它)。如果您不使用 BaseViewModel,那么您将需要找到另一种方法来构建导航模型,然后将导航模型传递给您的 MasterPage。

对我来说,我发现拥有一个强类型的 MasterPage 可以让事情变得更轻松、更清晰。 King Wilder 在他的GolfTracker Series 中有一个很好的实现方式。

【讨论】:

  • 感谢广泛的示例,但是,我不想要控制器或任何其他代码文件中的任何标记,我想要 html 标记中的某种递归循环。
猜你喜欢
  • 1970-01-01
  • 2019-01-22
  • 1970-01-01
  • 2011-09-17
  • 2012-03-22
  • 1970-01-01
  • 2017-05-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多