Thursday, April 09, 2009

Referencing jQuery on a masterpage without breaking intellisense

If you are like me, you were pretty excited when Microsoft added support for jQuery to intellisense in visual studio. I am not going to go into how this is accomplished. If you would like details you can read Scotts blog here.

The Problem
When referencing jquery through a masterpage, you are vulnerable at runtime to the path of the content page. So if you keep your masterpages in a folder like "/Masters" but the content page you are using is in the root, a jquery path like this won't work:

<script src="../js/jquery-1.3.2.js" type="text/javascript"></script>

Intellisense works fine with this, but at runtime the path is wrong when using the content page in the root. To get the correct path at runtime we can do something like this:

<script src="<%= VirtualPathUtility.ToAbsolute("~/js/jquery-1.3.2.min.js") %>" type="text/javascript"></script>

With that reference we get the correct reference at runtime, but we get no intellisense.

The Solution
Until this problem is fixed by Microsoft, we need to use the best of both worlds. A static reference for the IDE, but a dynamic reference at runtime. To do this we use a condition on the reference that we don't want at runtime, like this:

<script src="<%= VirtualPathUtility.ToAbsolute("~/js/jquery-1.3.2.min.js") %>" type="text/javascript"></script>
<% if (this == null) { %>
    <script src="../js/jquery-1.3.2.js" type="text/javascript"></script>
<% } %>

You can replace the "this == null" with a simple "false" but you will get unreachable code warnings. You just need anything that always evaluates to false. This works great and has very little overhead in your page. I am happy that Microsoft added intellisense support for jQuery, now they just need to finish it so we don't have to use kludges like this.

Tuesday, April 07, 2009

'DropDownList' has a SelectedValue which is invalid because it does not exist in the list of items.

This error occurs when the SelectedValue property of a DropDownList is being set to a value which does not exist in the DropDownList. There are various reasons this can occur. In my case, the cause was that a record in the database was referring to a user configurable "category" that had been deleted by the user. Here are three possibilities for handling the scenario. They differ mostly in style, so pick the one that suits your taste.

All of these examples use a DropDownList set up like this:

<asp:DropDownList runat="server" ID="ddlCategory" DataSource='<%# GetCategories() %>'   
    DataTextField="Value" DataValueField="Key" AppendDataBoundItems="true">
    <asp:ListItem Text="(none)" Value="" />
</asp:DropDownList>

Option 1:



SelectedIndex='<%# Eval("CategoryID") is DBNull ? -1 : ddlCategory.Items.IndexOf(ddlCategory.Items.FindByValue(Eval("CategoryID").ToString()))  %>'



Here, I use the ternary operator to determine the index to select. If the database value is DBNull then -1 is used as the index, otherwise a search of the existing list items is performed to determine where in the list the desired value is. The great thing about this is that if the item is not DBNull but it's not in the list of items either, IndexOf() will return -1!

Option 2:



SelectedIndex='<%# GetSelectedCategoryIndex(ddlCategory, Eval("CategoryID")) %>'



This passes the DropDownList and the desired value to a method. There you can use the provided information to determine which index to use and handle any errors that may occur. The method should return an int value.

Option 3:


aspx:

OnDataBound=ddlCategory_DataBound



C#:
protected void ddlCategory_DataBound(object sender, EventArgs e)
{
    ddlCategory = sender as DropDownList;
    //set the selected index.  If the CategoryID is not in the list, set index to 0
    ddlCategory.SelectedIndex = 0;
    try
    {
        ddlCategory.SelectedValue = ((GetDataItem() as DataRowView).Row["CategoryID"]).ToString();
    }
    catch { }
}