Introduction
This is a simple ListBox swapper which might be useful at times we need one. Also the control maintains the view-state of the Lists as JSON in hidden fields in the form. (Thus can be used as example for maintaining mvc page view-state as Json in hidden field.)

Background
I tried searching for quick to use ListBox swapper but couldn't find the .Net MVC specific one online. There are some of useful implementations available though (some of links below).
Also, some times we may want to avoid repeated database trips in binding the View controls (like when we just need to display validation message with original View. Or when we want to maintain the updated view-state until "Save" button is pressed.) We can maintain the view-state as Json in hidden field.
Using the code
-
The code uses simple
Html.ListBoxFor
helper to render the two lists to select from(viz. SelectedAvailableItems
and SelectedAssignedItems
). - At first request Initial Lists("
AvailableList
" and "SelectedList
") are data-bound. - On subsequent post requests, the "
AvailableList
" and "SelectedList
" are first bound from currentAvailableList
and currentAssignedList
and then updated based on our selection in SelectedAvailableItems
and SelectedAssignedItems
. - The current view-state of the lists is maintained as json objects in two hidden fields (viz.
currentAvailableList
and currentAssignedList
). - The two buttons "Add >>" and "<< Remove" swap items between
SelectedAvailableItems
and SelectedAssignedItems
.
Here is how our model looks like:
public class ListSwapperModel
{
public string Message { get; set; }
public bool IsSuccess { get; set; }
public int Id { get; set; }
public string DisplayName { get; set; }
public string btnSubmit { get; set; }
public IList<SelectListItem> AvailableList { get; set; }
public string currentAvailableList { get; set; }
public IList<string> SelectedAvailableItems { get; set; }
public IList<SelectListItem> AssignedList { get; set; }
public string currentAssignedList { get; set; }
public IList<string> SelectedAssignedItems { get; set; }
}
In our controller here is how we initially data-bind the two lists.
public ActionResult Index()
{
ListSwapperModel model = new ListSwapperModel();
model.Message = "Welcome to ListSwapper Demo!";
var serializer = new JavaScriptSerializer();
model.AvailableList = getAllListFromDB(); model.AssignedList = new List<SelectListItem>();
model.currentAvailableList = serializer.Serialize(model.AvailableList); model.currentAssignedList = serializer.Serialize(model.AssignedList);
return View(model);
}
In our "HttpPost" action we simply load the view-state first and then update it based on our selection:-
[HttpPost]
public ActionResult Index(ListSwapperModel model)
{
var serializer = new JavaScriptSerializer();
if(model.currentAvailableList!=null)
model.AvailableList= serializer.Deserialize<List<SelectListItem>>(model.currentAvailableList);
if(model.currentAssignedList!=null)
model.AssignedList = serializer.Deserialize<List<SelectListItem>>(model.currentAssignedList); try
{
switch (model.btnSubmit.ToUpper())
{
case ("ADD >>"):
{
if ((model.SelectedAvailableItems == null) || (model.SelectedAvailableItems.Count <= 0))
{
throw new Exception("No Items Selected in the Available List.");
}
else
{
foreach (string itm in model.SelectedAvailableItems)
{
var slctItm = model.AvailableList.FirstOrDefault(i => i.Value.Equals(itm));
model.AvailableList.Remove(slctItm);
model.AssignedList.Add(slctItm);
}
}
}
break;
case ("<< REMOVE"):
{
if ((model.SelectedAssignedItems == null) || (model.SelectedAssignedItems.Count <= 0))
{
throw new Exception("No Items Selected in the Assigned List.");
}
else
{
foreach (string str in model.SelectedAssignedItems)
{
var slctItm = model.AssignedList.FirstOrDefault(i => i.Value.Equals(str));
model.AssignedList.Remove(slctItm);
model.AvailableList.Add(slctItm);
}
}
}
break;
case ("SAVE"):
{
model.Message = "Save Item here!";
model.IsSuccess = true;
}
break;
default:
model.Message = "Un-Identified action!";
break;
}
}
catch (Exception exc1)
{
model.Message = "Sorry an error has occured! =>"+exc1.Message;
model.IsSuccess = false;
}
if (model.AssignedList != null)
model.currentAssignedList = serializer.Serialize(model.AssignedList);
else
model.currentAssignedList = null;
if (model.AvailableList != null)
model.currentAvailableList = serializer.Serialize(model.AvailableList);
else
model.currentAvailableList = null;
return View(model);
}
Our view is pretty much simple form with the two selection lists, two buttons ("Add >>" and "<< Remove") and the two hidden fields for view-states of our Available and Assigned lists.
<% using (Html.BeginForm())
{%>
<%= Html.ValidationSummary(true)%>
<table>
<tr><th>Display Name:</th><td><%= Html.TextBoxFor(m => m.DisplayName)%></td></tr>
<tr><td colspan="2">
<table class="tblNoBorders">
<tr><td>
<input type="hidden" id="currentAvailableList" name="currentAvailableList" value='<%= Model.currentAvailableList %>' />
<%= Html.ListBoxFor(m => m.SelectedAvailableItems, new SelectList(Model.AvailableList, "Value", "Text"), new { @class = "listswapper-list", @title = "Hold Ctrl/Command and left-click to deselect" })%>
</td>
<td><input type="submit" name="btnSubmit" style="width:80px;text-align:center;padding:0;" value="Add >>" /><br /><br />
<input type="submit" name="btnSubmit" style="width:80px;text-align:center;padding:0;" value="<< Remove" />
</td>
<td>
<input type="hidden" id="currentAssignedList" name="currentAssignedList" value='<%= Model.currentAssignedList %>' />
<%= Html.ListBoxFor(m => m.SelectedAssignedItems, new SelectList(Model.AssignedList, "Value", "Text"), new { @class = "listswapper-list", @title = "Hold Ctrl/Command and left-click to deselect" })%>
</td>
</tr>
</table>
</td></tr>
<tr><td colspan="2" style="text-align:right"><input type="submit" value="Save" name="btnSubmit" /></td></tr>
</table>
<%} %>
Points of Interest
History
-
30-Oct-2013 Initial draft.