tags:

views:

483

answers:

6

I have a group of checkboxes that I only want to allow a set amount to be checked at any one time. If the newly checked checkbox pushes the count over the limit, I'd like the oldest checkbox to be automatically unchecked. The group of checkboxes all use the same event handler shown below.

I have achieved the functionality with a Queue, but it's pretty messy when I have to remove an item from the middle of the queue and I think there's a more elegant way. I especially don't like converting the queue to a list just to call one method before I convert the list back to a queue.

  • Is there a better way to do this?
  • Is it a good idea to unhook are rehook the event handlers like I did.

Here's the code.

private Queue<CheckBox> favAttributesLimiter - new Queue<CheckBox>();
private const int MaxFavoredAttributes = 5;

private void favoredAttributes_CheckedChanged(object sender, EventArgs e)
{
    CheckBox cb = (CheckBox)sender;

    if (cb.Checked)
    {
        if (favAttributesLimiter.Count == MaxFavoredAttributes)
        {
            CheckBox oldest = favAttributesLimiter.Dequeue();                  

            oldest.CheckedChanged -= favoredAttributes_CheckedChanged;
            oldest.Checked = false;
            oldest.CheckedChanged += new EventHandler(favoredAttributes_CheckedChanged);
        }

        favAttributesLimiter.Enqueue(cb);

    }
    else // cb.Checked == false
    {                
        if (favAttributesLimiter.Contains(cb))
        {
            var list = favAttributesLimiter.ToList();
            list.Remove(cb);
            favAttributesLimiter=new Queue<CheckBox>(list);
        }
    }
}

Edit:
Chakrit answered my actual question with a better replacement for Queue(Of T). However, the argument that my idea of unchecking boxes was actually a bad idea was quite convincing. I'm leaving Chakrit's answer as accepted, but I've voted up the other answers because they're offering a more consistent and usable solution in the eyes of the user.

+2  A: 

I think you are looking for a LinkedList.

Use AddLast instead of Enqueue and RemoveFirst instead of Dequeue and for removing something in the middle, just use a normal Remove.

chakrit
Well, that should be more efficient when removing items.
Matt Blaine
That is much better. Thanks!
Ben Robbins
+3  A: 

Just in case you haven't thought of it this way around.

For a usability point of view, presumably you have some text saying something like "click no more than 4 check boxes".

In which case, why not simply keep a count of the number of checked boxes, and prevent any changes to the 5th box (until of course there are only 3 check boxes).

David L Morris
I thought about that. I couldn't decide which way was more annoying to the user, so I went with letting the user check the boxes that they want and the program unchecking the oldest. For usability, I don't know which way is better honestly.
Ben Robbins
Well your way has less mouse clicks, but you are relying on the user noticing that the other boxes are being cleared. Personally, I find this more annoying (especially if I don't see it the first time).
David L Morris
After sleeping on it, I did decide not to automagically uncheck boxes. It only really made sense at 3 AM.
Ben Robbins
+3  A: 

One thing to ask yourself is: do you really want to implement this type of behavior with checkboxes? Checkboxes already have a well-understood behavior from a user point of view, and having a seemingly random box become unchecked when a new one is checked will likely be very confusing or maybe even frustrating for the average user.

Maybe consider something like a listbox with add/remove buttons, where the design of the list gives the user a visual cue that there is a max of (say) four items. As a reference, I'm thinking something along the lines of the toolbar customizing dialog in IE.

Perhaps not the answer you were looking for, but something to consider.

+1  A: 

What I've done before is have a multicolumn selection menu like this:

           <---->

choices:selected choice-1-empty box- choice-2-empty box- choice-3-empty box- choice-4-empty box-                  

Then people could highlight a "choice-1" and hit the right button. Suddenly the second column would be populated by the items in the first. Then you can disable the arrow after 3 choices have been added, and pop up a message saying, "You may only select three choices." This makes far more sense compared to other options. It would be far easier for the user.

Sargun Dhillon
A: 

If you ask a user to pick form a list of options and limit the number of choices it is likely that the first choice is there primary choice.

e.g. Pick two, you will never have any of what you don't choose:

  • Money
  • Power
  • Sex
  • Excitement
  • Gadgets
  • Army of Coders.

Was your first choice you primary choice?

If you want to use check boxes, simply disable all the unchecked ones when the the second one is checked.

mat_geek
+1  A: 

Is it a good idea to unhook are rehook the event handlers like I did.

That depends.

Is it Windows Forms? Windows Forms run on top of the WinAPI which mean that event handler is really just a function called by the message dispatch loop in the main thread. As such the functions do not need to be re-entrant and it is "safe".

But, you must do your error handling and catch any exceptions like failed allocations within your event handler or your application will terminate.

mat_geek