如何将变量传递给按钮事件方法?

26

我需要在点击按钮时向被触发方法传递两个对象。我该怎么做?

到目前为止,我一直在考虑创建一个更改的EventArgs

 public class CompArgs : System.EventArgs
    {
    private object metode;
    private Type typen;

    public CompArgs(object m, Type t)
    {
        this.metode = m;
        this.typen = t;
    }

    public object Metode()
    {
        return metode;
    }

    public Type Typen()
    {
        return typen;
    }
}

但是我该如何使用它呢?有没有可能覆盖按钮的点击事件,使用自定义的 eventhandler,并将 CompArgs 作为参数传递进去?

private void button1_Click(object sender, EventArgs e)
        {


            Assembly assembly = Assembly.LoadFile(@"c:\components.dll");

            int counter = 0;
            
            foreach (Type type in assembly.GetTypes())
            {
                if (type.IsClass == true)
                {

                    Button btn = new Button();
                    btn.Location = new Point(174 + (counter * 100),10);
                    btn.Size = new Size(95, 23);
                    btn.Name = type.Name;
                    btn.Text = type.Name;
                    btn.UseVisualStyleBackColor = true;
                    

                    this.Controls.Add(btn);

                    object obj = Activator.CreateInstance(type);

                    //I need to pass on obj and type to the btn_Click
                    btn.Click += new eventHandler(btn_Click);

                    counter++;
                }
            }
         }

我需要的是事件方法:

private void btn_Click(object sender, CompArgs ca)
        {
                MessageBox.Show((string)ca.Typen().InvokeMember("getMyName",
                             BindingFlags.Default | BindingFlags.InvokeMethod,
                             null,
                             ca.Metode(),
                             null));

        }

您可以通过从Button类派生来重写OnClick方法。我已经使用这种技术添加了一个答案。 - Ian
8个回答

78

哇,你们太把这件事复杂化了。没有必要使用任何自定义类或方法覆盖。在这个例子中,我只需要传递一个选项卡索引号。你可以指定任何你想要的值类型,只要你的方法期望那个值。

button.Click += (sender, EventArgs) => { buttonNext_Click(sender, EventArgs, item.NextTabIndex); };

void buttonNext_Click(object sender, EventArgs e, int index)
    {
       //your code
    }

1
不确定为什么人们不使用这个答案。这样简单多了。 - Larry
3
嘿,大家好。我知道这是一篇比较旧的帖子,但我想指出,如果您在上面的代码中搁置一个变量,VS会抛出错误。我有一个按钮,我想在按键时发送开关器,我使用了上面的代码(非常好用),但它不喜欢布尔变量... 这是方法签名:private void UpdateDBButton_Click(object sender, EventArgs e, bool fullReset),事件处理程序行:UpdateDBButton.Click += (sender, EventArgs) => {bool fullReset = false; UpdateDBButton_Click(sender, EventArgs, fullReset);}; - Jesse Fender
@Jesse Fender 确保传递相等的参数。你需要依次传递sender、EventArgs,然后是布尔值。 - Rudy Hinojosa
1
@DouglasPlumley 确保参数类型匹配。您将 EventArgs 作为第二个参数传递,但该方法期望的是 RoutedEventArgs。请将 RoutedEventArgs 更改为 EventArgs。 - Rudy Hinojosa
我们如何从点击事件中移除此项?我在listview对象中有一个按钮。所以我想做这样的事情: button.Click += (sender, EventArgs) => { buttonNext_Click(sender, EventArgs, item.NextTabIndex); }; && button.Click -= (sender, EventArgs) => { buttonNext_Click(sender, EventArgs, item.NextTabIndex); }; 怎么做呢? - user6159419
显示剩余8条评论

8

您能否在承载按钮的表单上设置属性或成员变量,并从按钮单击事件中访问这些变量?

编辑:根据反馈评论,建议使用自定义按钮类(与上述建议不同)

class MyButton : Button
{
    private Type m_TYpe;

    private object m_Object;

    public object Object
    {
        get { return m_Object; }
        set { m_Object = value; }
    }

    public Type TYpe
    {
        get { return m_TYpe; }
        set { m_TYpe = value; }
    }
}




Button1Click(object sender, EventArgs args)
{
  MyButton mb = (sender as MyButton);

  //then you can access Mb.Type
  //and  Mb.object
}

2
@WraithNath - 我以前用过这种方法,但感觉这是一种不太干净的方式。我需要按钮引用正确的对象,所以上次我做的方法是将对象存储在列表中。将按钮对象的相对索引保存在其Tag属性中,然后在需要时解析它。 - Bildsoe
你好,你想传递到点击事件中的信息是从哪里来的?它已经存在于表单上的按钮控件中吗?我认为改变按钮点击处理程序的唯一方法是实现自己的按钮控件或Windows Forms控件的派生类。您可以将要在单击事件中使用的对象存储在按钮的标记中,作为数组或自定义类型,然后只需将发送器强制转换为按钮并检索标记即可。按钮是否在弹出窗口中,在该窗口中用户选择选项并关闭窗口? - WraithNath
@WraithNath - 我已经添加了更多的代码。你描述的概念是我之前处理它的方式。但正如我所说,它感觉有点...不太干净。 - Bildsoe
啊,那我建议使用Matthew Steeples的方法,你可以创建一个自定义按钮来公开你所需的属性和对象。我已经编辑了我的答案,展示了一个例子。 - WraithNath
@WraithNath - 这看起来是一个不错的简单方法。 - Bildsoe

4
您可以使用按钮的Tag属性。您可以从设计器属性窗口添加一个字符串值到此属性中,然后在处理程序中获取它,如下所示:
    private void MyButton_Click(object sender, EventArgs e)
    {
        string tagValue = ((Button) sender).Tag;

        if(tag == "blah")
        {
            // Do something
        }
    }

