Referring to programmatically created button and it's click event - c#

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.

Related

How to create an event handler for multiple buttons using a for loop?

I want to set 50 button's click event to - private void buttonOnOff(object sender, EventArgs e).
It works when I write my code like this:
public Form1()
{
InitializeComponent();
button1.Click += button_Click;
button2.Click += button_Click;
button3.Click += button_Click;
.......
}
But is it possible do do this using a for loop. When I try I get the following error: Object reference not set to an instance of an object.
The error is on this line of code: Controls[buttonName].Click += buttonOnOff;
Here is the code I'm currently using:
public Form1()
{
InitializeComponent();
string buttonName;
for (int i = 1; i < 51; i++) // Does a check for each button.
{
buttonName = "button" + i;
Controls[buttonName].Click += buttonOnOff;
}
}
You can iterate over the forms controls (and even specifically buttons using Linq) - something like this:
foreach (Button b in this.Controls.OfType<Button>())
{
// here, you could check the name, if necessary
// and add your handler
b.Click += buttonOnOff;
}
Elevating some good comments to add to the answer:
As #mars-red and #donboitnott point out, don't allow this code to execute more than once. In your current example, you are doing it in the Form's constructor, so that is good.
Also, #mars-red mentions the Tag property of your buttons. If you had other buttons on your form and didn't want to try to figure out which buttons, you could add a specific value to the Tag of each and just check that - so, something like this:
foreach (Button b in this.Controls.OfType<Button>())
{
if (b.Tag.ToString() == "SomeIndicator")
{
b.Click += buttonOnOff;
}
}
And, of course, you could also just look for a particular pattern in the names of the buttons.
Your "object reference" issue doesn't make sense if you are using this code. The iterator is giving you specific instances of buttons, so "b" should always be a button in the loop.
Final Update:
The main issue was that the Form wasn't the "container" of the button controls (they were in a panel). So, instead of using the Form's controls, the user should have been using the Panel's controls (something like this):
SomePanel.Controls[buttonName].Click += buttonOnOff;
At first declare a control array variable and add your buttons to it.
Control[] B_list = new Control[] { Button1, Button2, Button3, ...... }
Then in the for loop, use these serially like
Button btn;
for(int index = 0; index < 50; index++)
{
btn = (Button)B_list[index];
btn.Click += new EventHandler(buttonOnOff);
}
OK, undeleting this in request of #Servy:
In the loop you are getting the button controls by name from the controls list. Your exception "Object reference not set to an instance of an object" simply means that there is no object with this name in the list.
So either there is a button missing or named else in your 1-50 set.
If you are using #snow_FFFFFF's solution and the error persists that is really strange.
Nevertheless, as I already suggested: debug the loop! Check on which button name/instance it crashes, check the contents of Controls (you have to go a bit down the private members) etc

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

Adding actions to programmatically added buttons

I am creating a program where it is required to have a list of buttons which relate to different things on the host computer.
I have achieved this as shown in the code below (example code):
Button btn = new Button();
btn.Name = "Button1";
btn.Text = "Item 1";
this.formLayoutProject.Controls.Add(btn);
This will eventually go into a for loop and more buttons will be on the flow layout, but i am starting small scale and working up so i don't get problems later.
This produces the button as expected (formatting will be done here too but haven't got that far).
What I am stuck on is how would I add a click event/right click event context menu onto the button?
I can put the click events in the code for buttons and items which will be fixed on the form and they work but I don't know how to do this dynamically as the buttons will be different on each target machine.
I have searched on Google but I haven't found what im looking for (probably due to the fact im not sure what the keywords are for the search).
How would I accomplish this?
You can add a button click event handler by using += operator on the event of your new btn object:
// Add 10 buttons dynamically.
// Bind to the same method.
for (var i = 1; i <= 10; i += 1)
{
Button btn = new Button();
btn.Name = "Button" + i;
btn.Text = "Item " + i;
this.formLayoutProject.Controls.Add(btn);
// Add handler.
btn.Click += btn_Click;
}
Now you just need to define the handler method:
private void btn_Click(object sender, EventArgs e)
{
// This is the button which was clicked.
var button = (Button)sender;
var buttonName = button.Name;
// Do some stuff.
// To detect certain mouse click events, cast e as a MouseClick.
MouseEventArgs mouseEvents = (MouseEventArgs)e;
// Now use the mouseEvents object to handle specific events based on the button clicked.
if (mouseEvents.Button == MouseButtons.Right)
{
// Right button clicked.
}
else if (mouseEvents.Button == MouseButtons.Left)
{
// Left button clicked.
}
}

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.

Events and Buttons

I want to get the text of the button whenever I click on it.
The algorithm that I made is where i have a function that is a loop that creates a number of buttons and assigns numbers:
void ListAllPage()
{
if (pageMax < 50)
{
//if page max less than 50
for (int i = 0; i < pageMax; i++)
{
Button newBtn = new Button();
newBtn.Text = i.ToString();
newBtn.Width = 50;
newBtn.Click += page_Clicked;
pageCell.Controls.Add(newBtn);
}
}
}
Now buttons will appear on the screen, their events will be triggered and the function page_Click; will be executed:
public void page_Clicked(object sender, EventArgs e)
{
//inside this function I want to obtain the button number that was clicked by the user. How do I do that?
}
Take note, I must all the functions that I described here,...
My thinking is to feed all the buttons that i created inside the loop to a dictionary..
Dictionary.. it will take variables like this btndic.Add(Button b=new Button,b.text);
But the issue is how to retrieve the buttons,,,
if there is a better way, i would like to hear about it...
instead of using the Click Event -> Use the Command Event: http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.button.oncommand.aspx then you can distinguish which button has been clicked
You just need to cast the sender object to a Button, or more generally, a Control:
public void page_Clicked(object sender, EventArgs e)
{
Control c = sender as Control;
MessageBox.Show("Clicked on " + c.Text);
}
Also, it might be more appropriate to use the Tag property to store your custom information (number). In that case, Text property can be anything you like.
Try this way
public void page_Clicked(object sender, EventArgs e)
{
Button btn=(Button)sender;
}
in your ListAllPage method assign Tag to each button:
newBtn.Tag = i;
In your handler you can obtain button instance from sender:
var clickedButton = (Button)sender;
int pageIndex = (int)clickedButton.Tag;

Categories