使用强类型视图的上下文敏感的ASP.NET MVC导航菜单?

时间:2009-07-26 03:15:31

标签: asp.net-mvc

我试图找出在ASP.NET MVC中创建导航菜单的最佳方法,该菜单可以根据构建它的控制器操作进行更改(根据用户权限等也会有所不同)。导航菜单显示在母版页中,我们的所有视图都是强类型的。

我想在基本控制器中使用OnActionExecuting来填充标准菜单,然后在每个控制器操作中相应地修改它。这似乎不是一个选项,因为在调用我的动作之前视图模型不可用。

我能想到的另一件事是在基础ViewModel构造函数中预先填充菜单对象。然后我可以根据需要添加/删除我的控制器操作。这似乎并不完全合适,因为我将实例化链接回ViewModel构造函数中的控制器动作(因为每个Menu项都可以有一个控制器/动作/ id)。

有关最佳方法的任何建议吗?特别是对于根据上下文发生显着变化的导航菜单(或可能是树视图)。

2 个答案:

答案 0 :(得分:1)

我使用MasterModel来做这种事情。

鉴于.aspx及其.master是可分离的(例如,在渲染aspx时你可以换掉master;而且aspx不会从master继承继承,只有使用主人)在我看来,视图模型也应该是分开的。 (即PageModel不应继承自MasterModel。)

aspx可能只关心接收一个IEnumerable< Foo>,为什么它的视图模型需要继承一些常见的类?

所以而不是:

public class MasterModel
{
    public string Title { get; set; }
    public MenuModel Menu { get; set; }
}

public class PageViewModel
    : MasterModel
{
    public IEnumerable<Foo> TheOnlyThingInTheWorldMyPageReallyCaresAbout { get; set; }
}

public ActionResult TheAction()
{
    var items = GetItems();
    var model = new PageViewModel {
        TheOnlyThingInTheWorldMyPageReallyCaresAbout = items
    };
    return view("Viewname", model);
}

我用:

public class BaseController
    : Controller
{
    protected MasterModel MasterModel
    {
        get { return ViewData["MasterModel"] as MasterModel; }
        set { ViewData["MasterModel"] = value; }
    }
}

public class MasterModel
{
    public MasterModel()
    {
        // Sensible defaults
    }

    public string Title { get; set; }
    public MenuModel Menu { get; set; }
}

public ActionResult TheAction()
{
    var items = GetItems();

    // Prepare the MasterModel as part of the controller's "select and prepare the view" responsibility.
    // Common cases can be factored into ActionFilter attributes or to a base Controller class.
    MasterModel = new MasterModel();
    return view("Viewname", items);
}

然后,您可以将其添加到母版页:

<%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %>

<script runat="server">
    public MasterModel Model { get { return ViewData["MasterModel"] as MasterModel; } }
</script>

现在,您无需对页面视图模型执行任何特殊操作。他们只关心他们应该关心的事情。

为什么所有这些迂回的东西都很有趣?

  

我想在基本控制器中使用OnActionExecuting来填充标准菜单,然后在每个控制器操作中相应地修改它。这似乎不是一个选项,因为在调用我的动作之前视图模型不可用。

     

我能想到的另一件事是在基础ViewModel构造函数中预先填充菜单对象。然后我可以根据需要添加/删除我的控制器操作。这似乎并不完全合适,因为我将实例化链接回ViewModel构造函数中的控制器动作(因为每个Menu项都可以有一个控制器/动作/ id)。

现在很容易做到这些,与您的页面浏览模型无关:

MasterModel = this.DefaultMasterModel();

// -- or --

MasterModel = this.DefaultMasterModel();
this.AlterMenu(MasterModel.Menu);

// -- or --

MasterModel = new MasterModel();
MasterModel.Menu = this.SpecialReplacementMenu();

// -- or --

MasterModel = new NestedMasterModel();

// -- or -- 

public class FlyingPigAttribute : ActionFilterAttribute
{
    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        var viewResult = filterContext.Result as ViewResult;

        if (viewResult!=null)
        {
            var masterModel = viewResult.ViewData["MasterModel"] as MasterModel;
            MakePigsFly(masterModel);
        }
    }
}    

虽然没有提供一个特定的解决方案,但是这会为您提供选项,以便您可以重构代码以使它更好闻?

答案 1 :(得分:0)

SubControllers中的{p> MvcContrib似乎是您问题的合适解决方案。