Event handler for a dynamically added button - c#

Code :
public partial class Form3 : Form
{
...
...
private void button1_Click(object sender, EventArgs e)
{
Panel p = new Panel();
TextBox diaryName = new TextBox();
Button b = new Button();
Label l = new Label();
diaryName.Font = new Font("Consolas", 12, FontStyle.Bold);
b.Font = buttonFont;
l.Font = buttonFont;
b.BackColor = Color.Wheat;
l.Text = "Diary Name : ";
b.Text = "Add Diary";
Point lbl = l.Location;
diaryName.Location = new Point(l.Location.X + l.Width + 5, lbl.Y);
Point txtbox = diaryName.Location;
b.Location = new Point(txtbox.X + diaryName.Width + 20, txtbox.Y);
p.Controls.Add(l);
p.Controls.Add(diaryName);
p.Controls.Add(b);
p.Location = new Point(12,272);
p.Size = new Size(20 + 20 + 20 + diaryName.Width + l.Width + b.Width, diaryName.Height);
// I need help here..
// b.Click += new EventHandler(); ???
this.Controls.Add(p);
this.Height += 50;
this.Width += 30;
this.FormBorderStyle = FormBorderStyle.Fixed3D;
}
...
}
The above code adds a panel that contains a label,a textBox and a button to the form , that's all working fine, my problem is that I want to handle click event of the dynamically added button (b) , In my event handling code I should be able to access the dynamically added TextBox (diaryName) for validation purposes, but I don't know how to do It. I tried adding another function within the same class Form3 , but since The textbox t is created within the button1_Click function, I am unable to access the textbox , so How can I get around this ?
I am new to c#, I have a Java background so is there any way in c# to declare event handlers like this
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
System.out.println("You clicked the button");
}
});

You can easy assign a handler to the button's event this way:
b.Click += new EventHandler(newButtonClick);
where
protected void newButtonClick(object sender, EventArgs e)
{
//Access your textbox like this
var myTextBox = this.Controls.OfType<TextBox>().FirstOrDefault(tb=>tb.Name == "diaryName");
if(myTextBox!=null)
{
//rest of your code here
}
}
However it's a poor practice. Your button will depend heavily on the objects created dynamically somewhere outside - that breaks encapsulation rule of OOP. Secondly - did you think what will happen if you'll click your original button (the one you showed your handler for) twice?
edit: When I've come to think about it, your method is not that dynamic really. It creates those controls on the fly, but they're not generic in any way - it's a static piece of code, that creates always the same result. So in this case I'd think about putting your new panel, textbox and button in the form as a public items and then initialize them inside your method.
It'd be even better to create them in the visual studio's designer already, hide them using Visible properties and then in button1_Click you could only change their sizes and show them up.

Ok, need to add event for button click,
1) So after you create new Button
Button b = new Button();
2) next add click event for that
b.Click += new EventHandler(b_Click);
3) and then write the actual function body for click event in your code
void b_Click(object sender, EventArgs e)
{
//Your code for click operation
}

Related

C# Event Handler is called multiple times when selecting a menu item