3

我会创建一个新的按钮并覆盖OnClick方法。而不是传递EventArgs,使用一个新的派生类,并加入你的其他成员。

在接收事件的委托上,将给定的EventArgs转换为您期望获得的更具体的派生类,或者设置一个在按下按钮时同时触发的新事件,并连接到该事件以使事情更隐含。

示例代码:

 public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        ButtonEx b1 = new ButtonEx();
        b1.OnCustomClickEvent += new ButtonEx.OnCustomClickEventHandler(b1_OnCustomClickEvent);
    }

    void  b1_OnCustomClickEvent(object sender, ButtonEx.CustomEventArgs eventArgs)
    {
        string p1 = eventArgs.CustomProperty1;
        string p2 = eventArgs.CustomProperty2;
    }
}

public class ButtonEx : Button
{
    public class CustomEventArgs : EventArgs
    {
        public String CustomProperty1;
        public String CustomProperty2;
    }

    protected override void  OnClick(EventArgs e)
    {
        base.OnClick(e);
        if(OnCustomClickEvent != null)
        {
            OnCustomClickEvent(this, new CustomEventArgs());
        }
    }

    public event OnCustomClickEventHandler OnCustomClickEvent;
    public delegate void OnCustomClickEventHandler(object sender , CustomEventArgs eventArgs);
}

2
您不能在预定义的事件处理程序签名中使用自己的自定义事件参数类。至少,在任何默认调用处理程序时,自定义事件参数类型都将不会被使用(在按钮的情况下,处理程序只能是EventArgs类型);您可能会自己调用处理程序,传递您的自定义类型,但是,您需要有逻辑来将其从EventArgs转换回原来的类型。
作为可能的解决方案(取决于您的情况),请考虑使用组合类型封装所需的项,就像您的事件参数类型一样,但将所需实例保留为可访问变量,可以从事件处理程序内部或者至少通过事件处理程序调用的方法中使用它。
例如,定义您的类型...
public class MyType
{
    public object AccessibleItem { get; set; }
}

在您的表单类中...

private MyType MyTypeInstance = new MyType();

private void Button_Click(object sender, EventArgs e)
{
    //here we can set the item, if needs be...
    MyTypeInstance.AccessibleItem = new Anything();
    //or access the item to use as required...
    DoSomeMagicWithMyObject(MyTypeInstance.AccessibleItem);
}

编辑:

好的,看着你现在的代码,我现在只能暂时给你这个(它不会将项目添加到表单控件容器中,而且它使用了 Linq 中的变量迭代器(我认为这是不被赞同或者完全错误的(?),但是嘿...):

    private void BuildButtonToObjectDictionary()
    {
        int counter = 0;
        var assembly = Assembly.LoadFile(@"c:\components.dll");

        var buttonToObjectDictionary = (
            from type in assembly.GetTypes()
            where type.IsClass && !type.IsAbstract
            select new
            {
                Button = new Button
                {
                    Name = type.Name,
                    Text = type.Name,
                    Size = new Size(95, 25),
                    Location = new Point(175 + (counter * 100), 10),
                    UseVisualStyleBackColor = true
                },
                Item = Activator.CreateInstance(type),
                Index = counter++
            });
    }

但是我将有许多具有自己对象的按钮需要被访问。我该怎么做呢?早些时候,我将对象存储在列表中。将按钮对象的相关索引保存在其标记属性中,然后在需要时解析它。 - Bildsoe
@Bildsoe:这些实例是在哪里创建的? 这是相同的概念,但是您可以使用“Dictionary”(字典),其中“键”是指向按钮的引用,并与适当的“值”相关联 - 甚至可以使用“Hashtable”(散列表)。 - Grant Thomas
我已经添加了一些更多的代码,以展示我正在尝试实现什么。 - Bildsoe
我认为你采用了错误的方法。使用反射来解决这个简单问题是不正确的... - Ian
@Ian - 那你会怎么做呢?我的想法是创建一种方式,通过动态读取程序集来扩展我的应用程序。 - Bildsoe

2

不确定Winforms中是否存在,但在WPF中存在:“标签”对象可附加到所有控件。您可以保存要传递的对象,然后在事件处理程序中从发送方对象中读取它。

private void Button_Click(object sender, RoutedEventArgs e)
{
    var note = (sender as FrameworkElement).Tag as Note;
    //Do something with note here
}

1
虽然这不是一种非常优雅的方式。但你必须了解标签及其中所包含的内容。 - Ian

0
如果您打开yourForm.Designer.cs文件,您将看到所有由VS自动生成的代码。在这里,您可以更改单击按钮时调用的方法(或添加第二个方法)。
this.yourButton.Location = new System.Drawing.Point(211, 51);
this.yourButton.Name = "yourButton";
this.yourButton.Size = new System.Drawing.Size(75, 23);
this.yourButton.TabIndex = 0;
this.yourButton.Text = "yourButton";
this.yourButton.UseVisualStyleBackColor = true;
this.yourButton.Click += new System.EventHandler(this.yourMethodHere(object1, object2);

希望这能有所帮助。

这是可能的吗?发送者和事件参数不总是作为参数传递吗? - Bildsoe
1
就我所看到的,这根本行不通。即使它能够工作,也总是指定相同的两个参数作为参数,而不是在每次调用时调用并指定参数。如果这是期望的行为,那么它仍然无法编译,因为缺少括号。 - Grant Thomas

0
需要查看更多的代码才能给出更好的答案,但是您可以创建一个事件,将CompArgs作为参数传递,并在捕获buttonEvent时触发该事件。

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