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() { };