MVC CheckBoxList不按预期发布

时间:2012-01-17 16:36:27

标签: asp.net-mvc

我已经在HtmlHelper上构建了一个CheckBoxListFor扩展,这要归功于这个精彩的答案http://bit.ly/Aevcea(下面的代码),但我发现它没有按预期发布。

我的表单基于一个模型组,该组具有(在其他属性中)字符串Name和int [] PersonIDs。

CheckBoxListFor呈现如下内容:

<ul>
    <li><input type="checkbox" name="PersonIDs" value="1" id="PersonIDs_1" /></li>
    <li><input type="checkbox" name="PersonIDs" value="2" id="PersonIDs_2" /></li>
</ul>

我的控制器有一个编辑(组组)方法来处理此表单的提交。但是,提交后我发现group.PersonIDs为null。虽然Request.Form [“PersonIDs”]设置为所选值(例如,如果检查上述两个项,则为“1,2”)。此外,如果我向我的Edit方法(int [] PersonIDs)添加另一个参数,那么它将带有预期内容(所选ID)。

有谁能解释我做错了什么?我的观点的相关位看起来像这样(额外的位被剥离):

@Html.TextBoxFor(m => m.Group.Name)
@Html.CheckBoxListFor(m => m.Group.PersonIDs, Model.MultiSelectListOfAllPeople)

请注意,我的Edit方法中的group参数确实返回了根据表单设置的名称。

为了完整起见,这是我的CheckBoxListFor扩展的完整主体:

public static MvcHtmlString CheckBoxListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, IEnumerable<TProperty>>> expression, MultiSelectList multiSelectList, object htmlAttributes = null)
{
    //Derive property name for checkbox name
    MemberExpression body = expression.Body as MemberExpression;
    string propertyName = body.Member.Name;

    //Get currently select values from the ViewData model
    IEnumerable<TProperty> list = expression.Compile().Invoke(htmlHelper.ViewData.Model);

    //Convert selected value list to a List<string> for easy manipulation
    List<string> selectedValues = new List<string>();

    if (list != null)
    {
        selectedValues = new List<TProperty>(list).ConvertAll<string>(delegate(TProperty i) { return i.ToString(); });
    }

    //Create div
    TagBuilder wrapper = new TagBuilder("ul");
    wrapper.AddCssClass("clearfix");
    wrapper.MergeAttributes(new RouteValueDictionary(htmlAttributes), true);

    //Add checkboxes
    foreach (SelectListItem item in multiSelectList)
    {
        wrapper.InnerHtml += String.Format("<li><input type=\"checkbox\" name=\"{0}\" id=\"{0}_{1}\" " +
                                            "value=\"{1}\" {2} /><label for=\"{0}_{1}\">{3}</label></li>",
                                            propertyName,
                                            item.Value,
                                            selectedValues.Contains(item.Value) ? "checked=\"checked\"" : "",
                                            item.Text);
    }

    return MvcHtmlString.Create(wrapper.ToString());
}

2 个答案:

答案 0 :(得分:1)

好的,问题是CheckBoxListFor扩展需要将控件名称呈现为Group.PersonID而不仅仅是PersonID。表单绑定到一个对象,该对象本身是视图模型的子属性。我已经快速调整了我的CheckBoxListFor方法,但是我会非常感激地接受更优雅的解决方案!我正在传递一个额外的布尔参数includeDeclaringType来告诉它是否在ID中包含声明类型的名称。不确定这是否可以通过任何其他方式推断..?

    public static MvcHtmlString CheckBoxListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, IEnumerable<TProperty>>> expression, MultiSelectList multiSelectList, bool includeDeclaringType, object htmlAttributes = null)
    {
        //Derive property name for checkbox name
        MemberExpression body = expression.Body as MemberExpression;
        string declaringTypeName = body.Member.DeclaringType.Name;
        string propertyName = body.Member.Name;

        //Get currently select values from the ViewData model
        IEnumerable<TProperty> list = expression.Compile().Invoke(htmlHelper.ViewData.Model);

        //Convert selected value list to a List<string> for easy manipulation
        List<string> selectedValues = new List<string>();

        if (list != null)
        {
            selectedValues = new List<TProperty>(list).ConvertAll<string>(delegate(TProperty i) { return i.ToString(); });
        }

        //Create div
        TagBuilder wrapper = new TagBuilder("ul");
        wrapper.AddCssClass("clearfix");
        wrapper.MergeAttributes(new RouteValueDictionary(htmlAttributes), true);

        //Add checkboxes
        foreach (SelectListItem item in multiSelectList)
        {
            var name = string.Concat(
                includeDeclaringType ? string.Format("{0}.", declaringTypeName) : "",
                propertyName
            );

            var id = string.Concat(
                includeDeclaringType ? string.Format("{0}_", declaringTypeName) : "",
                propertyName,
                "_",
                item.Value
            );

            wrapper.InnerHtml += String.Format("<li><input type=\"checkbox\" name=\"{0}\" id=\"{1}\" " +
                                                "value=\"{2}\" {3} /><label for=\"{1}\">{4}</label></li>",
                                                name,
                                                id,
                                                item.Value,
                                                selectedValues.Contains(item.Value) ? "checked=\"checked\"" : "",
                                                item.Text);
        }

        return MvcHtmlString.Create(wrapper.ToString());
    }

答案 1 :(得分:0)

您还可以使用ExpressionHelper.GetExpressionText方法,它会为您生成正确的输入名称:

var expressionText = ExpressionHelper.GetExpressionText(expression);

wrapper.InnerHtml +=
        string.Format("<li><input type=\"checkbox\" name=\"{0}" id=\"{0}_{1}\" " +
                      "value=\"{1}\" {2} /><label for=\"{0}_{1}\">{3}</label></li>",
                       expressionText,
                       item.Value,
                       selectedValues.Contains(item.Value) ? "checked=\"checked\"" : "",
                       item.Text);