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

时间:2021-04-30 22:33:10

I am trying to figure out the best way to create a navigation menu in ASP.NET MVC that can change based on the controller action from which it was built (it would also be different based on user permissions and such). The navigation menu is displayed in the Master Page, and all of our views are strongly typed.

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

I wanted to use OnActionExecuting in a base controller to populate the standard menu, then modify it accordingly within each controller action. This didn't seem to be an option though since the view model wouldn't be available until my action is called.

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

The only other thing I could come up with was to pre-populate the menu object in the base ViewModel constructor. Then I could add/remove as necessary in my controller actions. This didn't seem entirely appropriate though since I'd be instantiating links back to controller actions in the ViewModel constructor (since each Menu item can have a controller/action/id).

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

Any suggestions on the best way to do this? Particularly for navigation menus (or possibly treeviews) that change dramatically based on context.

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

2 个解决方案

#1


1  

I use a MasterModel for this kind of thing.

我使用MasterModel来做这种事情。

Given that the .aspx and its .master are separable (e.g. you can swap out the master when rendering the aspx; further the aspx does not inherit from the master, it only uses the master) in my mind, the view models should therefore be separate as well. (i.e. PageModel should not inherit from MasterModel.)

鉴于.aspx及其.master是可分的(例如,你可以在渲染aspx时换出master;进一步aspx不会从master继承,它只使用master),因此视图模型应该是也是分开的。 (即PageModel不应继承自MasterModel。)

The aspx might only care to receive, say, an IEnumerable<Foo>, why should it's view model be required to inherit from some common class?

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

So instead of:

所以代替:

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);
}

I use:

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);
}

You can then add this to your master page:

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

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

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

Now you don't have to do anything special to your page view models. They can care about only what they should be caring about.

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

Why is all this roundabout stuff interesting?

I wanted to use OnActionExecuting in a base controller to populate the standard menu, then modify it accordingly within each controller action. This didn't seem to be an option though since the view model wouldn't be available until my action is called.

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

The only other thing I could come up with was to pre-populate the menu object in the base ViewModel constructor. Then I could add/remove as necessary in my controller actions. This didn't seem entirely appropriate though since I'd be instantiating links back to controller actions in the ViewModel constructor (since each Menu item can have a controller/action/id).

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

Now it's real easy to do any of these, independent of your page view model:

现在很容易做到这些,独立于您的页面视图模型:

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);
        }
    }
}    

While not giving one specific solution, does this open up you options as far as how you can refactor the code so it smells better?

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

#2


0  

SubControllers in MvcContrib seem like an appropriate solution to your problem.

MvcContrib中的子控制器似乎是解决您问题的合适解决方案。

#1


1  

I use a MasterModel for this kind of thing.

我使用MasterModel来做这种事情。

Given that the .aspx and its .master are separable (e.g. you can swap out the master when rendering the aspx; further the aspx does not inherit from the master, it only uses the master) in my mind, the view models should therefore be separate as well. (i.e. PageModel should not inherit from MasterModel.)

鉴于.aspx及其.master是可分的(例如,你可以在渲染aspx时换出master;进一步aspx不会从master继承,它只使用master),因此视图模型应该是也是分开的。 (即PageModel不应继承自MasterModel。)

The aspx might only care to receive, say, an IEnumerable<Foo>, why should it's view model be required to inherit from some common class?

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

So instead of:

所以代替:

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);
}

I use:

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);
}

You can then add this to your master page:

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

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

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

Now you don't have to do anything special to your page view models. They can care about only what they should be caring about.

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

Why is all this roundabout stuff interesting?

I wanted to use OnActionExecuting in a base controller to populate the standard menu, then modify it accordingly within each controller action. This didn't seem to be an option though since the view model wouldn't be available until my action is called.

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

The only other thing I could come up with was to pre-populate the menu object in the base ViewModel constructor. Then I could add/remove as necessary in my controller actions. This didn't seem entirely appropriate though since I'd be instantiating links back to controller actions in the ViewModel constructor (since each Menu item can have a controller/action/id).

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

Now it's real easy to do any of these, independent of your page view model:

现在很容易做到这些,独立于您的页面视图模型:

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);
        }
    }
}    

While not giving one specific solution, does this open up you options as far as how you can refactor the code so it smells better?

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

#2


0  

SubControllers in MvcContrib seem like an appropriate solution to your problem.

MvcContrib中的子控制器似乎是解决您问题的合适解决方案。