Mutually Exclusive Checkboxes with JQuery

The Problem

Let’s say you want to have a set of mutually exclusive check boxes and want to implement that logic on the client so that the user can get immediate, responsive feedback about their selection choices. Normally, mutual exclusivity would be enforced with radio buttons ensuring user familiarity with the interface selections. However, in this specific case, let’s suppose that within a specific sub category, a user can select as many options as they want, but cannot select options from more than one subcategory. Imagine setting contact preferences as either No or some type of Yes. Or perhaps there is a Online food order form where certain topping choices are mutually exclusive between categories.

Solution

You can bind JQuery to the click event of your checkboxes and use that event to ensure that checkboxes in any other groups are set to checked=false.
First group each distinct set of checkboxes inside a set of <div></div> tags:

<div id="opt1">  
  <input id="a" type="checkbox">  
  <input id="b" type="checkbox">  
</div>  

We can monitor for clicks to any of the child items inside of the div tag, by using it’s ID as our initial selector $('#opt1') and looking for a Click event on that div. This will fire when any element inside of that div tag is clicked.

Next, we’ll check if the event that fired the event has a checked property set to true, by chekcing if (event.target.checked). This will prevent clicks on the label from processing our code or de-selections in a particular category from running un-neccessary code.

Finally, we’ll want to go through all check box categories and remove any check marks from any checkboxes that they contain. To do this, we’ll first start by selecting the ID from an alternate div tag. Then we’ll call find('input') to find all child items of type Input. To each of these items, we’ll call removeAttr('checked') to remove the checked attribute from each one.

$('#yes').click(function (event) {  
  if (event.target.checked) {  
    $('#no').find('input').removeAttr('checked');  
  }  
}); 

Source Code

Handle Text Change In Selected Index Changed Event

The Problem

You may have noticed that .NET won’t let you change the Text value of a ComboBox during the SelectedIndexChanged event. This can be quite frustrating and difficult to work-around if you have want to update the ComboBox text during a when the user makes a selection. For example, you might want to back out of a bad user selection or bind each selection item with different formatting when selected. Let’s clear up a couple things that will help us understand why .NET prevents us from doing what seems like a reasonable default functionality, and then we’ll show one way to workaround this limitation.

Understanding the System.Windows.Forms.ComboBox

When you select an item from a combo box, the SelectedIndex property of the ComboBox changes to the index location of the item you selected. The ComboBox Text property is automatically updated to the display value of the item at the specified index. This is the only update to the ComboBox Text that is allowed by default. To understand this, let’s look at another out-of-the-box ComboBox behavior.

If you have a ComboBox populated with the following items:

  1. Red
  2. Blue
  3. Green

Let’s say the current SelectedIndex value is -1 and the current Text value is Select a color...

If in our code we change the value of the Text to blue:

ComboBox1.Text = "Blue"

Then .NET will automatically recognize that this matches an item in our collection and change the SelectedIndex value to 2, thereby also firing the SelectedIndexChanged event. The reason .NET won’t let you change the Text property during a SelectedIndexChanged event is because they are worried about creating an endless loop by firing another SlectedIndexChanged event.

The Solution

We can resolve this relatively easily by invoking a delegate during the selection changed event that will eventually change the text property. We’ll load a generic WinForms window by adding two items to a ComboBox, one of which we want to reset the form when selected. This item represents a selection that may or may not be invalid, but is expensive to figure out, so we don’t want to necessarily evaluate it early and remove it from the list before the user has a chance to select it.

We’ll start by declaring a Delegate Sub within our class. The only important thing about a Delegate is the method signature that you are passing in. Since we want to call the ResetComboBox method which contains on parameters, our Delegate will not contain any arguments as well.

On the SelectedIndexChanged event, we’ll call BeginInvoke and specify that when it invokes, it should look to a method at the AddressOf ResetComoboBox.

'Declares a delegate sub that takes no parameters  
Delegate Sub ComboDelegate()  

'Loads form and controls  
Private Sub LoadForm(sender As System.Object, e As EventArgs) _  
 Handles MyBase.Load  
 ComboBox1.Items.Add("This is okay")  
 ComboBox1.Items.Add("This is NOT okay")  
 ResetComboBox()  
End Sub  

'Handles Selected Index Changed Event for combo Box  
Private Sub ComboBoxSelectionChanged(sender As System.Object, e As EventArgs) _  
 Handles ComboBox1.SelectedIndexChanged  
 'if option 2 selected, reset control back to original  
 If ComboBox1.SelectedIndex = 1 Then  
  BeginInvoke(New ComboDelegate(AddressOf ResetComboBox))  
 End If  

End Sub  

'Exits out of ComboBox selection and displays prompt text   
Private Sub ResetComboBox()  
 With ComboBox1  
  .SelectedIndex = -1  
  .Text = "Select an option"  
  .Focus()  
 End With  
End Sub  

You’ll notice that when ResetComboBox is eventually called by the delegate, it will also fire the SelectionChanged event when we change the SlectedIndex to -1. If there’s a chance that you’re handling anything in the change event that could cause a repetitive loop, you can include a private cancelAction boolean property in your class, defaulted to False. Then when you start the ResetComboBox method, set cancelAction to True and reset it to False at the end of method. In the selection changed event, exit the sub if cancel action will set and you will never accidentally execute code when you’re resetting controls

If cancelAction Then Exit Sub

Source Code

You can find the source code for this application from SkyDrive. Please comment with any suggestions or questions