关于razor:ASP.NET MVC 3中ViewBag.Title的替代

Alternative to ViewBag.Title in ASP.NET MVC 3

默认情况下,ASP.NET MVC 3的新项目模板将以下内容添加到默认布局(剃刀中的母版页):

1
@ViewBag.Title

然后,该视图必须包含以下内容以分配页面标题,例如:

1
2
3
@{
     ViewBag.Title ="Log On";
}

也许这只是我自己的喜好,但我发现使用ViewBag来保持标题有点错误(我想的是太多的魔术弦味)。 所以我的问题是:对于使用ASP.NET MVC 3和剃须刀(使用动态属性包)的人们,这是推荐的最佳实践,还是您选择了更强类型的内容(可能涉及自定义基类?)


我认为asp.net MVC 3附带的默认标题处理功能没有什么不好的,可以这样做。

我亲自(下面写的)来处理标题,我不认可以下代码,也不是说它比默认功能更好,它只是一个首选项。

1
    @RenderSection("Title");

视图

1
2
3
4
@section Title
{
    write title
}

我可能建议改善默认功能的一件事

1
2
3
4
@{
    string pageTitle = @ViewBag.Title ??"Title Not Set";
}
@pageTitle

因此,每当您忘记将其添加到视图包中时,页面就会显示title = Title Not Set

也可以创建一个基类,然后使所有控制器从该基类继承。但是我认为它为title带来了极大的痛苦。


标题的ViewBag非常好(我什至会说这是拥有ViewBag的目的)-动态并不是绝对的邪恶。"标题"是众所周知的,不可能更改甚至在视图模板中预定义。我个人使用以下标题:

1
@(ViewBag.Title == null ? string.Empty : ViewBag.Title +" |")Site Name

如果您担心误输入ViewBag.Title,可以通过创建自定义WebViewPage将其设置为强类型,但是由于该WebViewPage的多个实例,您仍然必须在该强类型属性中使用ViewBag或也许HttpContext.Items在渲染IIRC期间创建的。

我建议坚持使用ViewBag,创建自己的WebViewPage,因为这似乎有点过头了-我认为即使您已经有自定义的WebViewPage,即使在其上创建单个属性也只是毫无用处的复杂性-那是由于人这通常会使工程过度。


我也完全不使用ViewBag。

在_Layout.shtml的顶部...

1
@model <YourWebAppNameSpace>.Models.Base.EveryPageViewModel

在_Layout.shtml中...

1
@Model.Title

在您的模型中...

1
2
3
4
5
6
/// <summary>
/// Index View Model
/// </summary>
public class IndexViewViewModel : EveryPageViewModel {

}

在EveryPageViewModel中

1
2
3
4
5
6
7
8
9
10
11
12
13
/// <summary>
/// Every Page View Model
/// </summary>
public abstract class EveryPageViewModel {
    /// <summary>
    /// Title
    /// </summary>
    public string Title { get; set; }
    /// <summary>
    /// Sub Title
    /// </summary>
    public string SubTitle { get; set; }
}

在您的控制器动作中

1
2
3
4
5
6
7
8
9
    /// <summary>
    /// Index
    /// </summary>
    /// <returns></returns>
    public ActionResult Index() {
        var model = new IndexViewViewModel();
        model.Title ="Home";
        return View(model);
    }

我喜欢创建PageTitle ActionFilter属性,而不是编辑单个ViewBags

用法:保持视图不变

1
@ViewBag.Title

对于控制器范围内的页面标题:

1
2
3
4
[PageTitle("Manage Users")]
public class UsersController : Controller {
    //actions here
}

对于个人观点:

1
2
3
4
5
6
public class UsersController : Controller {
    [PageTitle("Edit Users")]
    public ActionResult Edit(int id) {
          //method here
    }
}

属性代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class PageTitleAttribute : ActionFilterAttribute
{
    private readonly string _pageTitle;
    public PageTitleAttribute(string pageTitle)
    {
        _pageTitle = pageTitle;
    }

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        base.OnActionExecuted(filterContext);
        var result = filterContext.Result as ViewResult;
        if (result != null)
        {
            result.ViewBag.Title = _pageTitle;
        }
    }
}

易于管理,魅力十足。


使用ViewBag.Title =" My Title"没有什么错;

您要做的就是使用动态属性。

问题实际上是应该在哪里"声明"信息。

即,出于手头的目的,它在哪里最容易访问。

如果以每页为基础,那是正确的位置。

但是,如果页面标题可以从Model派生,则应该这样做。

在这种情况下,我可能会为您使用的ViewModel使用基类,并在其中创建PageTitle属性,该属性包含从Model属性中派生页面标题的逻辑。

所以:

1
Model.PageTitle

总而言之,只要您了解它们的本质和作用,参加课程的马就不必害怕使用动态属性。


我们更喜欢强标题设置。.BaseController类中的一些示例。 (页面定义了封装视图模态)

1
2
3
4
5
6
7
8
9
10
protected override ViewResult View(string viewName, string masterName, object model)
{
    if (model is Page)
    {
        ViewBag.Title = ((Page)model).Title;
        //HACK: SEO
        //TODO: SEO
    }
    return base.View(viewName, masterName, model);
}

我想说的是,只要是您要设置的标题,就可以使用ViewBag。好吧,也许不仅如此-最多2-3个属性。

但是,如果您开始发现您在每个控制器操作中都设置了越来越多的(通用)属性,那么我将使用强类型的" ViewModelBase类"。但这只是我。


就我个人而言,我认为这种情况是ViewBag的可接受用法。它仅限于"知名"属性,将来可能不会引起任何问题。最后,这一切都是要务实并找到一种最快的方法。在我看来,如果有一个需要设置标题的基类,那么太多的代码就不值得类型安全。

祝好运!