Build List utilizing for loop not populating list

Art Hansen 566 Reputation points
2021-01-15T18:08:42.9+00:00

Hello - I'm trying to clear values associated with a checkbox list when a checkbox changes state to unchecked. The winform layout is: checkbox -> label -> combobox -> combobox-> textbox with 10 checkboxes. I can accomplish it thru brute force but am attempting to use a "for" loop to build a list of statements to clear appropriate form controls. I've attempted various approaches to no avail. This is where my code currently stands:

        private List<string> flushForm00()
        {
            var flushFields = new List<string>();
           //flushFields.Clear();

            if (placeLevel00CB.Checked == false)
            {   for (int i = 0; i < 10; i++) 
                {   string flushEachN = $"PlaceComboBox0{i}.Text = \"\";";
                    //string flushEachT = $"TypeComboBox0{i}.Text = \"\";";
                    Console.WriteLine(flushEachN);
                    Console.WriteLine($"TypeComboBox0{i}.Text = \"\";");
                    flushFields.Add(flushEachN);
                    flushFields.Add($"TypeComboBox0{i}.Text = \"\";");
                }
                for (int i = 1; i < 10; i++)
                {   string flushEachC = $"placeLevel0{i}.Checked = false;";
                    flushFields.Add(flushEachC);
                    Console.WriteLine(flushEachC);
                }
            }

            Console.WriteLine(flushFields);
            return flushFields;
        }

