knguyen158
knguyen158

Reputation: 33

How to loop through check boxes and assign enumeration values when boxes are checked?

I have a group of check boxes, to be precise there are 3 boxes. It works for me when using if statement but I wonder there is a way to loop through check boxes and assign enumeration values when a box is checked or more.

The code looks like this:

if (chkTomato.Checked && !chkLettuce.Checked && !chkCarrot.Checked)
{
    cart.VegChosen = Veggies.Tomato;
}
else if (!chkTomato.Checked && chkLecctuce.Checked && !chkCarrot.Checked)
{
    cart.VegChosen = Veggies.Lecctuce;
}
else if (!chkTomato.Checked && !chkLecctuce.Checked && chkCarrot.Checked)
{
    cart.VegChosen = Veggies.Carrot;
}
else if (chkTomato.Checked && chkLettuce.Checked && chkCarrot.Checked)
{
    cart.VegChosen = Veggies.All;
}
else if (chkTomato.Checked && chkLettuce.Checked && !chkCarrot.Checked)
{
    cart.VegChosen = Veggies.TomatoAndLettuce;
}
else if (chkTomato.Checked && !chkLettuce.Checked && chkCarrot.Checked)
{
    cart.VegChosen = Veggies.TomatoAndCarrot;
}
else if (!chkTomato.Checked && chkLettuce.Checked && chkCarrot.Checked)
{
    cart.VegChosen = Veggies.LettuceAndCarrot;
}
else
{
    cart.VegChosen = Veggies.None;
}

I want to find out a way to loop it in case there are more than just 3 check boxes, the if statement would be very long.

Thank you!

Upvotes: 3

Views: 367

Answers (3)

Caius Jard
Caius Jard

Reputation: 74700

Once you e implemented Llamas answer of making a flags enum you can put all your checkboxes in a groupbox and do this:

var veg = Veggies.None:
groupbox.Controls
  .OfType<CheckBox>()
  .Where(c => c.Checked)
  .ToList()
  .ForEach(c => veg |= Enum.Parse<Veggies>(c.Name[3..]));

Then all you have to do is add more enum members and add more checkboxes where the checkbox name is like "chkXXX" where xxx is the name of the enum member

This is a looping construct: it gets all the controls on the groupbox and filters to only those of type checkbox, then reduces it to only checked checkboxes. It turns this into a list (so we can foreach it, because foreach is a list thing not a LINQ thing). Foreach will visit every checkbox and ask if it is checked, it will OR the existing flags enum value with the result of parsing the checkbox name (dropping the first 3 chars) to a Veggies. At the end of it your veg variable will represent all the checkboxes that were Checked

Note that using ranges and even Enum.Parse<T> required a fairly modern version of c# - if you're running a version that doesn't support ranges you can use Substring(3). You should make sure that all your checkbox names are well aligned with your enum names. If you want to get really trick you could create your checkboxes dynamically by enumerating the Enum and putting the enum value as the checkbox Tag when you add the control to the form (dynamically, in incrementing positions) this way your form will just adapt automatically to however many enum members you have

Upvotes: 3

Prime
Prime

Reputation: 2482

Create the enum like so:

enum Veggies {
    Tomato = 1 << 0,
    Lettuce = 1 << 1,
    Carrot = 1 << 2,
    All = Tomato | Lettuce | Carrot

}

This makes the value of Veggies.Tomato = 1 which is 0000 0001 in bits, Veggies.Lettuce = 2 which is 0000 0010, and Veggies.Carrot = 4 which is 0000 0100.

It is important to have the enum values as bit-shifted 1's (powers of 2) so that you can combine two enum values later as an int like I've done with Veggies.All, which is 0000 0111.

Change VegChosen to an int, then simply change your code to something like this to bitwise-or the enum value into the VegChosen int:

cart.VegChosen = 0;

if(chkTomato.Checked) cart.VegChosen |= Veggies.Tomato;
if(chkLettuce.Checked) cart.VegChosen |= Veggies.Lettuce;
if(chkCarrot.Checked) cart.VegChosen |= Veggies.Carrot;

Later if you want to test what veggies were chosen from cart.VegChosen you can bitwise-and with one of the enum values and check if it's 0 like so:

if((cart.VegChosen & Veggies.Carrot) != 0)
{
    //... cart.VegChosen contains Veggies.Carrot
}
if((cart.VegChosen & Veggies.Lettuce) != 0)
{
    //... cart.VegChosen contains Veggies.Carrot
}
//etc

This is typically ~50% faster/more performant than Enum.HasFlag() because HasFlag() contains various sanity checks, but if you're not programming specifically for performance it's better to use what is easier to use and read and in that respect I would recommend Llama's answer.

Upvotes: 3

ProgrammingLlama
ProgrammingLlama

Reputation: 38850

While this doesn't use loops, I expect this is what you're trying to achieve. Assuming your enum is declared like this:

[Flags]
public enum Veggies
{
    None = 0,
    Tomato = 1,
    Lettuce = 2,
    Carrot = 4,
    TomatoAndLettuce = Tomato | Lettuce,
    TomatoAndCarrot = Tomato | Carrot,
    LettuceAndCarrot = Lettuce | Carrot,
    All = Tomato | Lettuce | Carrot
}

Then you should be able to use a similar bitwise approach to assign values:

Veggies selectedVeggies = Veggies.None;
if (chkTomato.Checked)
{
    selectedVeggies = selectedVeggies | Veggies.Tomato;
}

if (chkLettuce.Checked)
{
    selectedVeggies = selectedVeggies | Veggies.Lettuce;
}

if (chkCarrot.Checked)
{
    selectedVeggies = selectedVeggies | Veggies.Carrot;
}

cart.VegChosen = selectedVeggies;

The net result of this will be the same as your current set of if statements. The reason we use 1, 2, 4, etc. for the enum values is because there isn't overlap between them when rendered in binary (1 is 001, 2 is 010, 4 is 100, etc.) so that specific bit can only identify that one enum value.

Also note that declarations such as TomatoAndLettuce and TomatoAndCarrot are perhaps also unnecessary, since you can use Enum.HasFlag().

For example:


var selectedVeggies = Veggies.Tomato | Veggies.Carrot;
// or var selectedVeggies = Veggies.TomatoAndCarrot; // effectively the same as above

if (selectedVeggies.HasFlag(Veggies.Tomato))
{
    cart.Add(new Tomato());
}

if (selectedVeggies.HasFlag(Veggies.Carrot))
{
    cart.Add(new Carrot());
}

// cart ends up with a Tomato and a Carrot

Further reading: What does the bitwise or | operator do?

Upvotes: 5

Related Questions