一、何谓动态表单
最近再做一个项目,运营只是初步确定了功能,再加上项目比较复杂,所以我不好确定数据库结构
我随时有可能在某个表中加一个属性…
这时候,如果我有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?)
最后,就可以自动生成了动态表单了
三、上码
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)]
publicclass DynamicFormAttribute : Attribute
{
privateint order =0;
publicint Order
{
get { return order; }
set { order = value; }
}
privateint type =0;
publicint Type
{
get { return type; }
set { type = value; }
}
private Dictionary<string, bool> state =new Dictionary<string, bool>();
publicboolthis[string key]
{
get
{
if (state.ContainsKey(key))
{
return state[key];
}
else
{
returnfalse;
}
}
}
///<summary>
/// 用法示例:DynamicFormAttribute("Create",true,"Edit",false)
/// 上述用法:在创建的时候显示,在修改的时候不显示
///</summary>
public DynamicFormAttribute(paramsobject[] states)
{
for (int k =0; k < states.Length; k +=2)
{
state.Add(states[k].ToString(), (bool)states[k +1]);
}
}
}
publicstaticclass HtmlExtensions
{
///<summary>
/// 动态生成表单
///</summary>
///<typeparam name="TModel"></typeparam>
///<param name="htmlHelper"></param>
///<param name="state">状态</param>
///<param name="ItemTemplate">模板</param>
///<returns></returns>
publicstatic 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>
publicstatic 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>
publicstatic 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>
publicstatic 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, newobject[] { 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/>"
2)表单中有一个属性Content,需要用 TextArea
{
[MetadataType(typeof(MusicMetaData))]
publicpartialclass Music
{ }
publicclass MusicMetaData
{
[DynamicForm("Create", true, "Edit", false, Order =3)]
publicbool IsDeleted { get; set; }
[DynamicForm("Create", true, "Edit", false, Order =1)]
publicbool IsExist { get; set; }
[DynamicForm("Create", true, "Edit", false, Order =2, Type =2)]
publicstring Content { get; set; }
}
}
注意上面,我把Content属性的Type改成了2
五、
这个想法应该还有很多不完善的地方,所以就先不上示例程序了
如果有什么问题,欢迎大家指出,也可以留言询问各种用法~
所有代码均在上面那块代码段中,可以直接使用~
别忘记添加一些引用~