Binding to enums

A swedish guy had a task to complete: he had 2 listboxes containing mammals and birds in one of the and some kind of mammals and birds in the other. Yes, we should show only relevant results in the second one, depending on the selection. After selecting mammal, we should list all the mammals in the second listbox. Selecting one animal in the second one, we should show some details in some textboxes. Of course we always have to clear selections and textboxes if needed.

Unfortunately (or not) he wanted to use enums for animal “types” and for mammals and birds.

Let’s create a new WinForm application and place some controls on it:

  • 2 listboxes
  • 2 buttons
  • 2 groupboxes
  • 6-6 labels and textboxes in each groupbox

The form should look like this:

I set “Enable” property of the groupboxes to false and the “ReadOnly” property of the textboxes to true.

By request, let’s create those enums:

namespace Animals
{
    // https://en.wikipedia.org/wiki/Cockatoo
    // https://en.wikipedia.org/wiki/True_parrot
    // https://en.wikipedia.org/wiki/Dog
    // https://en.wikipedia.org/wiki/Cat

    enum Animals
    {
        Birds,
        Mammals
    }

    enum Mammals
    {
        Dog,
        Cat
    }

    enum Birds
    {
        Parrot,
        Cockatoo
    }

    enum Kingdom
    {
        Animalia
    }

    enum Phylum
    {
        Chordata
    }

    enum Class
    {
        Aves,
        Mammalia
    }

    enum Order
    {
        Psittaciformes,
        Carnivora
    }

    enum Suborder
    {
        Caniformia,
        Feliformia
    }

    enum Superfamily
    {
        Cacatuoidea,
        Psittacoidea
    }

    enum Family
    {
        Cacatuidae,
        Canidae,
        Felidae
    }
}

To show the information about the selected animal, I created 4 classes with some basic, readonly property:

namespace Animals
{
    class Cockatoo
    {
        public Kingdom Kingdom { get; }
        public Phylum Phylum { get; }
        public Class Class { get; }
        public Order Order { get; }
        public Superfamily Superfamily { get; }
        public Family Family { get; }

        public Cockatoo()
        {
            Kingdom = Kingdom.Animalia;
            Phylum = Phylum.Chordata;
            Class = Class.Aves;
            Order = Order.Psittaciformes;
            Superfamily = Superfamily.Cacatuoidea;
            Family = Family.Cacatuidae;
        }
    }

    class Parrot
    {
        public Kingdom Kingdom { get; }
        public Phylum Phylum { get; }
        public Class Class { get; }
        public Order Order { get; }
        public Superfamily Superfamily { get; }

        public Parrot()
        {
            Kingdom = Kingdom.Animalia;
            Phylum = Phylum.Chordata;
            Class = Class.Aves;
            Order = Order.Psittaciformes;
            Superfamily = Superfamily.Psittacoidea;
        }
    }

    class Dog
    {
        public Kingdom Kingdom { get; }
        public Phylum Phylum { get; }
        public Class Class { get; }
        public Order Order { get; }
        public Suborder Suborder { get; }
        public Family Family { get; }

        public Dog()
        {
            Kingdom = Kingdom.Animalia;
            Phylum = Phylum.Chordata;
            Class = Class.Mammalia;
            Order = Order.Carnivora;
            Suborder = Suborder.Caniformia;
            Family = Family.Canidae;
        }
    }

    class Cat
    {
        public Kingdom Kingdom { get; }
        public Phylum Phylum { get; }
        public Class Class { get; }
        public Order Order { get; }
        public Suborder Suborder { get; }
        public Family Family { get; }

        public Cat()
        {
            Kingdom = Kingdom.Animalia;
            Phylum = Phylum.Chordata;
            Class = Class.Mammalia;
            Order = Order.Carnivora;
            Suborder = Suborder.Feliformia;
            Family = Family.Felidae;
        }
    }
}

Thank you WikiPedia for helping in the categorization.

In this kind of applications I like subscribing to events manually and not by the helping of the designer (double clicking on the event).

The form constructor goes like this:

public Form1()
{
    InitializeComponent();

    FillAnimals();

    lsbAnimals.SelectedIndexChanged += LsbAnimals_SelectedIndexChanged;
}

You may notice a new method called FillAnimals(). This will fill up and bind the listbox to the enum of Animals. While binding it’s useless to invoke event method, that’s why the subscribtion goes after it.

private void FillAnimals()
{
    lsbAnimals.DataSource = Enum.GetValues(typeof(Animals));
    lsbAnimals.ClearSelected();
}

The first line would invoke the SelectedIndexChanged event 2 times and the ClearSelected() method once more. If you bring the subscription line before the FillAnimals() method, blinking may occour, you can check it.

One more method may help us: resetting all the textboxes and groupboxes:

