更改表单上的所有按钮

4

我已经接近找到了一个解决方案,只是这一点小细节还差一步。

我的目标:
我想通过编写代码来更改表单(Form1)上每个按钮的光标样式。我知道如何使用foreach搜索表单上的所有控件,但我不确定如何将此控件作为参数传递给我编写的程序。下面我将展示一个示例。

private void Form1_Load(object sender, EventArgs e)
{
    foreach (Button b in this.Controls)
    {
        ChangeCursor(b);  // Here is where I'm trying to pass the button as a parameter.  Clearly this is not acceptable.
    }     
}

private void ChangeCursor(System.Windows.Forms.Button Btn)
{
    Btn.Cursor = Cursors.Hand;
}

请问有人能给我一些提示吗?

非常感谢

Evan

6个回答

7

更改

foreach (Button b in this.Controls)
{
    ChangeCursor(b);  // Here is where I'm trying to pass the button as a parameter.
                      // Clearly this is not acceptable.
}     

为了

foreach (Control c in this.Controls)
{
   if (c is Button)
   {
        ChangeCursor((Button)c);  
   }
}  

并不是表单上的每个控件都是按钮。

补充:您还应该查找嵌套的控件。请参见Bala R.的答案。


此外,表单上的并非每个控件都是直接子级。这种方法不包括子级的子级。 - Rob P.
1
我找到了问题所在:这只搜索了我的选项卡控件!而不是我的表单! - user725913

7
我看到的唯一问题是,如果您有嵌套的控件,那么this.Controls将无法获取它们。您可以尝试使用以下代码来获取所有控件:
public IEnumerable<Control> GetSelfAndChildrenRecursive(Control parent)
{
    List<Control> controls = new List<Control>();

    foreach(Control child in parent.Controls)
    {
        controls.AddRange(GetSelfAndChildrenRecursive(child));
    }

    controls.Add(parent);

    return controls;
}

并调用

GetSelfAndChildrenRecursive(this).OfType<Button>.ToList()
                  .ForEach( b => b.Cursor = Cursors.Hand);

我建议将 GetSelfAndChildrenRecursive 方法作为通用方法,并通过特定类型进行控件过滤,例如:GetSelfAndChildrenRecursive<Button>(),而不是获取所有类型然后再按特定类型进行过滤。 - Homam
@Homam 我在其他地方使用此函数,可能希望对不同类型的控件进行操作(例如启用所有嵌套控件)。但是对于 OP 的目的,你建议的方法可能有效,但仍然 OfType<> 很易读。 - Bala R
@Homam,我认为我的回答可能接近你的想法了,参见https://dev59.com/WVbTa4cB1Zd3GeqP_5bI#42400534。 - Arvo Bowen
根据这个链接:https://stackoverflow.com/q/44983056/4439444,我认为`.OfType<Button>()是正确的,而不是.OfType<Button>`。 - GntS

2

和Bala R的答案一样,原理相同,但我做法是...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;

namespace AppName
{
    public static class ControlExtensions
    {
        public static IEnumerable<Control> GetAllCtls(this Control control, Type type)
        {
            var controls = control.Controls.Cast<Control>();

            return controls.SelectMany(ctrl => GetAllCtls(ctrl, type))
                                      .Concat(controls)
                                      .Where(c => c.GetType() == type);
        }
    }
}

然后像这样使用它...
foreach (Control ctl in this.GetAllCtls(typeof(Button)))
{
    MessageBox.Show("Found a button on the form called '" + ctl.Text + "'");
}

1
如果您的控件没有从按钮继承,我认为您的foreach将会抛出异常。
尝试像这样做:
foreach (Control b in this.Controls)
{

  if (b is Button)
    ChangeCursor((Button)b);  
}    

Russel - 虽然你上面的代码没有抛出任何异常,但它并没有改变表单上的任何按钮。在循环中它在任何时候都没有通过 "if (b is button)" 这一点。 - user725913
1
我会相信你的 Evan,因为我还没有编译它,但我看不出为什么会这样。 - Russell Troywest
1
顺便说一下 - 即使只有一行代码,将foreach/if等块用花括号括起来也总是更好的,因为这样可以在以后节省编程错误 - 我只是懒得这么做。不过应该还是能正常工作的。 - Russell Troywest
1
@Evan。如果其中一个控件是按钮,那么它肯定会通过if语句。 - Morgan Herlocker

1

在我看来,那看起来是正确的;我有没有看到问题?

编辑:啊,是的 - 如果集合中有非按钮控件,则转换将失败。

您只想传递按钮的控件,因此您需要添加一个IF语句。


是的,Visual Studio 在调试模式下会高亮显示“按钮 b”,并提示“无法将类型为'System.Windows.Forms.TabControl'的对象强制转换为类型'System.Windows.Forms.Button'。” - user725913
1
不太准确,表单上的控件不仅仅可以是按钮。 - Liviu Trifoi
-1:只有当“表单”仅包含“按钮”控件时才会正确。 - Neil Knight
问题是,“但我不确定如何通过我编写的例程将此控件作为参数传递”。他的代码成功地做到了这一点。是的,非按钮控件可能会导致运行时错误,但这不是问题的一部分。 - Rob P.

1

您也可以使用以下语法来使代码更简洁:

foreach (Control c in this.Controls)    
{       
    if (c is Button)       
    {          
        ChangeCursor(c as Button);         
    }    
} 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接