在 C# 中使用 "for" 循环构建列表时,.Add() 方法不起作用
.Add() method not working when building a list utilizing a "for" loop in C#
我是 C# 新手。我正在处理一个显示 10 行“数据”的表单,每行都由前导复选框的状态控制。行布局为:复选框+标签+组合框+组合框+文本框。当用户最初访问表单时,只有复选框被启用。选择(选中)复选框可启用该行中的控件。选择较低级别的框会“激活”其上方的所有行,取消选择较高级别的框会“停用”其下方的所有行(如果有)。如果取消选择包含用户输入的行,则取消选择的行“恢复”到初始状态。有关表单控件的所有操作都由复选框状态的更改触发。我能够通过蛮力实现目标(仅显示一个复选框状态更改的代码,但所有复选框更改的代码都相似):
private void placeLevelCheckBox09_.CheckedChanged(object sender, EventArgs e)
{
if (placeLevelCheckBox09.Checked == false)
{
placeLabel09.Enabled = false;
PlaceComboBox09.Enabled = false;
TypeComboBox09.Enabled = false;
PlaceComboBox09.Text = "";
TypeComboBox09.Text = "";
initialzeTypeDisplay09();
return;
}
placeLabel09.Enabled = true;
PlaceComboBox09.Enabled = true;
TypeComboBox09.Enabled = true;
placeLevelCheckBox00.Checked = true;
placeLevelCheckBox01.Checked = true;
placeLevelCheckBox02.Checked = true;
placeLevelCheckBox03.Checked = true;
placeLevelCheckBox04.Checked = true;
placeLevelCheckBox05.Checked = true;
placeLevelCheckBox06.Checked = true;
placeLevelCheckBox07.Checked = true;
placeLevelCheckBox08.Checked = true;
}
我的代码看起来笨拙且效率低下,所以我试图 'tighten' 使用某种循环机制来生成和 execute/invoke 适当的语句。
我尝试了一种在 Microsoft 网站上建议的方法,该方法基于在 public Form() 块中声明的 ToList().ForEach() 语句,然后使用标签来建立我认为的父项-行的复选框与该行上的其他控件之间的子关系。我无法让它工作。
我现在创建了一个在复选框状态更改时调用的方法(它目前只处理单个复选框更改,但我假设我可以使用嵌套的“if”语句获得处理所有复选框的方法and/or 引入额外的变量):
private List<string> flushForm00()
{
var flushFields = new List<string>();
flushFields.Clear();
if (placeLevelCheckBox00.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 = $"placeLevelCheckBox0{i}.Checked = false;";
flushFields.Add(flushEachC);
Console.WriteLine(flushEachC);
}
}
从 Control.WriteLine 输出
中可以看出,循环正在按预期进行迭代
PlaceComboBox00.Text = "";
TypeComboBox00.Text = "";
...
PlaceComboBox09.Text = "";
TypeComboBox09.Text = "";
placeLevelCheckBox01.Checked = false;
...
placeLevelCheckBox09.Checked = false;
System.Collections.Generic.List`1[System.String]
从输出中也可以看出,列表“flushFields”仍然是空的。我试过在方法声明行上使用 static/void 但没有成功。
我已经在其他地方成功使用了 .Add() 方法,但没有在循环中使用。为什么列表没有被填充,循环代码是否“可修复”,如果是如何?
我很抱歉这已经发展了这么久。
预先感谢您的时间和专业知识。
List 没有任何方法可以将列表中包含的每个单个对象表示为字符串。即使对象本身是字符串。因此,当您在其上调用 ToString()(并且 Console.WriteLine 调用 ToString())时,列表会响应并返回对象 class 名称。有不同的方法可以解决您的问题。首先,List 有一个 Count 属性 告诉你元素的数量,然后有一个名为 Any 的 IEnumerable 扩展 returns 一个布尔值(如果至少有一个元素则为真)或者你可以写您自己的 class 派生自 List<string>
并实现了覆盖基本功能的不同版本的 ToString。
说到这里,我认为您确实需要数组。不要按名称处理每个单个元素,而是将代码需要更改的所有内容放在一个数组中。 CheckBox 数组,Labels 数组,TextBox 数组等。数组的每个元素都应存储在与您的索引逻辑相关的渐进位置。
因此,以与复选框相关的示例为例,您可以(在全局范围内)声明一个 CheckBoxes
数组
public class Form1 : Form
{
private CheckBox[] _myChecks;
public Form1():base()
{
InitializeComponent();
// Notice that this doesn't create another set of checkboxes.
// It just stores the reference to the checkboxes created by
// InitializeComponent in an array
_myChecks = new CheckBox[]
{
placeLevelCheckBox00,placeLevelCheckBox01,placeLevelCheckBox02,
placeLevelCheckBox03,placeLevelCheckBox04,placeLevelCheckBox05,
placeLevelCheckBox06,placeLevelCheckBox07,placeLevelCheckBox08
};
}
此时如果您需要激活从索引 0 到 4 的所有内容并停用您可以编写的其他所有内容
private void HandleCheckboxes(int activateLimit)
{
for(int x = 0; x <= activateLimit; x++)
_myChecks[x].Checked = true;
for(int x = activateLimit+1; x < _myChecks.Length; x++)
_myChecks[x].Checked = false;
}
当然,您可以扩展这些示例,添加其他数组并在 activate/deactivate 复选框数组
的同一循环中应用所需的逻辑
现在是最后一点。您如何在不查看每个复选框的变量名称的情况下知道要用于 activation/deactivation 的索引位置?
您可以使用每个复选框的标签 属性 来做到这一点。这个 属性 可以被程序员用来存储任何类型的数据。您可以在那里存储用于该特定复选框的索引(您可以在每个复选框 属性 列表中的表单设计器中执行此操作)
placeLevelCheckBox00.Tag = 0;
placeLevelCheckBox01.Tag = 1;
// and so on for the others
现在一切就绪,可以进行重大改进。
您可以为转到每个复选框 属性 window 的每个复选框设置相同的事件处理程序,然后为事件列表中的 CheckedChanged 事件选择相同的处理程序。你不需要十个不同的事件处理程序做同样的事情
private void placeLevelCheckBox_CheckedChanged(object sender, EventArgs e)
{
// Here sender is the checkbox clicked. Just need to cast it
int activateLimit = Convert.ToInt32((sender as CheckBox).Tag);
HandleCheckboxes(activateLimit);
}
我是 C# 新手。我正在处理一个显示 10 行“数据”的表单,每行都由前导复选框的状态控制。行布局为:复选框+标签+组合框+组合框+文本框。当用户最初访问表单时,只有复选框被启用。选择(选中)复选框可启用该行中的控件。选择较低级别的框会“激活”其上方的所有行,取消选择较高级别的框会“停用”其下方的所有行(如果有)。如果取消选择包含用户输入的行,则取消选择的行“恢复”到初始状态。有关表单控件的所有操作都由复选框状态的更改触发。我能够通过蛮力实现目标(仅显示一个复选框状态更改的代码,但所有复选框更改的代码都相似):
private void placeLevelCheckBox09_.CheckedChanged(object sender, EventArgs e)
{
if (placeLevelCheckBox09.Checked == false)
{
placeLabel09.Enabled = false;
PlaceComboBox09.Enabled = false;
TypeComboBox09.Enabled = false;
PlaceComboBox09.Text = "";
TypeComboBox09.Text = "";
initialzeTypeDisplay09();
return;
}
placeLabel09.Enabled = true;
PlaceComboBox09.Enabled = true;
TypeComboBox09.Enabled = true;
placeLevelCheckBox00.Checked = true;
placeLevelCheckBox01.Checked = true;
placeLevelCheckBox02.Checked = true;
placeLevelCheckBox03.Checked = true;
placeLevelCheckBox04.Checked = true;
placeLevelCheckBox05.Checked = true;
placeLevelCheckBox06.Checked = true;
placeLevelCheckBox07.Checked = true;
placeLevelCheckBox08.Checked = true;
}
我的代码看起来笨拙且效率低下,所以我试图 'tighten' 使用某种循环机制来生成和 execute/invoke 适当的语句。
我尝试了一种在 Microsoft 网站上建议的方法,该方法基于在 public Form() 块中声明的 ToList().ForEach() 语句,然后使用标签来建立我认为的父项-行的复选框与该行上的其他控件之间的子关系。我无法让它工作。
我现在创建了一个在复选框状态更改时调用的方法(它目前只处理单个复选框更改,但我假设我可以使用嵌套的“if”语句获得处理所有复选框的方法and/or 引入额外的变量):
private List<string> flushForm00()
{
var flushFields = new List<string>();
flushFields.Clear();
if (placeLevelCheckBox00.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 = $"placeLevelCheckBox0{i}.Checked = false;";
flushFields.Add(flushEachC);
Console.WriteLine(flushEachC);
}
}
从 Control.WriteLine 输出
中可以看出,循环正在按预期进行迭代PlaceComboBox00.Text = "";
TypeComboBox00.Text = "";
...
PlaceComboBox09.Text = "";
TypeComboBox09.Text = "";
placeLevelCheckBox01.Checked = false;
...
placeLevelCheckBox09.Checked = false;
System.Collections.Generic.List`1[System.String]
从输出中也可以看出,列表“flushFields”仍然是空的。我试过在方法声明行上使用 static/void 但没有成功。
我已经在其他地方成功使用了 .Add() 方法,但没有在循环中使用。为什么列表没有被填充,循环代码是否“可修复”,如果是如何?
我很抱歉这已经发展了这么久。 预先感谢您的时间和专业知识。
List 没有任何方法可以将列表中包含的每个单个对象表示为字符串。即使对象本身是字符串。因此,当您在其上调用 ToString()(并且 Console.WriteLine 调用 ToString())时,列表会响应并返回对象 class 名称。有不同的方法可以解决您的问题。首先,List 有一个 Count 属性 告诉你元素的数量,然后有一个名为 Any 的 IEnumerable 扩展 returns 一个布尔值(如果至少有一个元素则为真)或者你可以写您自己的 class 派生自 List<string>
并实现了覆盖基本功能的不同版本的 ToString。
说到这里,我认为您确实需要数组。不要按名称处理每个单个元素,而是将代码需要更改的所有内容放在一个数组中。 CheckBox 数组,Labels 数组,TextBox 数组等。数组的每个元素都应存储在与您的索引逻辑相关的渐进位置。
因此,以与复选框相关的示例为例,您可以(在全局范围内)声明一个 CheckBoxes
public class Form1 : Form
{
private CheckBox[] _myChecks;
public Form1():base()
{
InitializeComponent();
// Notice that this doesn't create another set of checkboxes.
// It just stores the reference to the checkboxes created by
// InitializeComponent in an array
_myChecks = new CheckBox[]
{
placeLevelCheckBox00,placeLevelCheckBox01,placeLevelCheckBox02,
placeLevelCheckBox03,placeLevelCheckBox04,placeLevelCheckBox05,
placeLevelCheckBox06,placeLevelCheckBox07,placeLevelCheckBox08
};
}
此时如果您需要激活从索引 0 到 4 的所有内容并停用您可以编写的其他所有内容
private void HandleCheckboxes(int activateLimit)
{
for(int x = 0; x <= activateLimit; x++)
_myChecks[x].Checked = true;
for(int x = activateLimit+1; x < _myChecks.Length; x++)
_myChecks[x].Checked = false;
}
当然,您可以扩展这些示例,添加其他数组并在 activate/deactivate 复选框数组
的同一循环中应用所需的逻辑现在是最后一点。您如何在不查看每个复选框的变量名称的情况下知道要用于 activation/deactivation 的索引位置?
您可以使用每个复选框的标签 属性 来做到这一点。这个 属性 可以被程序员用来存储任何类型的数据。您可以在那里存储用于该特定复选框的索引(您可以在每个复选框 属性 列表中的表单设计器中执行此操作)
placeLevelCheckBox00.Tag = 0;
placeLevelCheckBox01.Tag = 1;
// and so on for the others
现在一切就绪,可以进行重大改进。
您可以为转到每个复选框 属性 window 的每个复选框设置相同的事件处理程序,然后为事件列表中的 CheckedChanged 事件选择相同的处理程序。你不需要十个不同的事件处理程序做同样的事情
private void placeLevelCheckBox_CheckedChanged(object sender, EventArgs e)
{
// Here sender is the checkbox clicked. Just need to cast it
int activateLimit = Convert.ToInt32((sender as CheckBox).Tag);
HandleCheckboxes(activateLimit);
}