private void ClearInformationBoxes()
{
    foreach (Control control in grbMammal.Controls)
    {
        if (control is TextBox)
        {
            ((TextBox)control).Text = "";
        }
    }
    foreach (Control control in grbBird.Controls)
    {
        if (control is TextBox)
        {
            ((TextBox)control).Text = "";
        }
    }

    grbMammal.Enabled = false;
    grbBird.Enabled = false;
}

We walk through in both groupboxes and checking all the Controls if they are TextBoxes. If so, we clear the text. In the end, we disable both groupboxes.

The simpliest way to subscribe to the Click events of the buttons is to double click on the event name in designer mode and that will generate both methods. Each click events will clear the selection of the corresponding listboxes.

private void btnClearAnimalsSelection_Click(object sender, EventArgs e)
{
    lsbAnimals.ClearSelected();
}

private void btnClearAnimalFamilySelection_Click(object sender, EventArgs e)
{
    lsbAnimalFamily.ClearSelected();
}

And finally let’s subscribe with the listboxes to their SelectedIndexChanged event. The first method will look like this:

private void LsbAnimals_SelectedIndexChanged(object sender, EventArgs e)
{
    lsbAnimalFamily.SelectedIndexChanged -= LsbAnimalFamily_SelectedIndexChanged;

    if (lsbAnimals.SelectedItem != null)
    {
        switch ((Animals)lsbAnimals.SelectedItem)
        {
            case Animals.Birds:
                lsbAnimalFamily.DataSource = Enum.GetValues(typeof(Birds));
                break;
            case Animals.Mammals:
                lsbAnimalFamily.DataSource = Enum.GetValues(typeof(Mammals));
                break;
        }

        lsbAnimalFamily.ClearSelected();
    }
    else
    {
        lsbAnimalFamily.DataSource = null;
    }

    ClearInformationBoxes();

    lsbAnimalFamily.SelectedIndexChanged += LsbAnimalFamily_SelectedIndexChanged;
}

To not seeing those ugly blinkings, we first unsubscribe from the other listbox SelectedIndexChanged event. Then we check if any item is selected. If yes, we bind the second listbox to the corresponding enum. If no, we clear the second listbox. Finally we clear all the textboxes and subscribe again to the SelectedIndexChanged event of the second listbox.

The SelectedIndexChanged of the second listbox will fill the textboxes with the information of the selected animal. This way:

private void LsbAnimalFamily_SelectedIndexChanged(object sender, EventArgs e)
{
    ClearInformationBoxes();

    if (lsbAnimalFamily.SelectedItem != null)
    {
        switch (lsbAnimalFamily.SelectedItem)
        {
            case Mammals.Cat:
                Cat cat = new Cat();
                txbClassMammal.Text = cat.Class.ToString();
                txbFamilyMammal.Text = cat.Family.ToString();
                txbKingdomMammal.Text = cat.Kingdom.ToString();
                txbOrderMammal.Text = cat.Order.ToString();
                txbPhylumMammal.Text = cat.Phylum.ToString();
                txbSuborderMammal.Text = cat.Suborder.ToString();
                grbMammal.Enabled = true;
                break;
            case Mammals.Dog:
                Dog dog = new Dog();
                txbClassMammal.Text = dog.Class.ToString();
                txbFamilyMammal.Text = dog.Family.ToString();
                txbKingdomMammal.Text = dog.Kingdom.ToString();
                txbOrderMammal.Text = dog.Order.ToString();
                txbPhylumMammal.Text = dog.Phylum.ToString();
                txbSuborderMammal.Text = dog.Suborder.ToString();
                grbMammal.Enabled = true;
                break;
            case Birds.Cockatoo:
                Cockatoo cockatoo = new Cockatoo();
                txbClassBird.Text = cockatoo.Class.ToString();
                txbFamilyBird.Text = cockatoo.Family.ToString();
                txbKingdomBird.Text = cockatoo.Kingdom.ToString();
                txbOrderBird.Text = cockatoo.Order.ToString();
                txbPhylumBird.Text = cockatoo.Phylum.ToString();
                txbSuperfamilyBird.Text = cockatoo.Superfamily.ToString();
                grbBird.Enabled = true;
                break;
            case Birds.Parrot:
                Parrot parrot = new Parrot();
                txbClassBird.Text = parrot.Class.ToString();
                txbKingdomBird.Text = parrot.Kingdom.ToString();
                txbOrderBird.Text = parrot.Order.ToString();
                txbPhylumBird.Text = parrot.Phylum.ToString();
                txbSuperfamilyBird.Text = parrot.Superfamily.ToString();
                grbBird.Enabled = true;
                break;
        }
    }
}

The complete zipped solution can be downloaded here: Animals listbox tutorial (52 downloads)