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,234 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. Abdulhakim M. Elrhumi 351 Reputation points
    2021-01-15T20:58:35.837+00:00

    Hi

    public static void ClearValues(TableLayoutPanel tp)
    {
    foreach(Control item in tp.Controls)
    {
    if (item is TextBox) { item.Text = ""; }
    if (item is TextEdit) { item.Text = ""; }
    if (item is ComboBoxEdit) { item.Text = "";}
    if (item is DateEdit) { item.Text = ""; }
    }
    }

    Best Regards.
    Please remember to mark the replies as answers if they help.

    0 comments No comments

  2. Karen Payne MVP 35,036 Reputation points
    2021-01-15T23:34:09.697+00:00

    Hello @Art Hansen

    If you must use a for, don't continue.

    In this code sample, there is a CheckBox with a ComboBox and TextBox associated to the CheckBox by setting the Tag property of the TextBox and ComboBox to a int.

    CheckBox1: it's ComboBox tag is 1, TextBox is 1
    CheckBox2: it's ComboBox tag is 2, TextBox is 2

    Etc.

    Form code

    public partial class Form1 : Form  
    {  
        public Form1()  
        {  
            InitializeComponent();  
            Controls.OfType<CheckBox>().ToList()  
                .ForEach(cb => cb.CheckStateChanged += OnCheckStateChanged);  
        }  
      
        private void OnCheckStateChanged(object sender, EventArgs e)  
        {  
            var cb = (CheckBox) sender;  
            if (cb.Checked) return;  
              
            var index = Convert.ToInt32(Regex.Match(cb.Name, @"\d+").Value);  
            Controls.OfType<Control>()  
                .Where(c => c.Tag != null && Convert.ToInt32(c.Tag.ToString()) == index)  
                .ToList()  
                .ForEach(control => ControlExtensions.FindAndInvoke(control.GetType(), control));  
        }  
    }  
    

    Helper class

    public static class ControlHelpers  
    {  
        private static readonly Dictionary<Type, Action<Control>> ControlDefaults = new Dictionary<Type, Action<Control>>()  
        {  
            {typeof(TextBox), c => ((TextBox)c).Clear()}, {typeof(ComboBox), c => ((ComboBox)c).Text = ""}  
        };  
        public static void FindAndInvoke(Type type, Control control)  
        {  
            if (ControlDefaults.ContainsKey(type))  
            {  
                ControlDefaults[type].Invoke(control);  
            }  
        }  
      
    }  
    
    0 comments No comments

  3. Art Hansen 566 Reputation points
    2021-01-16T17:35:47.567+00:00

    Hello @Karen Payne MVP

    Thank you for the substantive and educational response. As you can probably tell I’m a bit of a novice in the C# world. I was unaware that degree of control over winform controls is possible; it opens up a whole new arena of possibilities. Unfortunately I’ve not been able to get the provided code to work.

    If you’ll bear with me I’d appreciate further dialogue on this topic. To share a bit more about the purpose of the form under discussion. It presents 10 rows of “data” with each row being controlled by the state of the leading checkbox (which you obviously understood). When a user initially accesses the form only the checkboxes are enabled. Selecting (checking) a lower level box “activates” all rows above and deselecting a higher level box “deactivates” all rows (if any) below it. If rows containing user input are deselected the deselected rows “revert” to initial condition. All action regarding the form controls is triggered by changes to the checkbox state. I am able to accomplish the objectives through brute force (only shows code for top/bottom checkboxes):

    // Checking any level checkox "activates" all checkboxes above it  
    private void placeLevel09CB_CheckedChanged(object sender, EventArgs e)  
    {  
     if (placeLevel09CB.Checked == false)  
     {  
     placeLabel09.Enabled = false;  
     placeLabel09.Font = new Font(  
     "Microsoft Sans Serif", 9.75F, FontStyle.Regular, GraphicsUnit.Point, ((byte)(0)));  
     placeLabel09.ForeColor = SystemColors.ControlText;  
     PlaceComboBox09.Enabled = false;  
     TypeComboBox09.Enabled = false;  
     PlaceComboBox09.Text = "";  
     TypeComboBox09.Text = "";  
     initialzeTypeDisplay09();  
     return;  
     }  
     placeLabel09.Enabled = true;  
     placeLabel09.Font = new Font(  
     "Microsoft Sans Serif", 8.75F, FontStyle.Bold, GraphicsUnit.Point, ((byte)(0)));  
     placeLabel09.ForeColor = Color.DarkSlateGray;  
     PlaceComboBox09.Enabled = true;  
     TypeComboBox09.Enabled = true;  
     placeLevel00CB.Checked = true;  
     placeLevel01CB.Checked = true;  
     placeLevel02CB.Checked = true;  
     placeLevel03CB.Checked = true;  
     placeLevel04CB.Checked = true;  
     placeLevel05CB.Checked = true;  
     placeLevel06CB.Checked = true;  
     placeLevel07CB.Checked = true;  
     placeLevel08CB.Checked = true;  
    }  
    // Unchecking any level checkox "deactivates" all checkboxes below it  
    private void placeLevel00CB_CheckedChanged(object sender, EventArgs e)  
    {  
     if (placeLevel00CB.Checked == false)  
     {  
     placeLevel01CB.Checked = false;  
     placeLevel02CB.Checked = false;  
     placeLevel03CB.Checked = false;  
     placeLevel04CB.Checked = false;  
     placeLevel05CB.Checked = false;  
     placeLevel06CB.Checked = false;  
     placeLevel07CB.Checked = false;  
     placeLevel08CB.Checked = false;  
     placeLevel09CB.Checked = false;  
     placeLabel00.Enabled = false;  
     placeLabel09.Font = new Font(  
     "Microsoft Sans Serif", 9.75F, FontStyle.Regular, GraphicsUnit.Point, ((byte)(0)));  
     placeLabel00.ForeColor = SystemColors.ControlText;  
     PlaceComboBox00.Enabled = false;  
     TypeComboBox00.Enabled = false;  
     PlaceComboBox00.Text = "";  
     TypeComboBox00.Text = "";  
     initialzeTypeDisplay00();  
     typeInstruction.Visible = true;  
     return;  
     }  
     placeLabel00.Enabled = true;  
     placeLabel09.Font = new Font(  
     "Microsoft Sans Serif", 8.75F, FontStyle.Bold, GraphicsUnit.Point, ((byte)(0)));  
     placeLabel00.ForeColor = Color.DarkSlateGray;  
     PlaceComboBox00.Enabled = true;  
     TypeComboBox00.Enabled = true;  
    }  
    

    My code is obviously not efficient at all and would be a nightmare to maintain (especially when compared to yours). Which is I why I started exploring looping mechanisms to generate the necessary control state statements and also why I’d like to get your code to work. It would be fair to say I’ve just got a glimmer of what the code is doing (specifically) and how it’s doing it …

    My questions or interpretations I'd appreciate being confirmed (or not) are:
    [from intro]
    “must use a for, don’t continue”: Do you mean if I’m required to use the for statement what I’m attempting cannot be accomplished via a “for”? Or don’t continue the for loop (which interpretation thoroughly confuses me)? Or something else entirely?

    CheckBox1: it's ComboBox tag is 1, TextBox is 1
    CheckBox2: it's ComboBox tag is 2, TextBox is 2

    I set the tag property (using form designer in VS) of each “row’s ” non-checkbox controls to that row’s int. How is the linkage between the “master” checkbox and its row’s “slave” controls is established or actioned? In [Form code | line 16]?

    [Form code | line 6]: Is this creating then populating a list in which there’s a line/list item for each occurrence for each checkbox control on the form along with each state change for the named control event?

    [Form code | line 15]: This appears to be setting the variable “index” of type var to the integer representation (?reference?) of each checkbox control.

    [Form code | line 16]: This appears to be getting (not in the C# “get” sense) the list of controls slaved to a given checkbox then executing the appropriate statement(s based the ControlExtension class, FindAndInvoke method.

    [Form code | line 19]: Is this actually supposed to be calling ControlHelpers. FindAndInvoke rather than ControlExtension.FindAndInvoke?

    [Helper class | line 3]: Creates a dictionary pairing control types with appropriate actionable statements.

    [Helper class | line 7]: FindAndInvoke method uses the ControlDefaults dictionary to define the framework to execute appropriate statements when called (and populated) by [Form code | line 16].

    Unless I’m really out in left field I think my biggest understand gap is how [Form code | line 6] and [Form code | line 16] are working together to establish and utilize the links set up based on the tags.

    Any further help will be much appreciated,
    Art


  4. Art Hansen 566 Reputation points
    2021-01-26T13:16:51.263+00:00

    @Karen Payne MVP Hey - your suggestions worked great! It reduced my code from approx.. 800 lines down to under 200, or over a 75% reduction in initial coding overhead which I suspect also translates into reduced maintenance costs. It's taken so long to get back because I've also done it using an array approach instead of the list approach we used here. I'm ambivalent about which approach is better; both approaches are relatively close in terms of lines of code.

    Thanks again for the superb help ....

    I'm posting completed code in case it's useful to some future novice. As a caveat this code does not include user input validation or connections to a SQL database where the data is stored since (a) it's off topic & (b) what's posted is already log enough.
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    using TMclassLibrary_framework.Models;
    using TMclassLibrary_framework.Connections;
    using System.Text.RegularExpressions;

    namespace TourneyManagerUI  
    {  
        public partial class PrizeEntry : Form  
        {  
            public PrizeEntry()  
            {  
                InitializeComponent();  
                RefreshTypeDisplay(Controls, 0);  
                // tableLayoutPanel1.Controls.OfType<CheckBox>().ToList()  
                //   .ForEach(cb => cb.CheckStateChanged += AnyCB_CheckedChanged);  
            }  
            private void AnyCB_CheckedChanged(object sender, EventArgs e)  
            {  
    		   if (_working)  
    		   {  
    			   return;  
    		   }  
                  
                int mstrIndex = Convert.ToInt32((sender as CheckBox).Tag);  
                if ((sender as CheckBox).Checked == true)  
                {  
                    string chkDirection = "Chking";  
                    HandleCheckboxes(mstrIndex, chkDirection);  
                }  
                else if ((sender as CheckBox).Checked == false)  
                {  
                    string chkDirection = "unChking";  
                    HandleCheckboxes(mstrIndex, chkDirection);  
                }  
            }  
            private void HandleCheckboxes(int mstrIndex, string chkDirection)  
            {  
                var checkBoxes = tableLayoutPanel1.Controls.OfType<CheckBox>().ToList();  
                if (chkDirection == "Chking")  
                {  
                    foreach (var checkBox in checkBoxes)  
                    {  
                        int cbIndex = Convert.ToInt32(checkBox.Tag);  
                        if (cbIndex <= mstrIndex)  
                        {  
                            checkBox.Checked = true;  
                            HandleLabels(mstrIndex, chkDirection);  
                            HandleComboboxes(mstrIndex, chkDirection);  
                        }  
                    }  
                }  
                else if (chkDirection == "unChking")  
                {  
                    foreach (var checkBox in checkBoxes)  
                    {  
                        int cbIndex = Convert.ToInt32(checkBox.Tag);  
                        if (cbIndex >= mstrIndex)  
                        {  
                            checkBox.Checked = false;  
                            HandleLabels(mstrIndex, chkDirection);  
                            HandleComboboxes(mstrIndex, chkDirection);  
                        }  
                    }  
                }  
            }  
            private void HandleLabels(int mstrIndex, string chkDirection)  
            {  
                var labels = tableLayoutPanel1.Controls.OfType<Label>().ToList();  
                if (chkDirection == "Chking")  
                {  
                    foreach (var label in labels)  
                    {  
                        int labelIndex = Convert.ToInt32(label.Tag);  
                        if (labelIndex <= mstrIndex)  
                        { label.Enabled = true; }  
                    }  
                }  
                else if (chkDirection == "unChking")  
                {  
                    foreach (var label in labels)  
                    {  
                        int labelIndex = Convert.ToInt32(label.Tag);  
                        if (labelIndex >= mstrIndex)  
                        { label.Enabled = false; }  
                    }  
                }  
            }  
            private void HandleComboboxes(int mstrIndex, string chkDirection)  
            {  
                var comboBoxes = tableLayoutPanel1.Controls.OfType<ComboBox>().ToList();  
                if (chkDirection == "Chking")  
                {  
                    foreach (var combobox in comboBoxes)  
                    {  
                        int comboboxIndex = Convert.ToInt32(combobox.Tag);  
                        if (comboboxIndex <= mstrIndex)  
                        { combobox.Enabled = true; }  
                    }  
                }  
                else if (chkDirection == "unChking")  
                {  
                    foreach (var combobox in comboBoxes)  
                    {  
                        int comboboxIndex = Convert.ToInt32(combobox.Tag);  
                        if (comboboxIndex >= mstrIndex)  
                        {  
                            combobox.Enabled = false;  
                            combobox.Text = null;  
                            RefreshTypeDisplay(Controls, mstrIndex);  
                        }  
                    }  
                }  
            }  
      
            private void RefreshTypeDisplay(Control.ControlCollection listControls, int mstrIndex)  
            {  
                for (int x = mstrIndex; x <= 10; x++)  
                {  
                    foreach (Control c in listControls)  
                    {  
                        if (c.Name == $"trophySelected0{x}") { c.Visible = false; }  
                        if (c.Name == $"percentSymb0{x}") { c.Visible = false; }  
                        if (c.Name == $"AmountTextBox0{x}") { c.Visible = false; c.Text = ""; }  
                        if (c.Name == $"PercentTextBox0{x}") { c.Visible = false; c.Text = ""; }  
                    }  
                }  
            }  
      
            private void AnyPrizeTypeCB_ComboBoxChanged(object sender, EventArgs e)  
            {  
                typeInstruction.Visible = false;  
                int mstrIndex = Convert.ToInt32((sender as ComboBox).Tag);  
                string sendingPTCB = (sender as ComboBox).Name;  
                string PrizeTypeSender = (string)(sender as ComboBox).SelectedItem;  
      
                HandlePTtextboxes(Controls, mstrIndex, sendingPTCB, PrizeTypeSender);  
            }  
            private void HandlePTtextboxes(Control.ControlCollection listControls, int mstrIndex, string sendingPTCB, string PrizeTypeSender)  
            {  
                foreach (Control c in listControls)  
                {  
                    string amtTB = "AmountTextBox0";  
                    string prcntTB = "PercentTextBox0";  
                    string prcntLab = "percentSymb0";  
                    string trphLab = "trophySelected0";  
      
                    if (PrizeTypeSender == "Amount of Prize")  
                    {  
                        if (c.Name == $"{amtTB}{mstrIndex}") { c.Visible = true; }  
                        if (c.Name == $"{prcntTB}{mstrIndex}") { c.Visible = false; }  
                        if (c.Name == $"{prcntLab}{mstrIndex}") { c.Visible = false; }  
                        if (c.Name == $"{trphLab}{mstrIndex}") { c.Visible = false; }  
                    }  
                    else if (PrizeTypeSender == "Percent of Prize Budget")  
                    {  
                        if (c.Name == $"{amtTB}{mstrIndex}") { c.Visible = false; }  
                        if (c.Name == $"{prcntTB}{mstrIndex}") { c.Visible = true; }  
                        if (c.Name == $"{prcntLab}{mstrIndex}") { c.Visible = true; }  
                        if (c.Name == $"{trphLab}{mstrIndex}") { c.Visible = false; }  
                    }  
                    else if (PrizeTypeSender == "Trophy")  
                    {  
                        if (c.Name == $"{amtTB}{mstrIndex}") { c.Visible = false; }  
                        if (c.Name == $"{prcntTB}{mstrIndex}") { c.Visible = false; }  
                        if (c.Name == $"{prcntLab}{mstrIndex}") { c.Visible = false; }  
                        if (c.Name == $"{trphLab}{mstrIndex}") { c.Visible = true; }  
                    }  
                }  
            }  
      
            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;  
            }  
        }  
    }  
      
    
    0 comments No comments