利用Attribute實現的 MVC動態表單
一、何謂動態表單
最近再做一個項目,運營只是初步確定了功能,再加上項目比較復雜,所以我不好確定數據庫結構
我隨時有可能在某個表中加一個屬性…
這時候,如果我有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()) %>
五、
這個想法應該還有很多不完善的地方,所以就先不上示例程序了
如果有什么問題,歡迎大家指出,也可以留言詢問各種用法~
所有代碼均在上面那塊代碼段中,可以直接使用~
別忘記添加一些引用~

浙公網安備 33010602011771號