2

I'm using c#, and I have a project that instantiates a lot of PictureBox buttons. I also have all of the click,hover,mouseUp,mouseDown event methods programmed. How do I call a method from a string name so that I don't have to write all of them by hand? Thanks in advance, Carson

Dictionary<string, PictureBox> buttonList = new Dictionary<string,PictureBox>();
string buttonName = "button_file";

buttonList[buttonName].Click += new EventHandler(buttonName + "_click");

public void button_file_click(object sender, EventArgs e)
{
     // do on click stuff here
}
dovid
  • 6,354
  • 3
  • 33
  • 73
Carson
  • 19
  • 4
  • 1
    Using reflection (https://stackoverflow.com/questions/1121441/addeventhandler-using-reflection) would answer your question, but most likely you don't want to do that and instead refactor your code to have less handlers ... – Alexei Levenkov Jun 27 '17 at 01:20

4 Answers4

2

if you set the Name property of button, you can so:

buttonList[buttonName].Name = buttonName;
buttonList[buttonName].Click += ButtonClick;

and only One metode hanle all buttons click but in accordance of their name:

public void ButtonClick(object sender, EventArgs e)
{
    var btn = (Button)sender;
    btn.Text = btn.Name;  //only for demo
}

but you can completely write your own method:

buttonList[buttonName].Click += (s,e)=> clickEvent(buttonList[buttonName], buttonName);

this is a short lambda function which accepts the parameters accordingly of Click event singature and fire your custom function, the clickEvent:

public void clickEvent(Button thePressedButton, string nameOfPresedButton)
{
    thePressedButton.Text = btn.nameOfPresedButton; //only for demo
}
dovid
  • 6,354
  • 3
  • 33
  • 73
  • So in this case, he would have to use if-else statements or a switch case to differentiate the buttons and point them to correct methods, right? – Sometowngeek Jun 27 '17 at 01:34
  • @Sometowngeek Not necessarily. For example, if each button is pressed, changes the value of an item in a `dictionary`, no one `if` will be required – dovid Jun 27 '17 at 01:37
  • So, is this combining all my click events into one method (clickEvent), and checking to see which (nameOfPresedButton) i pressed? I think I would like to have individual methods for the click events. This program basicly has a makeButtonAtPoint() method and I have one line of hard-coded information {makeButtonAtPoint("button_file", 1, 1, true);} – Carson Jun 27 '17 at 01:40
  • 1
    @Carson, I still do not understand why one method can not serve you. Expand and explain what method you specify and what parameters it receives (if necessary, you can edit your question and include the relevant code) – dovid Jun 27 '17 at 01:45
0

From your sample code, I assume you want them all to call button_file_click.

In that case just set the eventhandler to that method:

buttonList[buttonName].Click += new EventHandler("button_file_click");

Greg
  • 3,442
  • 3
  • 29
  • 50
0

A method name isn't quite enough... you also need a class name... but assuming the method belongs to the current class (this) you can use Reflection to do this:

public void ExecuteMethod(string methodName)
{
    var methodInfo = this.GetType().GetMethod(methodName);
    methodInfo.Invoke(this, new [] {});
}

But you can't assign the above to an event because it is not the right delegate type; most event handlers require a sender and an EventArgs. So to make your code work you'd need a little more glue:

Dictionary<string, PictureBox> buttonList = new Dictionary<string,PictureBox>();
string buttonName = "button_file";

buttonList[buttonName].Click += GetHandler(buttonName + "_click");

public Action<object, EventArgs> GetHandler(string handlerName)
{
    var methodInfo = this.GetType().GetMethod(handlerName, new Type[] {typeof(object), typeof(EventArgs)});
    return new Action<object, EventArgs> (sender, eventArgs) => methodInfo.Invoke(this, new [] {sender, eventArgs});
}

The idea here is that GetHandler returns an Action with the right signature (accepting an object and an EventArgs, in that order), which is written as Action<object, EventArgs>. The GetHandler method uses reflection to find the right method in the current class, then creates a lambda expression that invokes the method via reflection, passing the arguments as an array.

The above is of course just an example... it would probably be better to store your delegates in a static dictionary that is computed when the page is loaded for the first time.

That being said, if you're looking for event handling flexibility based on a runtime parameter, a better approach is probably to use the Command event, which allows you to pass a string to the handler, which can then take a different action depending on the contents of the string. That way you can hardcode the handler reference but still softcode what it will do.

Dictionary<string, PictureBox> buttonList = new Dictionary<string,PictureBox>();
string buttonName = "button_file";

buttonList[buttonName].Command += buttonList_Command;
buttonList[buttonName].CommandName = buttonName;

protected void buttonList_Command(object sender, CommandEventArgs e)
{
     switch (e.CommandName)
     {
          case buttonName:
              //Do stuff for button_file
              break;
          case "Foo":
              //Do stuff for some other button named foo
              break;
          default:
              throw new InvalidOperationException();
     }
 }
John Wu
  • 50,556
  • 8
  • 44
  • 80
  • It is best to prepare a dictionary in advance and not Reflection again and agian (and even twice in each event) – dovid Jun 27 '17 at 01:49
  • The command event could work. Even though I was looking for individual method calls from string names, I think the command event will work. Thanks – Carson Jun 27 '17 at 02:10
  • Can I add the .Command property to a PictureBox? what namespace is the Command and CommandName using? – Carson Jun 27 '17 at 02:13
0

You can use reflection to look up the method of a class by name and then create an event handler delegate using that method. For example:

 void bindHandler(string buttonName)
        {
            string methodName = buttonName + "_click";  
            System.Reflection.MethodInfo m = this.GetType().GetMethods().FirstOrDefault(x => x.Name == buttonName + "_click");
            PictureBox button = buttonList[buttonName];
            Delegate handler = Delegate.CreateDelegate(typeof(EventHandler), this, m);
            button.Click += (EventHandler)handler;
        }

Fulling working code:

namespace WindowsFormsApp1Cs
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            MyClass c = new MyClass();
            foreach (var item in c.buttonList)
            {
                this.Controls.Add(item.Value);
            }
        }
    }
    public class MyClass
    {
        public Dictionary<string, PictureBox> buttonList;
        public delegate void MyClickHandler(object sender, EventArgs e);
        public MyClass()
        {
            buttonList = new Dictionary<string, PictureBox>();
            buttonList.Add("button_file_1", new PictureBox() { Width = 100, Height = 100, Name = "button_file_1", Anchor = AnchorStyles.Top | AnchorStyles.Left, Top = (buttonList.Count * 100) + 10, Left = 10, ImageLocation="0.jpg" });
            buttonList.Add("button_file_2", new PictureBox() { Width = 100, Height = 100, Name = "button_file_2", Anchor = AnchorStyles.Top | AnchorStyles.Left, Top = (buttonList.Count * 100) + 10, Left = 10, ImageLocation = "0.jpg" });
            buttonList.Add("button_file_3", new PictureBox() { Width = 100, Height = 100, Name = "button_file_3", Anchor = AnchorStyles.Top | AnchorStyles.Left, Top = (buttonList.Count * 100) + 10, Left = 10, ImageLocation = "0.jpg" });
            foreach (var item in buttonList)
            {
                bindHandler(item.Key);
            }
        }
        void bindHandler(string buttonName)
        {
            string methodName = buttonName + "_click";  
            System.Reflection.MethodInfo m = this.GetType().GetMethods().FirstOrDefault(x => x.Name == buttonName + "_click");
            PictureBox button = buttonList[buttonName];
            Delegate handler = Delegate.CreateDelegate(typeof(EventHandler), this, m);
            button.Click += (EventHandler)handler;
        }
        public void button_file_1_click(object sender, EventArgs e)
        {
            Debug.WriteLine("button_file_1_click");
        }
        public void button_file_2_click(object sender, EventArgs e)
        {
            Debug.WriteLine("button_file_2_click");
        }
        public void button_file_3_click(object sender, EventArgs e)
        {
            Debug.WriteLine("button_file_3_click");
        }
    }
}
Alexander Higgins
  • 6,765
  • 1
  • 23
  • 41
  • Keep in mind that method second parameter (this) should be an instance of an object where the method m is declared. Delegate handler = Delegate.CreateDelegate(typeof(EventHandler), this, m) – Alexander Higgins Jun 27 '17 at 17:02
  • Thank you. If it solves your problem you can mark it as the accepted answer to help others who have the same problem that read this question. – Alexander Higgins Jun 27 '17 at 17:14