用于动态视图数据/表单的ASP.Net MVC编辑器模板

时间:2011-09-13 14:45:51

标签: c# asp.net asp.net-mvc asp.net-mvc-3 c#-4.0

我正在实现一个MVC3 / Razor Web应用程序,它可以检索用户可以从其他服务编辑的一些“字段”,因此在视图中编辑的属性列表在编译时是完全动态的和未知的。

我编写了一些部分视图和HTML帮助程序,它们遍历从其他服务检索到的组和属性。现在,我必须为各种属性类型构建标记,并考虑为什么不重新使用MVC编辑器模板系统呢?支持各种数据类型(例如复选框等),甚至可以使用我的自定义模板对其进行自定义。

到目前为止一切顺利,但如何将Html.EditorFor()Html.Editor()用于自定义数据对象/属性?构建动态表单而不使用静态类型数据作为视图模型的含义。

以下是我的HTML帮助程序代码的简约示例:

public static MvcHtmlString GetField(this HtmlHelper helper, Field field)
{
...
   return helper.EditorFor(field, m => m.Value);
...
}

属性“field”是我从外部服务获得的字段。它具有object类型的“Value”属性。我喜欢为这个属性类型构建编辑器代码。

据我所知,编辑器模板是基于当前视图模型构建的。我可以将另一个对象作为模型传递给当前视图模型(例如,在上面的示例“field”中)?

任何帮助都会很棒!

干杯, 马克

1 个答案:

答案 0 :(得分:3)

我的第一个ASP MVC任务涉及构建动态表单,而且不是直截了当的 基本上你不能使用内置的助手或验证,因为他们期望强类型的对象。

我的视图基本上遍历输入字段,检查DataType(bool,int,string,datetime等)并直接为该类型构建编辑器。

您还必须手动执行所有验证,使用属性修改类型以执行此操作不起作用,请参阅我的问题if(ModelState.IsValid) doesn't work with FormsCollection. What to use instead?

Razor View逻辑(我们使用DevExpress MVC扩展,但你得到漂移)
表单对象及其字段集合是我们定制的对象,它描述了表单的外观(页面收集搜索条件,这就是为什么你会在代码中看到标准和搜索类型名称)。

<table id="criteriaTable">
@foreach (var field in form.Fields)
{
    <tr id="criteriaTableRow">
    @if (field.IsVisible)
    { 
    <td id="criteriaTableLabelCol"> 
       @Html.DevExpress().Label(s => s.Text = field.Caption).GetHtml()
    </td>
    <td id="criteriaTableEditCol">
        @if (field.Type == typeof(bool))
        {
           @Html.CheckBox(s =>
      {
          s.Checked = field.IsBoolSet;
          s.Name = field.Name;
          s.ClientEnabled = !field.IsReadonly;
      }).GetHtml()
        }
        else if (field.Type == typeof(DateTime))
        {
            Html.DevExpress().DateEdit(s =>
            {
                s.Name = field.Name;
                s.ClientEnabled = !field.IsReadonly;
                if (!string.IsNullOrEmpty(field.Value))
                {
                    DateTime dateValue;
                    if (DateTime.TryParse(field.Value, out dateValue))
                        s.Date = dateValue;
                }
            }).GetHtml();
        }
        else if (field.ListValues.Count > 0)
        {
            <input type="hidden" id="@("initiallySelected" + field.Name)" value="@field.Value" />
            Html.DevExpress().ListBox(s =>
            {
                s.Name = field.Name;
                s.ClientVisible = field.IsVisible;
                s.ClientEnabled = !field.IsReadonly;
                s.Properties.SelectionMode = DevExpress.Web.ASPxEditors.ListEditSelectionMode.CheckColumn;
                s.Properties.TextField = "Name";
                s.Properties.ValueField = "Value";
                s.Properties.ValueType = typeof(string);

                //s.Properties.EnableClientSideAPI = true;
                foreach (var item in field.ListValues)
                {
                    s.Properties.Items.Add(item.Name, item.Value);
                }

                //s.Properties.ClientSideEvents.SelectedIndexChanged = "MultiSelectListChanged";
                s.Properties.ClientSideEvents.Init = "MultiSelectListInit";
            }).GetHtml();

        }
        else
        {
            //Html.TextBox(field.Name, field.Value)
            Html.DevExpress().TextBox(s =>
            {
                s.Name = field.Name; s.Text = field.Value;
            }).GetHtml();
        }
        @Html.ValidationMessage(field.Name)
        <input type="hidden" name="@("oldvalue_" + field.Name)" value="@field.Value" />
        <input type="hidden" name="@("olduse_" + field.Name)" value="@(field.IncludeInSearch ? "C" : "U")" />
    </td>
    <td id="criteriaTableIncludeCol"> 
        @Html.DevExpress().CheckBox(s =>
   {
       s.Checked = field.IncludeInSearch;
       s.Name = "use_" + field.Name;
       s.ClientEnabled = (!field.IsMandatory);
   }).GetHtml()
    </td>
    }
    </tr>
}
    </table>

Controller操作接受Forms Collection。我循环遍历formsCollection,查找我在视图中指定的控件名称。

[HttpPost]
public ActionResult QueryCriteria(FormCollection formCollection)
{
    var isValid = true;
    foreach (var field in form.Fields)
    {
        var value = (formCollection[field.Name] ?? "").Trim();
        ...

如果存在任何验证错误,我可以通过将ModelError直接添加到模型来指定控制级别验证,例如

ModelState.AddModelError(field.Name, "This is a mandatory field");

如果有验证错误,我会返回View。

希望这有帮助。