我想在表单上的所有文本框中添加一个事件:
foreach (Control C in this.Controls)
{
if (C.GetType() == typeof(System.Windows.Forms.TextBox))
{
C.TextChanged += new EventHandler(C_TextChanged);
}
}
问题是它们存储在几个组框中,而我的循环却看不到它们。我可以单独循环遍历每个组框的控件,但是可以在一个循环中以简单的方式完成所有操作吗?
答案 0 :(得分:31)
表单和容器控件的Controls
集合仅包含直接子节点。为了获得所有控件,您需要遍历控件树并以递归方式应用此操作
private void AddTextChangedHandler(Control parent)
{
foreach (Control c in parent.Controls)
{
if (c.GetType() == typeof(TextBox)) {
c.TextChanged += new EventHandler(C_TextChanged);
} else {
AddTextChangedHandler(c);
}
}
}
注意:表单(间接)从Control
派生,所有控件都有Controls
集合。所以你可以在你的表单中调用这样的方法:
AddTextChangedHandler(this);
更通用的解决方案是创建一个递归方法,以递归方式将操作应用于所有控件。在静态类(例如WinFormsExtensions
)中添加此方法:
public static void ForAllControls(this Control parent, Action<Control> action)
{
foreach (Control c in parent.Controls) {
action(c);
ForAllControls(c, action);
}
}
静态类名称空间必须是“可见的”,即如果它在另一个名称空间中,则添加适当的using
声明。
然后你可以像这样调用它,其中this
是形式;您也可以用嵌套控件必须受影响的表单或控件变量替换this
:
this.ForAllControls(c =>
{
if (c.GetType() == typeof(TextBox)) {
c.TextChanged += C_TextChanged;
}
});
答案 1 :(得分:13)
一些简单的通用工具使这个问题变得非常简单。我们可以创建一个简单的方法来遍历整个控件的树,返回所有子节点的序列,所有子节点等等,覆盖所有控件,而不仅仅是固定的深度。我们可以使用递归,但是通过避免递归它会表现得更好。
public static IEnumerable<Control> GetAllChildren(this Control root)
{
var stack = new Stack<Control>();
stack.Push(root);
while (stack.Any())
{
var next = stack.Pop();
foreach (Control child in next.Controls)
stack.Push(child);
yield return next;
}
}
使用这个我们可以得到所有的孩子,过滤掉我们需要的那些孩子,然后轻松地附上处理器 :
foreach(var textbox in GetAllChildren().OfType<Textbox>())
textbox.TextChanged += C_TextChanged;
答案 2 :(得分:7)
试试这个
AllSubControls(this).OfType<TextBox>().ToList()
.ForEach(o => o.TextChanged += C_TextChanged);
AllSubControls
private static IEnumerable<Control> AllSubControls(Control control)
=> Enumerable.Repeat(control, 1)
.Union(control.Controls.OfType<Control>()
.SelectMany(AllSubControls)
);
LINQ很棒!
答案 3 :(得分:2)
Haven没有看到任何人使用linq和/或产量,所以这里是:
public static class UtilitiesX {
public static IEnumerable<Control> GetEntireControlsTree(this Control rootControl)
{
yield return rootControl;
foreach (var childControl in rootControl.Controls.Cast<Control>().SelectMany(x => x.GetEntireControlsTree()))
{
yield return childControl;
}
}
public static void ForEach<T>(this IEnumerable<T> en, Action<T> action)
{
foreach (var obj in en) action(obj);
}
}
然后你可以将它用于你心中的愿望:
someControl.GetEntireControlsTree().OfType<TextBox>().ForEach(x => x.Click += someHandler);
答案 4 :(得分:1)
正如您所说,您将不得不深入了解表单中的每个元素。遗憾的是,这意味着使用嵌套循环。
在第一个循环中,循环遍历每个元素。如果元素是GroupBox类型,那么你知道在继续之前你需要遍历组框内的每个元素;否则正常添加事件。
你似乎对C#有了不错的把握,所以我不会给你任何代码;纯粹是为了确保您开发解决问题所涉及的所有重要概念:)
答案 5 :(得分:1)
您只能使用表单集合在Windows表单中循环打开表单,例如为所有打开表单设置Windows开始位置:
public static void setStartPosition()
{
FormCollection fc = Application.OpenForms;
foreach(Form f in fc)
{
f.StartPosition = FormStartPosition.CenterScreen;
}
}
答案 6 :(得分:1)
我知道这是一个较旧的主题,但是会说来自http://backstreet.ch/coding/code-snippets/mit-c-rekursiv-durch-form-controls-loopen/的代码段是解决此问题的聪明方法。
它使用ControlCollection的扩展方法。
public static void ApplyToAll<T>(this Control.ControlCollection controlCollection, string tagFilter, Action action)
{
foreach (Control control in controlCollection)
{
if (!string.IsNullOrEmpty(tagFilter))
{
if (control.Tag == null)
{
control.Tag = "";
}
if (!string.IsNullOrEmpty(tagFilter) && control.Tag.ToString() == tagFilter && control is T)
{
action(control);
}
}
else
{
if (control is T)
{
action(control);
}
}
if (control.Controls != null && control.Controls.Count > 0)
{
ApplyToAll(control.Controls, tagFilter, action);
}
}
}
现在,要为所有TextBox控件分配一个事件,您可以编写一个类似的语句(其中'this'是表单):
this.Controls.ApplyToAll<TextBox>("", control =>
{
control.TextChanged += SomeEvent
});
您可以选择按标签过滤控件。
答案 7 :(得分:0)
更新的答案:
我需要禁用表单中的所有控件,包括组框。这段代码有效:
import torch
arr = torch.from_numpy(np.random.random((3,28,28)))
答案 8 :(得分:0)
因为有关“向文本框添加事件”的问题;已经被回答;我提供一些解释,并使用for循环添加迭代替代方案。
问题:
解决方案:
即:
foreach (Control control in myContainer.Controls)
{
if (control is TextBox) { /* Do Something */ }
}
有关如何使用for循环的伪代码示例:
/// <summary> Iterate Controls Inside a Container using a for Loop. </summary>
public void IterateOverControlsIncontainer()
{
// Iterate Controls Inside a Container (i.e: a Panel Container)
for (int i = 0; i < myContainer.Controls.Count; i++)
{
// Get Container Control by Current Iteration Index
// Note:
// You don't need to dispose or set a variable to null.
// The ".NET" GabageCollector (GC); will clear up any unreferenced classes when a method ends in it's own time.
Control control = myContainer.Controls[i];
// Perform your Comparison
if (control is TextBox)
{
// Control Iteration Test.
// Shall Display a MessageBox for Each Matching Control in Specified Container.
MessageBox.Show("Control Name: " + control.Name);
}
}
}