[转载]利用Attribute实现的 MVC动态表单

[转载]利用Attribute实现的 MVC动态表单 – Dozer .NET 技术博客 – 博客园.

一、何谓动态表单

最近再做一个项目,运营只是初步确定了功能,再加上项目比较复杂,所以我不好确定数据库结构

我随时有可能在某个表中加一个属性…

这时候,如果我有2个页面,分别是 Create 和 Edit,那我就需要对这两个页面进行修改~

如果是更多的页面怎么办?

那为何不根据Model,自动生成表单呢?

网上查到一篇文章,是利用外部XML文件,好吧,我承认在一定程度上能方便一点,但写XML和写Html有什么本质区别吗?

二、大家想要怎么样的动态表单?

最懒的方法,只要我数据库和Model有变动,别的地方我不用动一行代码,页面就会自动生成最新的表单!

好理想的状态~ 其实我就是为了这个目标而做的。

虽然上面的方法最方便,但其实并不是如此,因为大部分情况下,表单中不会包含Model所有的属性(比如ID,不可能有吧?~)

另外,Create和Edit的时候,表单也是不同的。

所以,个人感觉,一个比较周全的方法,就是在Model的属性上添加Attribute,告诉程序,哪些属性要生成,哪些不要,它们分别在什么时候生成

上图中,我利用了 MetadataType(为了配合Entity Framework和MVC数据验证,详细请看我另一篇文章:传送门),

然后在MusicMetaData的属性上,加上了Attribute

这个Attribute代表,我在Create的时候,需要输入这个属性;在Edit的时候就不需要;Order很好理解了,就是顺序

然后怎么在页面中使用呢?

就是这么简单,”Create”代表我现在是在Create

后面是一个lambda表达式,传入的是 这个Model属性的名称,和类别(Textbox or TextArea?)

最后,就可以自动生成了动态表单了

三、上码

DynamicForm

