Monday 16 November 2009

Extending the ASP.NET MVC DropDownList Html helper

I'm currently using ASP.NET MVC v1.0 and came across the need to populate a drop down list complete with option groups via the Html.DropDownList helper class.  E.g.



Unfortunately the Html helper class in MVC v1.0 doesn't currently support this so I had to write my own.  There's probably a much quicker way of doing this but repeated googling failed to find it.  Anyway, you need four files called GroupSelectList.cs, GroupSelectListItem.cs, GroupMultiSelectList.cs and GroupDropListExtensions.cs.  The code for each can be found here:

http://www.filefactory.com/file/a1b8dd0/n/GroupMultiSelectList.cs
http://www.filefactory.com/file/a1b8dd1/n/GroupSelectList.cs
http://www.filefactory.com/file/a1b8dd2/n/GroupSelectListItem.cs
http://www.filefactory.com/file/a1b8ddh/n/GroupDropListExtensions.cs

Put these in your project, perhaps in a folder called HtmlHelperExtensions, but it doesn't really matter.  Once compiled you will then be able to create a grouped dropdown list in the same way you would a regular dropdown list.  The only difference is that when setting up your SelectList, instead of passing an IEnumerable of things to display, you pass a Dictionary, where the key corresponds to the group name and the IEnumerable corresponds to the options in that group.  E.g.

Controller file:

public ActionResult Index(){
  var group1 = new List<string> {"apple", "pear"};
  var group2 = new List<string> {"car", "bike"};

  var dictionary = new Dictionary<string, IEnumerable> { {"First group", group1}, {"Second group", group2}  };

  ViewData["GroupedDropDown"] = new GroupSelectList(dictionary);
  return View("Index");
}

View file
...
<%=Html.GroupDropDownList("Name", ViewData["GroupedDropDown"] as GroupDropDownList, "Select fruit or car...")%>

...


The above code would reproduce the dropdown list above.

Friday 6 November 2009

Argument 'Key must not be null' cannot be null.

Occasionally during development within an ASP.NET (MVC)/NHibernate/Spring.NET environment I come across the above, rather cryptic, error. In full the stack trace looks like this:


[ArgumentNullException: Argument 'Key must not be null' cannot be null.
Parameter name: Key must not be null]
Spring.Util.AssertUtils.ArgumentNotNull(Object argument, String name) +203
Spring.Transaction.Support.TransactionSynchronizationManager.HasResource(Object key) +68
Spring.Data.NHibernate.Support.SessionScope.Open() +246
Spring.Data.NHibernate.Support.OpenSessionInViewModule.context_BeginRequest(Object sender, EventArgs e) +47
System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +68
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +75


Usually this occurs following a fresh deployment to an IIS7 environment. Some guy here reckons it's something to do with the OpenSessionInView class not finding a suitable session factory but I'm not so sure.

The bottom line is, that if you open up your web.config file, make a change (i.e. add/remove some blank space) and save it, then when you refresh the page the error will go away. Occasionally there is another error which was hidden by the first, but usually the web application works fine at this point. This is only a workaround, but the error only appears immediately after a deployment, and once fixed doesn't reappear so it's good enough for me.

Tuesday 3 November 2009

Error using Raphael javascript library in Firefox for hidden elements

I'm currently using the Raphael javascript library to display vector graphics across multiple browsers. When attempting to create a text node on a hidden canvas...

style="display:none"

...I came across the following error in Firefox:

Error: uncaught exception: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsIDOMSVGLocatable.getBBox]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: http://localhost:54561/Scripts/raphael.js :: anonymous :: line 1146" data: no]

The problem, which for some reason doesn't appear under IE, is that display:none means that the canvas is not available and so the call to getBBox()fails. If instead you hide the element using...

style="visibility:hidden; position:absolute;"

...then the canvas is rendered but not displayed allowing the call to getBBox() to succeed.

However, I was actually hiding the element so that it could be displayed using the slideDown() jquery function. This, along with most other jquery display functions, only works with display:none and not visibility:hidden. This is an easy fix as the following code demonstrates.

//Change display property
$("#targetElement").attr("style", "visibility:visible;display:none");
//Slide down functionality now operational
$("#targetElement").slideDown("slow");