我对使用 WPF/Silverlight 的 MVVM 相当满意,但这是我第一次尝试 MVC Web 应用程序……仅供参考。
我创建了一个名为 TestSitesController 的控制器,它是从实体框架模型中的“Site”模型类(生成读/写操作和视图的模板)自动生成的。 我唯一修改的是在 3 个地方,某些方法有一个默认参数 Guid id = null。 我刚刚摆脱了“= null”,一切正常。 这是我更改的示例
public ActionResult Delete(Guid id = null)
{
//....
}
这已更改为
public ActionResult Delete(Guid id)
{
//....
}
站点模型没有什么特别的 SiteId、Abbreviation 和 DisplayName…对于这个问题,尽量保持简单。好的,我运行网站并转到 htpp://.../TestSites/ ,一切正常。
我注意到我的所有视图(创建、删除、详细信息和编辑)都使用 @model MVCWeb.MyEntities.Site,目前我对此完全满意;但在 Index.cshtml 视图中我注意到它使用的是
@model IEnumerable<MVCWeb.MyEntities.Site>
这对于生成的模板来说效果很好,但我想使用“复合视图模型”,也许这就是我试图混合我的 MVVM 知识的地方,但如果可能的话,我会坚持下去。 在我看来,复合视图模型只是一个特定于由 1 个或多个实体模型以及 SelectedSiteId 等附加属性组成的视图的模型。
所以我创建了一个非常简单的 ViewModel,名为 TestSitesViewModel
public class TestSitesViewModel
{
//Eventually this will be added to a base ViewModel to get rid
//of the ViewBag dependencies
[Display(Name = "Web Page Title")]
public string WebPageTitle;
public IEnumerable<MVCWeb.MyEntities.Site> Sites;
//Other Entities, IEnumberables, or properties go here
//Not important for this example
}
然后在我的控制器中我添加了一个名为 IndexWithViewModel 的操作方法
public ActionResult IndexWithViewModel()
{
var vm = new TestSitesViewModel();
vm.WebPageTitle = "Sites With Composite View Model";
vm.Sites = db.Sites.ToList();
return View(vm);
}
然后,我复制了 Index.cshtml 并将其命名为 IndexWithModel.cshtml 以匹配我的新 ActionResult 方法名称。 我改变了
的顶行@model IEnumerable<MVCWeb.MyEntities.Site>
至
@model MVCWeb.Models.TestSitesViewModel
我在表格部分之前添加了此内容以测试 DisplayNameFor 和 DisplayFor
@Html.DisplayNameFor(model => model.WebPageTitle)
@Html.DisplayFor(model => model.WebPageTitle)
<br />
改变了
@foreach (var item in Model) {
至
@foreach (var item in Model.Sites) {
并暂时注释掉包含所有表头的 tr 部分。 一切工作正常,除了 @Html.DisplayNameFor 显示“WebPageTitle”的变量名称,而不是使用“网页标题”,如
中数据注释的 Display 属性所示[Display(Name = "Web Page Title")]
public string WebPageTitle;
另外,如果我在包含表头信息的 tr 部分中进行评论。 我一生都无法弄清楚要放入什么我已经尝试过 model.Sites.Abbreviation、model.Sites[0].Abbreviation 和各种其他组合,但出现错误。
<tr>
<th>
@Html.DisplayNameFor(model => model.Abbreviation)
</th>
<th>
@Html.DisplayNameFor(model => model.DisplayName)
</th>
<th></th>
</tr>
那么我应该在那里使用什么呢? 我不太确定为什么 model.Sites.Abbreviation 不起作用,因为 Sites 与原始 Index.cshtml 中用作模型的类型完全相同
经过几个小时的折腾,我终于弄清楚了。
第一个问题,为了在数据注释中使用 Display 属性,必须添加 get 和 set 访问器方法,即使它们只是默认的方法。我假设这是因为它们必须仅适用于属性,而不适用于类的公共数据成员。
这是获取的代码:
@Html.DisplayNameFor(model => model.WebPageTitle)
正确工作
public class TestSitesViewModel
{
// Eventually this will be added to a base ViewModel to get rid of
// the ViewBag dependencies
[Display(Name = "Web Page Title")]
public string WebPageTitle { get; set; }
// PUBLIC DATA MEMBER WON'T WORK
// IT NEEDS TO BE PROPERTY AS DECLARED ABOVE
// public string WebPageTitle;
public IEnumerable<MVCWeb.MyEntities.Site> Sites;
// Other entities, IEnumerables, or properties go here
// Not important for this example
}
问题的第二部分是访问 IEnumerable
变量中的元数据。我声明了名为
headerMetadata
的临时变量并使用它,而不是尝试通过
IEnumerable
:访问属性
@{var headerMetadata = Model.Sites.FirstOrDefault();}
<tr>
<th>
@Html.DisplayNameFor(model => headerMetadata.Abbreviation)
</th>
<th>
@Html.DisplayNameFor(model => headerMetadata.DisplayName)
</th>
...
这确实引出了一个问题:headerMetadata
变量何时超出范围?它适用于整个视图还是仅限于 html 表标签?我想这是另一个约会的另一个问题。
不要调用
@Html.DisplayNameFor(model => model.WebPageTitle)
,而是尝试使用
@Html.DisplayFor(model => model.WebPageTitle)
。您似乎正在 IndexWithViewModel 控制器中设置 WebPageTitle 值,该值应该显示在您的视图中。查看 ASP.NET MVC 的共享
模板。您可以在 /Views/Shared/EditorTemplates/Site.cshtml
和
/Views/Shared/DisplayTemplates/Site.cshtml
为 DisplayFor/EditorFor 帮助程序设置自定义模板或部分。当使用 DisplayFor 和 EditorFor 渲染 Site 对象时,ASP.NET MVC 将自动选取这些模板。祝你好运!