Resolving the Selection Out of Range Error in a RadComboBox

If you've worked with ASP.NET WebForms for any length of time then you're familiar with the "yellow screen of death" and quite possibly have encountered this error.

The selection out of range error occurs when you have a dropdown list (or, in my case, a Telerik RadComboBox) and the value for the record you are editing no longer appears in the dropdown list. Many astute Developers and I.T. Experts will rightly point out that your app should have logic and constraints in place to prevent a user from deleting something from a list of values (i.e. a data source list) that already exists in some associated record.

As a simple example, perhaps you have a List of Colors (Red,Green,Blue,Yellow) that you show to Users and allow a user to select their Favorite Color (say, Blue). If this is a dropdown list and you have users' that selected "Blue" as their Favorite Color then you would not want "Blue" removed from the List of Colors because then editing an existing user with "Blue" as their Favorite Color will cause the error.

In the simple case above, you could place logic in the app to check associated records (i.e. Favorite Color) BEFORE removing a value from the List of Colors.

However, there are use cases that occur frequently that cannot be resolved using the method above. For example, if you tie anything to a Windows Username or Active Directory Group then the potential for the selection out of range error can exist for the following reasons:

  1. A User has left the company
  2. A User has requested that any identifying data be removed from your app
  3. A User has changed roles and is no longer in a particular Active Directory Group
I'm sure that you can think up additional scenarios related to dropdown lists where the source list data is outside of your app.

The example I'm going to use in this blog post is similar to that described previously. I have an ASP.NET WebForms app that uses Windows Authentication. Users register some additional information in the app that is tied to their Windows username. Administrators of the app assign tags to users, effectively mimicking Active Directory Groups. In this specific case, Projects are assigned to Principal Investigators. An Administrator assigns a Tag="Investigator" to the User record. Since a User can be removed from Active Directory and Investigators come and go, this is a use case that cannot be resolved by "preventing a deletion".

Consider that we have 3 users: dennis, mary and john. Dennis and Mary are both still employees but only Dennis is tagged as an "Investigator". John has left the company. At one time in the company's history, all 3 users were "Investigator(s)". Currently, only Dennis is still an "Investigator". Mary has moved into a management role and Dennis continues to ponder why he's always passed over for promotions.

Current User List
Current "Investigator" List

Now let's have a look at what happens when we EDIT a Project in each case.

First the base case (dennis), a user that is still an employee and an "Investigator". This is straight forward. The username "dennis" is set as the SelectedValue of the dropdown list. The dropdown list is sourced from Users who are Investigators and that is only Dennis.

 

The second case is "john". John is no longer an employee of the company so his username doesn't exist any more. He has no User record in the WebForms app. The screenshot to the right illustrates how we want the app to handle this situation. Add a new dropdown item to the dropdown list with "john" as both the text and the value.

 

The third and final case is "mary". Mary is still an employee of the company but no longer an "Investigator". She has a User record in the WebForms app. The screenshot to the right illustrates how we want the app to handle this situation. Add a new dropdown item to the dropdown list with "mary" as the value. Go to the User record to get values to create the text for the new dropdown item which includes the lastname, firstname (username)(e.g. "Jones, Mary (mary)" ).

So, let's take a look at the code changes we introduced to resolve the error.

The first thing we did was add code to the ItemCreated event of the FormView on the Edit Project WebForms page. The name of the RadComboBox we are dealing with is rcbAssignedTo


            rcb = CType(fvForm.FindControl("rcbAssignedTo"), RadComboBox)
            If rcb IsNot Nothing Then
                If rcbItem IsNot Nothing Then
                    rcb.Items.Add(rcbItem)
                    rcb.SelectedValue = rcbItem.Value
                End If
            End If
						   
Here we look for the RadComboBox. Once we find it, we check whether rcbItem exists. If it does then we add it to the RadComboBox Items collection. But...wait...where does rcbItem come from?

rcbItem is a page level private variable. It gets defined (or not) in the DataBinding event for the FormView.


    Private rcbItem As RadComboBoxItem
	
    Private Sub fvForm_DataBinding(sender As Object, e As EventArgs) Handles fvForm.DataBinding
        Dim id As String = Request.QueryString("Id")
        Dim usrNm As String = csiUserDB.GetInvestigatorUsernameForProjectId(id)
        Dim cnt As Integer = csiUserDB.GetInvestigatorCount(usrNm)
        If cnt = 0 Then
            rcbItem = New RadComboBoxItem()
            rcbItem.Value = usrNm
            Dim itm As Object = csiUserDB.GetInvestigator(usrNm)
            rcbItem.Text = itm
        End If
    End Sub
						   

In the DataBinding event, we first retrieve the project id "Id" from the query string. With that variable we make a database call to get the investigator's username on the Project. Then, we make another database call to determine if the retrieved investigator's username is still part of the Investigator List. If we come back with a count of zero, then the Investigator for this Project is no longer part of the list and we have to create a new RadComboBoxItem that we will store in the rcbItem variable for use during the ItemCreated event. When we make the GetInvestigator call, this will return the username if the user no longer exists in the database. So, either we get a "Lastname, Firstname (Username)" value back from the GetInvestigator call or we get the username we sent as a parameter.

Thus, with that little bit of code; a page level private variable; and, some code for the Form's ItemCreated and DataBinding events we've solved a typical yellow screen of death scenario. In addition, we have a model for how to approach the problem going forward. Moreover, this implementation has, in my opinion, a better "separation of concerns" between layers (and services or apps if your users are ultimately managed in a separate way).