using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Text; using System.Web.Mvc; namespace DS.Web.MVC { [AttributeUsage(AttributeTargets.Field|AttributeTargets.Property,AllowMultiple=false)] public class DynamicFormAttribute : Attribute { private int order =0; public int Order { get { return order; } set { order = value; } } private int type = 0; public int Type { get { return type; } set { type = value; } } private Dictionary<string, bool> state = new Dictionary<string, bool>(); public bool this[string key] { get { if (state.ContainsKey(key)) { return state[key]; } else { return false; } } } /// <summary> /// 用法示例:DynamicFormAttribute("Create",true,"Edit",false) /// 上述用法:在创建的时候显示,在修改的时候不显示 /// </summary> public DynamicFormAttribute(params object[] states) { for (int k = 0; k < states.Length; k += 2) { state.Add(states[k].ToString(), (bool)states[k + 1]); } } } public static class HtmlExtensions { /// <summary> /// 动态生成表单 /// </summary> /// <typeparam name="TModel"></typeparam> /// <param name="htmlHelper"></param> /// <param name="state">状态</param> /// <param name="ItemTemplate">模板</param> /// <returns></returns> public static MvcHtmlString DynamicForm<TModel>(this HtmlHelper<TModel> htmlHelper, string state, Func<string, int, string> ItemTemplate) { return DynamicForm(htmlHelper, state, ItemTemplate, null, null); } /// <summary> /// 动态生成表单 /// </summary> /// <typeparam name="TModel"></typeparam> /// <param name="htmlHelper"></param> /// <param name="state">状态</param> /// <param name="ItemTemplate">模板</param> /// <param name="AlternatingItemTemplate">隔行模板</param> /// <returns></returns> public static MvcHtmlString DynamicForm<TModel>(this HtmlHelper<TModel> htmlHelper, string state, Func<string, int, string> ItemTemplate, Func<string, int, string> AlternatingItemTemplate) { return DynamicForm(htmlHelper, state, ItemTemplate, AlternatingItemTemplate, null); } /// <summary> /// 动态生成表单 /// </summary> /// <typeparam name="TModel"></typeparam> /// <param name="htmlHelper"></param> /// <param name="state">状态</param> /// <param name="ItemTemplate">模板</param> /// <param name="SeparatorTemplate">分隔模板</param> /// <returns></returns> public static MvcHtmlString DynamicForm<TModel>(this HtmlHelper<TModel> htmlHelper, string state, Func<string, int, string> ItemTemplate, Func<string> SeparatorTemplate) { return DynamicForm(htmlHelper, state, ItemTemplate, null, SeparatorTemplate); } /// <summary> /// 动态生成表单 /// </summary> /// <typeparam name="TModel"></typeparam> /// <param name="htmlHelper"></param> /// <param name="state">状态</param> /// <param name="ItemTemplate">模板</param> /// <param name="AlternatingItemTemplate">隔行模板</param> /// <param name="SeparatorTemplate">分隔模板</param> /// <returns></returns> public static MvcHtmlString DynamicForm<TModel>(this HtmlHelper<TModel> htmlHelper, string state, Func<string, int, string> ItemTemplate, Func<string, int, string> AlternatingItemTemplate, Func<string> SeparatorTemplate) { var sb = new StringBuilder(); //分析出拥有DynamicFormAttribute的属性,并排序 var props = new List<object[]>(); var meta = typeof(TModel).GetCustomAttributes(typeof(MetadataTypeAttribute), false); if (meta.Length != 0) { foreach (var p in ((MetadataTypeAttribute)(meta[0])).MetadataClassType.GetProperties()) { var attrs = p.GetCustomAttributes(typeof(DynamicFormAttribute), false); if (attrs.Length != 0) { var attr = attrs.FirstOrDefault(a => ((DynamicFormAttribute)a)[state]); if (attr != null) { int index; for (index = 0; index < props.Count; index++) { if ((int)props[index][2] > ((DynamicFormAttribute)attr).Order) { break; } } props.Insert(index, new object[] { p.Name, ((DynamicFormAttribute)attr).Type,((DynamicFormAttribute)attr).Order }) ; } } } } //输出Html for (int k = 0; k < props.Count; k += AlternatingItemTemplate == null ? 1 : 2) { sb.Append(ItemTemplate(props[k][0].ToString(), (int)props[k][1])); if (k + 1 != props.Count) { if (SeparatorTemplate != null) { sb.Append(SeparatorTemplate()); } if (AlternatingItemTemplate != null) { sb.Append(AlternatingItemTemplate(props[k + 1][0].ToString(), (int)props[k + 1][1])); if (k + 2 != props.Count && SeparatorTemplate != null) { sb.Append(SeparatorTemplate()); } } } } //输出 return MvcHtmlString.Create(sb.ToString()); } } }

解释说明:

1、上面一部分是给Model加的Attribute

2、第二部分是HtmlHelper的扩展,用于生成Html代码

四、用法示例

1)生成表单,然后需要隔行更换样式,单行加上class=”1″,双行加上class=”2″,并且2行之间有特殊代码”<br/>”

代码

<%=Html.DynamicForm("Create", (name, type) => "<div class=\"1\">" + Html.TextBox(name).ToString() + "<div/>", (name, type) => "<div class=\"2\">" + Html.TextBox(name).ToString() + "<div/>", () => "<br/>")%>

2)表单中有一个属性Content,需要用 TextArea

代码

namespace EF { [MetadataType(typeof(MusicMetaData))] public partial class Music { } public class MusicMetaData { [DynamicForm("Create", true, "Edit", false, Order = 3)] public bool IsDeleted { get; set; } [DynamicForm("Create", true, "Edit", false, Order = 1)] public bool IsExist { get; set; } [DynamicForm("Create", true, "Edit", false, Order = 2, Type = 2)] public string Content { get; set; } } }

注意上面,我把Content属性的Type改成了2

<%=Html.DynamicForm("Create", (name, type) => type == 2 ? Html.TextArea(name).ToString() : Html.TextBox(name).ToString()) %>

五、

这个想法应该还有很多不完善的地方,所以就先不上示例程序了

如果有什么问题,欢迎大家指出,也可以留言询问各种用法~

所有代码均在上面那块代码段中,可以直接使用~

别忘记添加一些引用~

赞(0) 打赏
分享到: 更多 (0)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