在MVC2中实现多级的Treeview

多级的层次结构的表现是一个令人头疼的问题,尤其是涉及到数据库的读取和更改。好在目前已经有很多工具可以辅助解决这个难题。Web From 中的 Treeview无疑是很强大的工具,不过在MVC2中用不上了,得重新想办法才是。

有个叫Mick Bosch的人写了一篇这样的技术文章《Hierarchical Treeview with ASP.NET MVC & jQuery》,利用MVC用户控件来做递归,实现数据库的读取,再利用jQuery插件jQuery.Treeview来改善表现。正式利用该文的思路,我做了一份MVC2的实现品。根据下面的评论,实现的方式稍微有点不同。

数据库结构很简单,使用Entity Framework 4 建立一个只有3个属性的实体就可以了:Category实体

通过实体生成数据库,顺便添加一些数据:


Insert into Categories ([Name],[ParentId]) values ('鞋子',null);
Insert into Categories ([Name],[ParentId]) values ('帽子',null);
Insert into Categories ([Name],[ParentId]) values ('衣服',null);
Insert into Categories ([Name],[ParentId]) values ('男鞋',1);
Insert into Categories ([Name],[ParentId]) values ('女鞋',1);
Insert into Categories ([Name],[ParentId]) values ('男装',3);
Insert into Categories ([Name],[ParentId]) values ('女装',3);
Insert into Categories ([Name],[ParentId]) values ('凉鞋',5);
Insert into Categories ([Name],[ParentId]) values ('长筒',5);

新建一个MVC用户控件,控件的代码修改如下:


<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<TreeviewDemo.Category>>" %>
<%
foreach(var category in Model){
%>
<li>
<%:category.Name %>
<% var children = (ViewData["categories"] as IEnumerable<TreeviewDemo.Category>).Where(c => c.ParentId == category.ID);
if (children.Count() > 0)
{%>
<ul>
<% Html.RenderPartial("ItemControl", children); %>
</ul>
<%}%>
</li>
<%} %>

控制器这样写:


public ActionResult TreeView()
{
using (Model1Container db = new Model1Container())
{
var categories = db.Categories.ToList();
ViewData["categories"] = categories;
return View(categories.Where(c=>c.ParentId == null));
}
}

视图中调用MVC用户控件:

<%Html.RenderPartial("ItemControl", Model); %>

最终效果:

MVC Treeview 效果

提供项目源码下载。数据库很简单,就不提供了。

asp.net mvc 2:Model Validation

asp.net mvc 2中集成了Model验证机制。只需要少量代码,就能在网站必要的地方验证数据的正确有效性。ScottGu’s Blog上有一篇同名文章,APS.NET MVC 2 : Model Validation详述了Model验证的技术,包括如何在使用Entity Framework的情况下使用内置的验证机制。

在Model中,默认没有引用命名空间

using System.Web.Mvc;

结果就是当你要是用[Bind(Exclude="ID")]这个属性的时候,找不到命名空间。这个细节在ScottGu的文章中没有说明。第二个问题是,当我自定义一个用来验证的Attribute时,在使用Post验证时表现正常,在使用Ajax验证时就无法显示验证结果了。这一点也百思不得其解。

在使用DropDownListFor验证譬如文章归属哪个类别时,有一个属性非常重要。例如:


[Required(ErrorMessage = "Please select a Whatever!")]
[DisplayFormat(ConvertEmptyStringToNull = false)]
public class Whatever {
public int DropDownListReference { get; set; }
}

其中的[DisplayFormat(ConvertEmptyStringToNull=false)]属性,可以触发当下拉列表没有选中项时的Require检验。

当你在修改模式时,或者当你Create触发了检验失败时,务必记得要再初始化一次下拉列表的数据源。DropDownList只会保持一个单一的值,不会保持一个IEnumerable的对象。即使你复制给Model的一个属性或者赋值给ViewData也不行。感觉很奇怪。如果不这样做,你就会收到错误信息。当然,你不用担心状态保持问题,即使你的IEnumerable对象是新创建出来的,它也会如实的保持你的选中项。

MVC中有很多隐藏技巧,如果可能,真的想好好的看一下书。同学们,有好书推荐一下吗?