How to combine events into 1 method? - c#

by default, we add events in this way:
item1.Click += Click1;
item2.Click += Click2;
.....
private void Click1(){
.....
}
private void Click2(){
}
however, is there a way to combine it into one method, like:
item1.Click += Click(1);
item2.Click += Click(2);
....
private void Click(int num){
if(num==1){
....
}
else if(num==2){
....
}
}
(btw, newbie in C#)

Event handlers have sender argument usually which can be used to identify the actual sender:
void Click(object sender, EventArgs e)
{
var btn= (Button)sender; // you can access properties which you might have set to identify which button it is
.....
}
button1.Click+=Click;
button2.Click+=Click;
button3.Click+=Click;
button4.Click+=Click;
It this is not sufficient and you want to pass some extra data to the handler, you could use a lambda :
void Click(object sender, EventArgs e, int extraInfo)
{
var btn= (Button)sender;
.....
}
button1.Click+=(s, e) => Click(s, e, 1);
button2.Click+=(s, e) => Click(s, e, 2);
button3.Click+=(s, e) => Click(s, e, 3);
button4.Click+=(s, e) => Click(s, e, 4);
Unsubscribing will be an issue with anonymous methods but if the handler owner and the control have the same lifespan inside a page then it might not matter as they will get GC-ed together anyway

You can also compare the sender by reference:
item1.Click += Click;
item2.Click += Click;
....
void Click(object sender, EventArgs e)
{
Control c = sender as Control; // c = null if sender is not Control
if (sender == item1)
Debug.Print(c.Name + ", " + c.Text + ", " + c.Tag);
else if (sender == item2)
Debug.Print(c.Name + ", " + c.Text + ", " + c.Tag);
}

That's how events always worked. You can use the same event handler for multiple events as long as the signature is the same. The base EventHandler type accepts a sender which is the object that fires the event and an EventArgs-derived parameter which is probided when the code fires the event.
For example, you can have a single click handler for multiple buttons :
void myHandler(object sender,EventArgs e)
{
var theButton=(Button)sender;
.....
}
button1.Click+=myHandler;
button2.Click+=myHandler;
button3.Click+=myHandler;
button4.Click+=myHandler;
Imagine you wanted to create a calculator with 10 digits. You could read the Text or Tag property of the sender to retrieve the digit :
void myDigitsHandler(object sender,EventArgs e)
{
var theButton=(Button)sender;
//You could store a number to the tag
int digitFromTag=(int)theButton.Tag;
//Or you could parse the text
int digitFromText=int.Parse(theButton.Text);
.....
}

Related

C# - How can I name a new event handler with number suffix using a variable?

I am creating new picture boxes and their click event handlers programmatically. But I can't use them seperately because I can't name them properly. I need to name the event handlers like box1_click, box2_click...
What is the simplest way to name new event handlers separately numbered with an integer?
My attempt:
for(i=1; i<11; i++)
{
boxes[i] = new PictureBox();
boxes[i].Name = "box" + i;
boxes[i].Click += new EventHandler( ??? );
}
There are multiple ways to do that.
You could either add the event handler as a lambda expression that calls each specific handler by reflection like this (make sure to define your handlers as public methods):
MethodInfo method = this.GetType().GetMethod("B_Click" + i.ToString());
boxes[i].Click += (o,e) =>
{
if (null != method)
{
method.Invoke(this, new object[2] { o, e });
}
};
...
public void B_Click1(object sender, EventArgs e)
public void B_Click2(object sender, EventArgs e)
etc...
Another option is to create a delegate event handler using Delegate.CreateDelegate instead of a lambda expression like this:
MethodInfo method = this.GetType().GetMethod("B_Click" + i.ToString());
EventHandler handler = (EventHandler)Delegate.CreateDelegate(typeof(EventHandler), this, method);
boxes[i].Click += handler;
...
public void B_Click1(object sender, EventArgs e)
public void B_Click2(object sender, EventArgs e)
etc...
Probably the best option is to define one handler for all PictureBoxes and cast sender object as the clicked PictureBox like this:
boxes[i].Click += B_Click;
...
private void B_Click(object sender, EventArgs e)
{
PictureBox clickedPictureBox = sender as PictureBox;
if (clickedPictureBox != null)
{
string name = clickedPictureBox.Name; // for example get picture box name
}
}
You should always use the same event handler. Inside the handler you would decide upon the sender parameter what has to be done.

how to fix this EventArgs c#

I'm trying to build my own Simple Calculator.
I have this method with 10 references (from 0-10)
private void button_Click_1(object sender, EventArgs e)
{
Button b = (Button)sender;
tb.Text += b.ToString();
}`
Everything is working but the text sent to the TextBox come with extra things that I don't need.
I just want to show the number that I clicked on the calculator. I want to hide the "System.windows.forms.button, Text:" and show only the 7(in this case)
Use the Button.Text property of Button instead of ToString()
tb.Text += b.Text;
As Adil points out just use the Text property.
A further improvement can be made to your code base by instead of using the generated Event handler, create a single reusable event handler
instead of
private void button_Click_1(object sender, EventArgs e)
{
Button b = (Button)sender;
tb.Text += b.Text;
}
private void button_Click_2(object sender, EventArgs e)
{
Button b = (Button)sender;
tb.Text += b.Text;
}
private void button_Click_3(object sender, EventArgs e)
{
...
You could instead just have the single event handler. Remember to update your pointers on your buttons to point to this event handler.
private void numericButton_Click(object sender, EventArgs e)
{
Button b = (Button)sender;
tb.Text += b.Text;
}`

Combining Multiple Event Methods

Looking to refactor some application code. I have a GUI that has several of the same events that are reflected for different labels, textboxes, etc...
For example:
private void textBox1_Enter(object sender, EventArgs e)
{
textBox1.BackColor = Color.LightCyan;
}
and
private void textBox2_Enter(object sender, EventArgs e)
{
textBox2.BackColor = Color.LightCyan;
}
I just assign these methods to the event on the object properties in Visual Studio. Is there an efficient way to combine multiple event methods to clean up the code? Thanks!
Define a single event like:
private void textBox_Enter(object sender, EventArgs e)
{
TextBox textBox = sender as TextBox;
if (textBox != null)
textBox.BackColor = Color.LightCyan;
}
and then assign that event to all your TextBox Enter event like:
textBox1.Enter += textBox_Enter; //Same event handler
textBox2.Enter += textBox_Enter; //Same event handler
If both event handlers do the same exact logic, as you probably have listed here, just create 1 event handler and assign it to both components.
private void TextBox_Enter(object sender, EventArgs e)
{
TextBox textBox = sender as TextBox;
if (textBox != null) {
textBox.BackColor = Color.LightCyan;
}
}
To elaborate slightly you can take this approach even when you want to perform different actions depending on the control that fired the event.
private void textBox_Enter(object sender, EventArgs e)
{
//Check if the sender is textBox1
if(ReferenceEquals(sender, textBox1))
{
//Perform action on textBox1
}
//Check if the sender is textBox2
if(ReferenceEquals(sender, textBox2))
{
//Perform action on textBox2
}
}

how to create events on dynamic `textboxes`

I managed to create textboxes that are created at runtime on every button click. I want the text from textboxes to disappear when I click on them. I know how to create events, but not for dynamically created textboxes.
How would I wire this up to my new textboxes?
private void buttonClear_Text(object sender, EventArgs e)
{
myText.Text = "";
}
This is how you assign the event handler for every newly created textbox :
myTextbox.Click += new System.EventHandler(buttonClear_Text);
The sender parameter here should be the textbox which sent the even you will need to cast it to the correct control type and set the text as normal
if (sender is TextBox) {
((TextBox)sender).Text = "";
}
To register the event to the textbox
myText.Click += new System.EventHandler(buttonClear_Text);
Your question isn't very clear, but I suspect you just need to use the sender parameter:
private void buttonClear_Text(object sender, EventArgs e)
{
TextBox textBox = (TextBox) sender;
textBox.Text = "";
}
(The name of the method isn't particularly clear here, but as the question isn't either, I wasn't able to suggest a better one...)
when you create the textBoxObj:
RoutedEventHandler reh = new RoutedEventHandler(buttonClear_Text);
textBoxObj.Click += reh;
and I think (not 100% sure) you have to change the listener to
private void buttonClear_Text(object sender, RoutedEventArgs e)
{
...
}
I guess the OP wants to clear all the text from the created textBoxes
private void buttonClear_Text(object sender, EventArgs e)
{
ClearSpace(this);
}
public static void ClearSpace(Control control)
{
foreach (var c in control.Controls.OfType<TextBox>())
{
(c).Clear();
if (c.HasChildren)
ClearSpace(c);
}
}
This should do the job :
private void button2_Click(object sender, EventArgs e)
{
Button btn = new Button();
this.Controls.Add(btn);
// adtionally set the button location & position
//register the click handler
btn.Click += OnClickOfDynamicButton;
}
private void OnClickOfDynamicButton(object sender, EventArgs eventArgs)
{
//since you dont not need to know which of the created button is click, you just need the text to be ""
((Button) sender).Text = string.Empty;
}

How to set dynamic parameters in c# methods

I have written a method trial for button btnTrial1:
public void trial(object sender, EventArgs e, Button btn, TextBox txt, Label lbl)
{
}
In my application, i am generating more buttons and textboxes and labels dynamically through code and naming them sequentially like btnTrial2, txtTrial2, lblTrial2 then btnTrial3, txtTrial3, lblTrial3 and so on. Now i want to set trial as EventHandler for btnTrial2 then for btnTrial3 and so on.
So now when i call the method trial from btnTrial1, my parameters should be:
Public void (sender, e, btnTrail1, txtTrial1, lblTrial1)
But when i call the method trial from btnTrial2, my parameters should be:
Public void (sender, e, btnTrail2, txtTrial2, lblTrial2)
and so on...
btnTrial1.YourEvent += (sender, args) => trial(sender, args,
btnTrial1, txtTrial1, lblTrial1);
btnTrial2.YourEvent += (sender, args) => trial(sender, args,
btnTrial2, txtTrial2, lblTrial2);
You mention "generating them dynamically" - that is fine, but if you are in a loop you will also need to watch out for the infamous "captured variable / loop" problem - notably, the variables "captured" must be inside the loop; for example:
for(int i = 1 ; i <= 10 ; i++) {
var btnTrial = ...
var txtTrial = ...
var lblTrial = ...
btnTrial.YourEvent += (sender, args) => trial(sender, args,
btnTrial, txtTrial, lblTrial);
}
(if, for example, btnTrial was declared outside the loop, bad things would happen)
Why not make your trial function
public void trial(object sender, EventArgs e)
{
}
And then based on the sender, use different controls:
public void trial(object sender, EventArgs e)
{
Button btn;
TextBox txt;
Label lbl;
if (sender == btnTrial1){
btn = btnTrail1;
txt = txtTrial1;
lbl = lblTrial1;
}
if (sender == btnTrial2){
btn = btnTrail2;
txt = txtTrial2;
lbl = lblTrial2;
}
if (sender == btnTrial3){
btn = btnTrail3;
txt = txtTrial3;
lbl = lblTrial3;
}
//rest of the method
}
Two suggestions:
Refactor trial() to return an EventHandler (or ideally EventHandler<T>):
public EventHandler GetTrialEventHandler(Button btn, TextBox txt, Label lbl)
{
return (sender, args) =>
{
// Do something with btn, txt, lbl
};
}
Rather than name your form elements btnTrial1, btnTrial2, etc, why not just make lists of the elements (or a list of sets of Button+TextBox+Label)? Then you just have to enumerate over the list(s) to set up your event handlers, rather than hard-code for each.
To each button You can add:
this.button.Click += new System.EventHandler(this.button_Click);
method like this:
private void button_Click(object sender, EventArgs e)
{
String name = (sender as Control).Name;
int number = Int32.Parse(name.Substring(name.Length-1));
trial(sender, e, this.Controls["btnTrial"+number], this.Controls["txtTrial"+number], Controls["lblTrial"+number]);
}

Categories