views:

46

answers:

1

I'm trying out some MVC stuff and I was wondering if there's some way to edit dynamic fields. So to try this I added a keyed collection called CustomFields. I've tried both Hashtable and Dictionary.

In the view I then wrote:

<%:Html.TextBoxFor(model => model.CustomFields["x"])%>

This then generated this HTML:

<input id="CustomFields_x_" name="CustomFields[x]" type="text" value="" />

So it looks like I'm on the right way and that there are some functionality for this. But the problem is that when I call UpdateModel, it will not bind the value I write to this keyed collection.

Is this even possible to do? Do I need to do something special to solve it?

+1  A: 

I'm fairly certain there's a built in way of handling this but I couldn't find it. However, you can accomplish what you want with the following IModelBinder implementation.

Create a custom Binder for your model. First we're going to use the default Model Binder to attach any properties that aren't your dictionary.

public class CustomFieldsModelBinder : IModelBinder {

    IDictionary<string, string> dictionary;

    public CustomFieldsModelBinder() { }

    public object BindModel(ControllerContext controllerContext,
                            ModelBindingContext bindingContext) {
        if (bindingContext == null)
                        throw new ArgumentNullException("bindingContext");

        CustomFieldsModel returnValue;

        returnValue = (CustomFieldsModel)ModelBinders.Binders.DefaultBinder.BindModel(
                                         controllerContext,
                                         bindingContext);

        if (returnValue == null) {
            returnValue = new CustomFieldsModel();
        }

        if (returnValue.CustomFields == null)
                returnValue.CustomFields = new Dictionary<string, string>();
        foreach (string name in DictionaryKeyNames(controllerContext, "CustomFields")) {
            var postValue = controllerContext.HttpContext.Request.Form["CustomFields[" + name + "]"];
            returnValue.CustomFields[name] = postValue;
        }

        return returnValue;
    }

    //this method will grab the [name]'s from the collection
    protected virtual IList<string> DictionaryKeyNames(ControllerContext context, string prefix) {
        IList<string> list = new List<string>();
        Regex pattern = new Regex("^" + prefix + @"\[([^\]]+)\]");

        foreach (var key in context.HttpContext.Request.Form.AllKeys) {
            var match = pattern.Match(key);
            if (match.Success && !list.Contains(match.Value)) {
                list.Add(match.Groups[1].Value);
            }
        }

        return list;
    }
}

Once you have that you can then register your binder in the Global.asax

ModelBinders.Binders[typeof(CustomFieldsModel)] = new CustomFieldsModelBinder() { };
BuildStarted