0
votes

Background: I am creating a simple winform app for personal use to query data I've logged via XML serialization. This data consists of Champions and their associated Race(es), Class(es), etc. My problem is occurring in a UserControl designed to add new Champions.

Problem: I am using DataGridViews to add 1 or more Race, Class, etc. to the new champion. Each of these properties are enum types. Each DataGridView is tied to one enum type, having one column of ComboBoxes to select desired values.

Example DataGridView for Race: (Sorry, I had a screenshot but don't have enough rep.)

[_____|_ChampRace________________]

[_ .... _|_Dragon__________________v_]

[__* __|________________________ v _]

The source for these DataGridViews are each tied to BindingLists of each enum type. When I select a value in the view (say ChampRace.Dragon), the BindingList only receives the default added value (ChampRace.None). Why isn't the binding list being updated as I desire?

Example Enums:

public enum ChampRace
{
    None,
    Angel,
    Beast,
    Dragon,
    Etc
}

Serialized class:

public class Champion
{
    [XmlAttribute]
    public string Name { get; set; }

    [XmlElement]
    public List<ChampRace> Races { get; set; }

    [XmlElement]
    public List<ChampClass> Classes { get; set; }

    [XmlElement]
    public List<Faction> Factions { get; set; }
}

UserControl code behind:

public partial class NewChampion : UserControl
{
    public NewChampion()
    {
        this.InitializeComponent();
        this.InitializeAllData();
    }

    public Master Master { get; set; }
    public BindingList<ChampRace> Races { get; set; }
    public BindingList<ChampClass> Classes { get; set; }
    public BindingList<Faction> Factions { get; set; }

    private void AddChampionButton_Click(object sender, EventArgs e)
    {
        Champion champion = new Champion();
        champion.Name = this.nameTextBox.Text;
        champion.Classes = new List<ChampClass>();
        champion.Factions = new List<Faction>();
        champion.Races = new List<ChampRace>();

        foreach (ChampClass cc in this.Classes)
        {
            champion.Classes.Add(cc);
        }

        foreach (ChampRace cr in this.Races)
        {
            champion.Races.Add(cr);
        }

        foreach (Faction f in this.Factions)
        {
            champion.Factions.Add(f);
        }

        System.Diagnostics.Debug.WriteLine("Break point here during debug to verify data...");

        // Note: At this point based on the screenshot, this.Races has one value.
        // Instead of the expected value "Dragon", the value is always "None".
    }

    private void InitializeAllData()
    {
        this.Races = new BindingList<ChampRace>();
        this.Classes = new BindingList<ChampClass>();
        this.Factions = new BindingList<Faction>();

        /*
         *  The following event handlers were added to show a new editable row in the dgView.
         *  Without them, no rows appear.
         */
        this.Classes.AddingNew += (s, e) =>
        {
            // e.NewObject = ChampClass.None;
        };

        this.Races.AddingNew += (s, e) =>
        {
            // e.NewObject = ChampRace.None;
        };

        this.Factions.AddingNew += (s, e) =>
        {
            // e.NewObject = Faction.None;
        };

        this.raceData.DataSource = this.Races;
        this.classData.DataSource = this.Classes;
        this.factionData.DataSource = this.Factions;

        this.SetupDataOptions<ChampRace>(ref this.raceData);
        this.SetupDataOptions<ChampClass>(ref this.classData);
        this.SetupDataOptions<Faction>(ref this.factionData);
    }

    private void SetupDataOptions<T>(ref DataGridView data)
        where T : struct, IComparable, IFormattable, IConvertible
    {
        if (!typeof(T).IsEnum)
        {
            throw new ArgumentException("Type must be an enumeration");
        }

        DataGridViewComboBoxColumn combo = new DataGridViewComboBoxColumn();
        combo.ValueType = typeof(T);
        combo.DataSource = Enum.GetValues(typeof(T));
        combo.DataPropertyName = typeof(T).ToString();
        combo.Name = typeof(T).ToString().Split('.').Last();

        data.AutoGenerateColumns = false;
        data.AutoSize = true;
        data.Columns.Add(combo);
    }
}

This is the first time I've used DataGridViews and BindingLists, so I'm hoping it's a simple-overlooked mistake.

The most relevant research I found which got me here:

  1. Combobox doesn’t set default value if value is an enum
  2. Making a DataGridView column editable after binding it to a BindingList
  3. DataGridView doesn't update Enum in BindingList
  4. DataGridView Default Error on ComboBox Column
1

1 Answers

2
votes

I recreated the code in a test program and found that the BindingList behaves as you state. It keeps a list of the enum objects but leaves it at the first enumeration. Since the binding list at least has an enumeration object I was able to use a DataGridView event to accomplish the data update. In the DataGridView there is an event CellValueChanged. I implemented the event as follows:

private void DataGridView_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
      this.bindingList[e.RowIndex] = (myEnumeration)DataGridView[e.ColumnIndex, e.RowIndex].Value;
}

This puts the value in the BindingList with just a few extra lines of code.