In part 1, I discussed what I would like out of a reusable collection editor, and dropped the code for the series.
Now I want to talk about how the user is able to add and remove items from the collection through the one form and how to action this request.
This is a checkbox on every element of the collection displayed in the UI, the user can deselect an existing one to delete an item and select a new one to add.

This is provided through a helper and no property exists on the model in question.
<span class="selector">
@Html.CollectionSelector(Model.Id>0)
</span>This means that the model does not contain particular information about how to edit it, and this means that it can be used again for display. Sometimes you want a model to know specifics about the view its being used for, and some people advocate one model per view, but I didn't want the collection editor to be the reason why a model was tied to a particular view or a particular view mode.
So the mark-up produced will post a back the selected state for the item in the collection, for example.
<span class="selector">
<input id="Addresses_1____selector" type="checkbox"
value="true" name="Addresses[1].__selector">
<input type="hidden"
value="false" name="Addresses[1].__selector">
</span>On post the binder now has this necessary information to remove the unselected items, and importantly, remove any validation errors caused by items to be removed - adding is done by the default binder anyway.
To achieve this you must create a new IModelBinderProvider which can check for models which are collections and apply the new CollectionBinder.
public IModelBinder GetBinder(Type modelType) {
if (modelType.IsGenericType) {
var modelGenericType = modelType.GetGenericTypeDefinition();
if (modelGenericType == typeof(IEnumerable<>)
|| modelGenericType == typeof(ICollection<>)
|| modelGenericType == typeof(IList<>)) {
return _collectionBinder;
}
}
return null;
}This is the CollectionBinder.BindModel method doing the required work
public override object BindModel(
ControllerContext controllerContext,
ModelBindingContext bindingContext) {
// let the base do the majority of the work
var model = base.BindModel(controllerContext, bindingContext);
if(model!=null) {
var list = (IList) model;
var index = 0;
// loop a copy of the list
foreach (var item in list.Cast<object>().ToArray()) {
var key = string.Format("{0}[{1}]",
bindingContext.ModelName, index);
// check for the selector being false
if (controllerContext.HttpContext.Request.Form[
string.Format("{0}.{1}", key, SELECTOR_NAME)] == "false") {
// remove the item from the original list
list.Remove(item);
// remove any validation errors
foreach (var stateKey in bindingContext.ModelState.Keys.ToArray()
.Where(k => k.StartsWith(key)))
bindingContext.ModelState.Remove(stateKey);
}
index++;
}
}
return model;
}Its worth mentioning, I wanted really to do the check before the model was added to the collection and particularly before it was validated, but there no current entry points to be able to do this, and after pouring over the MVC 3 source code, this was the best solution I could find.
I will look at the Views and Templates used and show how the collection editor attempts to be as DRY as it can.
sandbox-collectionEditor.zip c# asp.net mvc vs 2010
* please note the demo uses the SQL Server compact framework, you can change the web.config connection string to point at your SQL Server instance instead
great!!
thanks a lot...
| < | May 2012 | |||||
|---|---|---|---|---|---|---|
| S | M | T | W | T | F | S |
| 29 | 30 | 1 | 2 | 3 | 4 | 5 |
| 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 13 | 14 | 15 | 16 | 17 | 18 | 19 |
| 20 | 21 | 22 | 23 | 24 | 25 | 26 |
| 27 | 28 | 29 | 30 | 31 | 1 | 2 |
Add-ins AJAX ASP.NET MVC Browsers C# Caching CodeDom Compression CORS CSS CV Data Database DependencyResolver Development Dynamic Entity Framework Error Handling Extend File Upload Forms GDI+ HTML HTML Editor HTTP Interfaces JavaScript JQuery MCE MetadataProvider MSBuild Numbers Objects Patterns Progressive Enhancement Projects Publish Regex Resources Security SEO SMTP Source Control Strings Sub-Collections TDD Templates Tools Twitter User Interface WCF Web Development WHS WMC XLinq XML
15/05/2012
WindowsAzure
Announcing the MEET Windows Azure Event! Streamed online June 7th. Register at http://t.co/bObzTAuL #MEETAzure #WindowsAzure
10/05/2012
kevinwhinnery
Comparing Titanium and PhoneGap - A common question I get asked at developer events and conferences is how... http://t.co/Zq2eND6B
09/05/2012
brianleroux
PhoneGap goals and philosophy: http://t.co/wkq8wI2T
just now
ChaseDiane
encrypting phonegap android by joseesfera: currently being developed for PhoneGap app, we need to encrypt the co... http://t.co/qCSGs9iX
just now
rgonv
#Tech Post: Bing Search API now available on Windows Azure Marketplace: The Bing Search API is now available on ... http://t.co/iBvXOjbC