如何在MVC5的Layout.cshtml中设计无需绑定ViewModel的全局下拉列表
解决方案
方案1:使用子操作渲染分部视图(推荐,.NET Framework MVC适用)
这个方案完全不需要布局页声明@model,布局页的下拉逻辑和当前页的模型完全解耦:
- 新增分部视图
Views/Shared/CategoryDropDown.cshtml,内容如下:
@model HomeViewModel @if (Model.AllCategory?.Count > 0) { <div class="col-lg-3"> <div class="hero__categories"> <div class="hero__categories__all"> <i class="fa fa-bars"></i> <span>All departments</span> </div> <ul> @foreach (var category in Model.AllCategory) { <li><a href="#">@category.Name</a></li> } </ul> </div> </div> }
- 删掉
_Layout.cshtml里原来写的分类渲染代码,替换为以下调用即可,注意替换为你实际的控制器名称:
@{ Html.RenderAction("CategoryDropDown", "Home"); } <!-- 也可以用更简洁的写法 @Html.Action("CategoryDropDown", "Home") -->
原理:
RenderAction会直接调用你写的CategoryDropDown控制器方法,方法自己组装好HomeViewModel返回分部视图,和当前页面的模型没有任何关联,不需要布局页声明模型。
方案2:使用全局ViewBag传递数据
如果不想用子操作,也可以通过全局过滤器给所有请求的ViewBag注入分类数据,布局页直接读取动态类型的ViewBag即可,不需要声明模型:
- 注册全局动作过滤器,在
Global.asax.cs的Application_Start方法里添加:
GlobalFilters.Filters.Add(new InjectCategoryAttribute());
- 实现过滤器类:
public class InjectCategoryAttribute : ActionFilterAttribute { public override void OnActionExecuted(ActionExecutedContext filterContext) { if (filterContext.Result is ViewResult viewResult) { viewResult.ViewBag.AllCategory = CategoriesService.Instance.allCategory(); } } }
- 布局页直接读取ViewBag即可:
@if (ViewBag.AllCategory?.Count > 0) { // 保留原来的分类渲染逻辑即可 }
适配ASP.NET Core场景
如果是ASP.NET Core项目,框架已经取消了Html.Action,推荐用视图组件(ViewComponent)实现:
- 创建视图组件类:
public class CategoryDropDownViewComponent : ViewComponent { public IViewComponentResult Invoke() { var model = new HomeViewModel { AllCategory = CategoriesService.Instance.allCategory() }; return View(model); } }
- 新增分部视图
Pages/Shared/Components/CategoryDropDown/Default.cshtml,内容和方案1的分部视图一致。 - 布局页调用:
@await Component.InvokeAsync("CategoryDropDown")
内容的提问来源于stack exchange,提问作者Md. Yeahia Khan
相关产品推荐
相关产品推荐