I want to implement some Pictureboxes and when one of them is clicked, a MessageBox should appear and tell which Box is clicked.
However, I want to implement a choice how many Pictureboxes should appear. When I choose another MenuItem, then the click event will be called multiple times. I tried unsubscribing, but it doesn't work.
Here is my code:
public partial class Form1 : Form
{
PictureBox Pbox1;
PictureBox Pbox2;
PictureBox Pbox3;
PictureBox Pbox4;
public Form1()
{
InitializeComponent();
this.Text = "Picturebox";
Pbox1 = new PictureBox();
Pbox2 = new PictureBox();
Pbox3 = new PictureBox();
Pbox4 = new PictureBox();
}
private void Pbox_Click(object sender, EventArgs e, int nr)
{
MessageBox.Show("Picture number " + nr.ToString() + " is clicked");
}
private void toolStripMenuItem2_Click(object sender, EventArgs e)
{
Pbox1.Image = new Bitmap(#"Picture.png");
Pbox1.Location = new Point(20, 40);
Pbox1.Size = new Size(160, 120);
Pbox1.SizeMode = PictureBoxSizeMode.StretchImage;
Pbox1.Click += (sender2, e2) => Pbox_Click(sender2, e2, 1);
this.Controls.Add(Pbox1); }
The rest is the same as toolStripMenuItem2.
Can you help me with this problem?
When will it be called multiple times? I guess when switching to another menu item everything should work fine. But when switching back to an item that was already clicked before, then another click handler will be registered in addition to the existing one and will call the Pbox_Click method twice.
You can try to put the event registration in the constructor method or release other click-events before re-registering them.

wpf Setting mouse handler for dynamic button

I am attempting to learn c# and wpf. I have a segment of code that works in that it shows the controls correctly. I also attempt to create a mouse button handler and when I debug it, the handler is never called. The buttons are nested within a couple of StackPanels, that can't seem to be the problem?
StackPanel tabPanel = new StackPanel();
for (int i = 0; i < 20; i++)
{
StackPanel micPanel = new StackPanel();
micPanel.Orientation = Orientation.Horizontal;
// Add the Calibration Button
Button micButton = new Button();
micButton.Height = 25;
micButton.Name = string.Format("Mic{0}", i);
micButton.Content = string.Format("Mic{0}", ((20 * index) + i));
micButton.MouseLeftButtonUp += new MouseButtonEventHandler(mic_MouseLeftButtonUp);
micPanel.Children.Add(micButton);
// Add the calibrated Value
Label micLabel = new Label();
micLabel.Height = 25;
micLabel.Content = string.Format("Value: {0}", ((20 * index) + i));
micPanel.Children.Add(micLabel);
tabPanel.Children.Add(micPanel);
}
tab.Content = tabPanel;
The handler looks like this:
private void mic_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
Button but = sender as Button;
}
I set a breakpoint and it never calls the handler?
This is typical: use the preview event handler to make sure it is raised first in the tree view.
micButton.PreviewMouseLeftButtonUp += new MouseButtonEventHandler(mic_MouseLeftButtonUp);
Why don't you handle the Click event of the Button?
micButton.Click += new ClickEventHandler(mic_Click);
...
private void mic_Click(object sender, RoutedEventArgs e)
{
Button but = sender as Button;
}
Certain controls do "swallow" certain events but the Button control doesn't raise any MouseButtonUp event. It raises a Click event. The Click event is actually a combination of the LeftButtonDown and LeftButtonUp event:
WPF MouseLeftButtonUp Not Firing
You can read more about routed events and how they work in the documentation on MSDN: https://msdn.microsoft.com/en-us/library/ms742806%28v=vs.110%29.aspx

Multiple message box showing in c#

I am making a one form application and created a hard coded class for my GUI and event handlers.
At first the messagebox is showing once but when I click another button it is showing multiple times, increasing every click of a button.
I have not yet created a unhandled exception is that something to do with that? And if it is unhandled exception how can I do it?
private void btnInventoryAddFanbelt_click(object sender, EventArgs e)
{
// CONTENTS
// labels
lblFanbeltName.Name = "lblFanbeltName";
lblFanbeltName.Text = "Fanbelt name";
lblFanbeltName.Visible = true;
lblFanbeltName.Location = new Point(10, 10);
// textbox
txtFanbeltName.Name = "txtFanbeltName";
txtFanbeltName.Visible = true;
txtFanbeltName.Location = new Point(115, 8);
// buttons
btnInventorySaveFanbelt.Name = "btnInventorySaveFanbelt";
btnInventorySaveFanbelt.Text = "Add fanbelt";
btnInventorySaveFanbelt.Visible = true;
btnInventorySaveFanbelt.Location = new Point(125, 50);
// END OF CONTENTS
// REMOVE CONTROLS
split3.Panel2.Controls.Clear();
// ADD CONTROLS
split3.Panel2.Controls.Add(lblFanbeltName);
split3.Panel2.Controls.Add(txtFanbeltName);
split3.Panel2.Controls.Add(btnInventorySaveFanbelt);
// EVENTHANDLERS
btnInventorySaveFanbelt.Click += new EventHandler(btnInventorySaveFanbelt_click);
}
private void btnInventorySaveFanbelt_click(object sender, EventArgs e)
{
MessageBox.Show("Fanbelt added");
}
Every time you click on btnInventoryAddFanbelt, you are adding another click event:
btnInventorySaveFanbelt.Click += new EventHandler(btnInventorySaveFanbelt_click);
Only add it once, usually best to do that in the constructor or through the designer. It can also be shortened to just:
btnInventorySaveFanbelt.Click += btnInventorySaveFanbelt_click;
Also, split3.Panel2.Controls.Clear(); does not dispose of the controls, it just removes them, so you are leaking memory here. Try using:
while (split3.Panel2.Controls.Count > 0) {
split3.Panel2.Controls[0].Dispose();
}

How to add several buttons and their click events dynamically in c# code behind?

Hi I want to add several buttons and their click events dynamically to my windows forms application in my code behind where my buttons will execute System.Diagnostics.Process.Start(targetURL); how can I acheieve this ?
You just need to create the button, set it's properties and event handlers and then add it to the Controls collection on the form.
var button = new Button();
try
{
button.Text = "Button 1";
button.Click += (sender, e) => System.Diagnostics.Process.Start(targetURL);
//Or if you don't want to use a lambda and would rather have a method;
//button.Click += MyButton_Click;
Controls.Add(button);
}
catch
{
button.Dispose();
throw;
}
//Only needed when not using a lambda;
void MyButton_Click(Object sender, EventArgs e)
{
System.Diagnostics.Process.Start(targetURL);
}
Declare your buttons variables.
Add the event handlers
Add them to the form Controls property.
Profit
You can add any control you like to the Controls collection of the form:
var targetURL = // ...
try
{
SuspendLayout();
for (int i = 0; i < 10; i++)
{
var button = new Button();
button.Text = String.Format("Button {0}", i);
button.Location = new Point(0, i * 25);
button.Click += (object sender, EventArgs e) => System.Diagnostics.Process.Start(targetURL);
this.Controls.Add(button);
}
}
finally
{
ResumeLayout();
}
When adding several controls to a parent control, it is recommended that you call the SuspendLayout method before initializing the controls to be added. After adding the controls to the parent control, call the ResumeLayout method. Doing so will increase the performance of applications with many controls.
You could write a user control that contains a textbox "txbURL", a button "btnNavigateToURL" and write the eventhandler of your button, to execute your code (System.Diagnostics.Process.Start(targetURL);)
Once you've done that, it's easy to add your control to your form on runtime, writing some code like this (don't have a c# editor right now, so you may have to verify the code)
MyControlClassName userControl = new MyControlClassName(string targetUrl);
userControl.Parent = yourForm;
yourForm.Controls.Add(userControl);
that's it.

Referring to programmatically created button and it's click event

I'm absolute beginner when it comes to mono for android.
I've used following code snippet to create 50 buttons programmatically:
for(int i=0;i<50;i++)
{
//code to calculate x and y position
btn=new Button(this);
//btn.SetBackgroundColor(Android.Resource.Color.);
btn.SetTextSize(Android.Util.ComplexUnitType.Sp,8);
btn.Text="Scrip "+i+"\n"+"CMP "+i+"\n"+"%Chg "+i;
lp = new RelativeLayout.LayoutParams(new ViewGroup.MarginLayoutParams((width+30)/5, (height-10)/10));
btn.LayoutParameters=lp;
lp.SetMargins(leftMargin,topMargin, 0, 0);
main.AddView(btn);
}
String str="";
btn.Click += (sender, e) =>
{
str=btn.Text;
Toast.MakeText(this, "Selected="+str,ToastLength.Short).Show();
Console.WriteLine("Selected="+str);
};
But one big problem with this code is at the end of the loop, btn object has reference of the last button only.
So if any button other than last button is pressed, button click event is not fired.
How to resolve it?
Ideally, it should return the text of the clicked button.
Also, referring to below screenshot, the default button style doesn't look good here. So I want to change it to exact rectangle and not rounded-rectangel(the default one).
Any idea on this?
As I'm very new to this, any help will be appreciated !!
EDIT
Asa result of your help, I'm able to create and refer all buttons appropriately.
But how to set their style as exact rectangle??
Move btn.Click += (sender, e) subscription inside for loop.
Even better - create one named method instead of creating many anonymous. E.g. Button_Click and subscribe to it:
btn = new Button(this);
btn.Click = Button_Click;
Inside that method you can cast sender object to Button and know which button was clicked.
UPDATE: here is complete code
const int rowsCount = 10;
const int columnsCount = 5;
int buttonsCount = rowsCount * columnsCount;
for (int i = 0; i < buttonsCount; i++)
AddButton();
I prefer not to use magic numbers in code :)
private void AddButton()
{
Button button = new Button(this);
button.Click += Button_Click;
// add text and other properties
main.AddView(button);
}
private void Button_Click(object sender, EventArgs e)
{
Button button = (sender as Button);
// use clicked button e.g. Console.WriteLine("Selected = {0}", button.Text);
}
Your for loop operates on a button and creates a new instance of it every time, but your click event is only added once (it is outside your for loop). Therefore it will only be added to the last instance of Button that is put into btn (the last button from the for loop).
You need to either create each button separately (putting them in a List say) and then outside the for loop you will have a reference to each that you can add the click event to each button individually. Or (the much better way) add the click event inside the for loop, so that each button you create adheres to it. Bear in mind that since the event will then be reached by an arbitrary button(one of your 50) that it will be best to use the sender parameter to determine its text value.
i.e.:
btn.Click += (sender, e) =>
{
Button b = sender as Button;
if ( b == null ) return;
String str;
str=b.Text;
Toast.MakeText(this, "Selected="+str,ToastLength.Short).Show();
Console.WriteLine("Selected="+str);
};
You have created 50 buttons, so you need to have 50 references to it. The easiest way to achieve this is to create an array of buttons, like so:
Button[] btns = new Button[50];
for(int i=0;i<50;i++)
{
{
//code to calculate x and y position
btns[i]=new Button(this);
//btn.SetBackgroundColor(Android.Resource.Color.);
btns[i].SetTextSize(Android.Util.ComplexUnitType.Sp,8);
btns[i].Text="Scrip "+i+"\n"+"CMP "+i+"\n"+"%Chg "+i;
lp = new RelativeLayout.LayoutParams(new ViewGroup.MarginLayoutParams((width+30)/5, (height-10)/10));
btns[i].LayoutParameters=lp;
lp.SetMargins(leftMargin,topMargin, 0, 0);
main.AddView(btn);
}
btns[i].Click += (sender, e) =>
{
String str= ( (sender as Button) != null) ? (sender as Button).Content.ToString() : "";
Toast.MakeText(this, "Selected="+str,ToastLength.Short).Show();
Console.WriteLine("Selected="+str);
}
}
//EDIT: You'll also need to create an eventhandler for each button
it's because you are setting the button click event outside the for loop. put it on the inside so it gets assigned to every button, not just the last one.

Categories