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:
- A User has left the company
- A User has requested that any identifying data be removed from your app
- A User has changed roles and is no longer in a particular Active Directory Group
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.
Now let's have a look at what happens when we EDIT a Project in each case.
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
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?
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
rcbItem is a page level private variable. It gets defined (or not) in the DataBinding event for the FormView.
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.
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
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).