The loop is functioning properly as can be seen from console writes:
[OUTPUT]
PlaceComboBox00.Text = "";
TypeComboBox00.Text = "";
...
PlaceComboBox09.Text = "";
TypeComboBox09.Text = "";
placeLevel01.Checked = false;
placeLevel02.Checked = false;
...
placeLevel08.Checked = false;
placeLevel09.Checked = false;
System.Collections.Generic.List`1[System.String]

I've previously used the listName.Add("text") approach to populate a list so I'm at a loss to understand why this isn't working.

Thank you in advance for your time & expertise,
Art

C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
10,277 questions
0 comments No comments
{count} votes

Accepted answer
  1. Karen Payne MVP 35,036 Reputation points
    2021-01-18T13:09:53.317+00:00

    Hello @Art Hansen

    The following code sample uses a Tablelayoutpanel (secondary thought was a DataGridView formatted to look the same as the TableLayoutPanel but that takes more code). Each control has it's tag property set, first row the CheckBox and two ComboBox controls tag is 0, moving on to the last row the tag is 9. This should get you going.

    Full source code

    Extensions class

    using System;  
    using System.Text.RegularExpressions;  
      
    namespace ClearControlsWindowsForms.Extensions  
    {  
        public static class ControlHelpers  
        {  
            public static int GetInt(this string sender) => Convert.ToInt32(Regex.Match(sender, @"\d+\.*\d*").Value) - 1;  
        }  
    }  
    

    Form code

    using ClearControlsWindowsForms.Extensions;  
    using System;  
    using System.Collections.Generic;  
    using System.ComponentModel;  
    using System.Data;  
    using System.Diagnostics;  
    using System.Drawing;  
    using System.Linq;  
    using System.Text;  
    using System.Threading.Tasks;  
    using System.Windows.Forms;  
      
    namespace ClearControlsWindowsForms  
    {  
        public partial class MainForm : Form  
        {  
            public MainForm()  
            {  
                InitializeComponent();  
      
                tableLayoutPanel1.Controls.OfType<CheckBox>().ToList()  
                    .ForEach(cb => cb.CheckStateChanged += OnCheckStateChanged);  
            }  
      
            private void OnCheckStateChanged(object sender, EventArgs e)  
            {  
                if (_working)  
                {  
                    return;  
                }  
                  
                var cb = (CheckBox)sender;  
                if (cb.Checked) return;  
                  
                var checkBoxIndex = cb.Name.GetInt() ;  
                var checkBoxes = tableLayoutPanel1.Controls.OfType<CheckBox>().ToList();  
      
                foreach (var checkBox in checkBoxes)  
                {  
                    var cbIndex = checkBox.Name.GetInt();  
                    if (cbIndex <= checkBoxIndex)  
                    {  
                        checkBox.Checked = true;  
                          
                    }  
                    else  
                    {  
                        checkBox.Enabled = false;  
                        var tagIndex = Convert.ToInt32(checkBox.Tag);  
                          
                        var controls = tableLayoutPanel1  
                            .Controls.OfType<ComboBox>()  
                            .Where(c => Convert.ToInt32(c.Tag) == tagIndex);  
                          
                        foreach (var comboBox in controls)  
                        {  
                            comboBox.Enabled = false;  
                        }  
                          
                    }  
                }  
      
            }  
      
            private bool _working;  
            private void ResetButton_Click(object sender, EventArgs e)  
            {  
                _working = true;  
                  
                foreach (var control in tableLayoutPanel1.Controls.OfType<ComboBox>())  
                {  
                    control.Enabled = true;  
                }  
                  
                foreach (var control in tableLayoutPanel1.Controls.OfType<CheckBox>())  
                {  
                    control.Enabled = true;  
                    control.Checked = false;  
                }  
      
                _working = false;  
            }  
        }  
      
      
    }  
    

    Note in the screenshot below the ComboBoxes are not populated an the reset button brings everything back as if the app just started.
    57695-tablelayoutpanel.png


6 additional answers

Sort by: Most helpful
  1. Karen Payne MVP 35,036 Reputation points
    2021-01-26T14:32:37.103+00:00

    I'm using an answer as a comment is limited. Note the downside to the following is it's not easy to use when you may want to replace existing controls, works best when starting new.

    Class project with controls

    Good to hear this. One more thing a tad off topic is to consider using custom controls that has a property of type int in place of Tag.

    For a CheckBox

    public class CheckBoxCustom : CheckBox  
    {  
        [Category("Behavior"), Description("Identifier")]  
        public int Id { get; set; }  
        public bool HasId => Id > 0;  
    }  
    

    Same for TextBox

    public class TextBoxCustom : TextBox  
    {  
        [Category("Behavior"), Description("Identifier")]  
        public int Id { get; set; }  
        public bool HasId => Id > 0;  
    }  
    

    The Id property may be set in code

    checkBoxCustom1.Id = 12;  
    

    Or in the property window
    60616-11111111111.png

    The default value is 0 although you can set the default value. If left as 0 a check can be done to see if it's been set.

    if (textBoxCustom1.HasId)  
    {  
        MessageBox.Show($"Id is {textBoxCustom1.Id}");  
    }  
    else  
    {  
        MessageBox.Show("No id set for text box, press OK to set");  
        textBoxCustom1.Id = 1;  
    }  
    

    Alternately, Id can be nullable int thus a minor change.

    public class CheckBoxCustom : CheckBox  
    {  
        [Category("Behavior"), Description("Identifier")]  
        public int? Id { get; set; }  
        public bool HasId => Id.HasValue;  
    }  
    

    Still works the same.

    if (checkBoxCustom1.HasId)  
    {  
        MessageBox.Show($"Id is {checkBoxCustom1.Id}");  
    }  
    else  
    {  
        MessageBox.Show("No id");  
    }  
    

    In closing in the above code HasId will show in the property window (unwanted) so that can be handled and also to show other types can be used I added a do nothing per-say Comment property.

    public class CheckBoxCustom : CheckBox  
    {  
        [Category("Behavior"), Description("Identifier")]  
        public int? Id { get; set; }  
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]  
        public bool HasId => Id.HasValue;  
        [Category("Behavior"), Description("Comment")]  
        public string Comment { get; set; }  
    }  
    

  2. Karen Payne MVP 35,036 Reputation points
    2021-01-29T15:36:49.667+00:00

    First option, write a component which implements IExtenderProvider e.g. this sample I had for another question. Add to a form and each control has Custom1 property in the control property window. Here it's a string but you can use any type, also this is for all controls, you might only want specific type of controls.

    using System.Collections;  
    using System.ComponentModel;  
    using System.Windows.Forms;  
      
    namespace ExtenderSample  
    {  
        [ProvideProperty("Custom1", typeof(Control))]  
        public class ControlExtender : Component, IExtenderProvider  
        {  
            private Hashtable _controls = new Hashtable();  
            public bool CanExtend(object extendee)  
            {  
                return extendee is Control;  
            }  
      
            public string GetCustom1(Control control)  
            {  
                if (_controls.ContainsKey(control))  
                {  
                    return (string)_controls[control];  
                }  
      
                return null;  
            }  
            public void SetCustom1(Control control, string value)  
            {  
                if (string.IsNullOrWhiteSpace(value))  
                {  
                    _controls.Remove(control);  
                }  
                else  
                {  
                    _controls[control] = value;  
                }  
            }  
        }  
      
    }  
      
    

    61896-11111111111.png

    MessageBox.Show(controlExtender1.GetCustom1(textBox1));  
    

    Otherwise a extension method e.g.

    public static class Extensions  
    {  
        public static int Identifier(this TextBox sender)  
        {  
            if (sender.Tag is null)  
            {  
                return -1;  
            }  
            else  
            {  
                if (int.TryParse(sender.Tag.ToString(), out var value))  
                {  
                    return value;  
                }  
                else  
                {  
                    return -1;  
                }  
            }  
        }  
    }  
    

    Usage

    var id = textBox1.Identifier();  
    if (id > -1)  
    {  
          
    }