I'm looping a collection of strings, coming from a database; for each entry, I create a new Button which is then added to a FlowLayoutPanel.
The Text of each Button is set to the current item in the string collection.
I'd like to assign an EventHandler to the Click event of each Button, however I am only able to access the Properties of a Button.
I have to cast the last entry of the FlowLayoutPanel's Controls collection to Button and add the Event Handler to this instance.
Does anyone know the reason why I can't access anything else then Properties? Is there a cleaner way of coding that?
List<string> temp = Database.GetNames();
foreach(string s in temp)
{
flp_main.Controls.Add(new Button()
{
Text = s.name
});
Button b = (Button)flp_main.Controls[flp_main.Controls.Count - 1];
b.Click += B_Click;
}
Asking for Improvements on Code Quality
Related
Has C# indexed control arrays or not? I would like to put a "button array" for example with 5 buttons which use just one event handler which handles the index of all this 5 controls (like VB6 does). Else I have to write for each of these 5 buttons one extra event handler. And if I have 100 buttons, I need 100 event handlers? I mean something like that:
TextBox1[i].Text="Example";
It could make coding definitely easier for me to work with control arrays. Now I have seen, that C# at least has no visible array functionality on user controls and no "index" property on the user controls. So I guess C# has no control arrays, or I must each element call by known name.
Instead of giving 100 TextBoxes in a for loop 100 incrementing values, I have to write:
TextBox1.Text = Value1;
TextBox2.Text = Value2;
...
...
TextBox100.Text = Value100;
A lot of more work + all these 100 event handlers each for one additional TextBox extra.
I know I'm a little late to this party, but this solution will work:
Make a global array:
TextBox[] myTextBox;
Then in your object's constructor, after the call to
InitializeComponent();
initialize your array:
myTextBox = new TextBox[] {TextBox1, TextBox2, ... };
Now you can iterate your array of controls:
for(int i = 0; i < myTextBox.Length; i++)
myTextBox[i].Text = "OMG IT WORKS!!!";
I hope this helps!
Pete
As I mentioned in comment to a solution by HatSoft, C# Winforms does not allow you to create control arrays like old VB6 allowed us. The nearest I think we can get to is what HatSoft and Bert Evans in their posts have shown.
One thing that I hope would satisfy your requirement is the event handler, you get a common event handler and in the event handler when you typecast the "sender" you get the control directly just like you would in VB6
C#
TextBox textBox = sender as TextBox;
VB6
TextBox textBox = TextBox1[i];
So the only trouble you might have is wiring those 100 TextBoxes to a single event handler, if you are not creating the controls dynamically through code rather creating it manually at design time then all one can suggest is group them in a container like say Panel. Then on Form Load wire them all up to a single event handler like this:
foreach (Control control in myTextBoxPanel.Controls)
{
if(control is TextBox)
control.TextChanged += new EventHandler(control_TextChanged);
}
Just create one handler and point all the buttons to it.
var ButtonHandler = (sender, args) => {
var clicked = (Button)sender;
if (clicked.Text == "whatever")
//do stuff
else
//do other stuff
};
button1.Click += ButtonHandler;
button2.Click += ButtonHandler;
Alternatively, if you are creating controls in code, you could use one of the techniques specified in this answer.
Instead of giving 100 TextBoxes in a for loop 100 incrementing values, I have to write:
for(int i = 0; i <100; i++)
{
TextBox t = new TextBox(){ Id = "txt_" + i, Value = "txt_" + i};
t.TextChanged += new System.EventHandler(this.textBox_Textchanged);
Page.Controls.Add(t);
}
//and for event on TextChanged
private void textBox_Textchanged(object sender, EventArgs e)
{
TextBox textBox = sender as TextBox;
if (textBox != null)
{
////
}
}
Another thing to note: if you really need to edit 100 strings on one form, you should probably think about whether 100 text boxes is really the best way to do it. Perhaps a ListView, DataGridView, or PropertyGrid would be better suited.
This applies almost any time you think you need a huge array of controls.
If you are working with Web Forms and not MVC, you can acces a collection of controls on the page as shown in Using the Controls Collection in an ASP.NET Web Page. Essentially the controls collection is a tree with the page hosting the first level of child controls and some items having children of their own. See How to: Locate the Web Forms Controls on a Page by Walking the Controls Collection for an example of how to follow the tree.
Also, see How to: Add Controls to an ASP.NET Web Page Programmatically.
You can use the same event handler for multiple items as long as the signature required is the same.
For Windows Forms this is nearly identical since they're based on similar architectural models, but you'll want Control.Controls Property and How to: Add Controls to Windows Forms.
Keeping it simple:
TextBox[] keybox = new TextBox[16]; //create an array
for (int i=0; i<16; i++)
{
keybox[i] = new TextBox(); //initialize (create storage for elements)
keybox[i].Tag = i; //Tag prop = index (not available at design time)
keybox[i].KeyDown += keybox_down; //define event handler for array
}
private void keybox_down(object sender, KeyEventArgs e)
{
int index = (int)((TextBox)sender).Tag //get index of element that fired event
...
}
so, I have a form that is dynamically populated with textboxes and buttons.
How can I create EventHandlers for each of those buttons dynamically (ex: it generates 20 buttons, I need 20 eventhandlers). Each button will have the same function (to delete something from a database) but I need the program to know whenever any one of them is clicked to trigger that code.
// also, the button creation code is within a while() so I can't use it ouside that while (just pointing that out)
Code:
public void LoadElements()
{
//more code here
while(some condition)
{
// more code above
Button b = new Button();
b.Text = "Delete";
b.Name = "button" + j;
b.Location = new Point(240, Y);
Controls.Add(b);
// more code bellow
}
// more code here
}
Assign them like you would for any other event in your code. You can simply add an event handler doing something like:
b.Click += b_Click
Add in the loop:
b.Click+=New Eventhandler(b_Click);
(Just press TAB twice after typing b.Click+=).
Define the function b_Click outside of the loop. It will be invoked when anyone of those button is clicked.
I read this topic (Adding buttons to a TabControl Tab in C#) but I don't figure out why my code below add one button only to the tabpage.
I've obviously debugged that the foreach works properly.
foreach (string line in File.ReadAllLines(#"C:\quicklauncher.ini"))
{
TabPage page = new TabPage(foldername);
DirectoryInfo d = new DirectoryInfo(line);
foreach (FileInfo file in d.GetFiles("*.*"))
{
Button button = new Button();
button.Text = file.Name;
button.Click += new EventHandler(button_Click);
page.Controls.Add(button);
}
tabControl.TabPages.Add(page); //add our tab page to the tab control
}
Thanks,
Steve
You thought it added only 1 button for you but in fact it did not, it added all the buttons for you but those buttons had the same Location (which is (0,0) by default). That's why you did think there was only 1 button (because you saw only 1 last button on top of others).
You added buttons automatically to your tabpage, so you should have some rule to locate them, I'm not sure what that rule is but I suppose you want to line them up vertically (just an example), I'm going to correct your code to achieve such a thing, at least you will see it work, and in fact all the buttons are added normally:
//you need some variable to save the next Top for each new button:
//let's call it nextTop:
int nextTop = 0;
foreach (FileInfo file in d.GetFiles("*.*"))
{
Button button = new Button { Top = nextTop,
Text = file.Name };
button.Click += new EventHandler(button_Click);
page.Controls.Add(button);
nextTop += button.Height + 5; //it's up to you on the
//Height and vertical spacing
}
//...
You can also try using some layout control like FlowLayoutPanel and TableLayoutPanel to contain all the buttons, they can help arrange your buttons in some way you may want, just try it.
I want to create buttons or list of items on the basis of number of items in my database or from list of items in my array and for each item create a onclick function for either buttons or any list of items
How about:
int y = 10;
foreach (string name in names)
{
Button button = new Button();
button.Text = name;
button.Position = new Point(10, y);
y += 20;
button.Click += HandleButtonClick;
Controls.Add(button);
}
You might also store the buttons in an array or a list... there's nothing particularly special about GUI controls that stops you from creating them at execution time just like any other object.
If that doesn't help, please give more information about what you need to do that the above doesn't help you with.
I have done it also by looking at Visual Studio code.
My background is pretty much ASP.Net, and I was asked to develop a small windows application. I tried to use a grid to present and select data, and I tough that the equivalent in windows forms to the ASP.Net's GridView was DataGridView. I'm not sure yet if that is the case, basically, in ASP.Net, you have the _RowCommand event associated to the grid, that is triggered after a Commandbutton is clicked. I noticed also that there is no such thing as a DataKeyNames property, so I don't know how to pass the current row key to the button clicked. Any help will be appreciated, thanks!
I forgot to mention: My grid has two DataGridViewButton type of columns, And I don't know the event that I need to code on to perform the selected command
The event you are looking for is the CellClick event - with the DataGridViewButtonColumn you do not actually associate event handlers to the particular buttons as you would with other buttons on your form.
From the event arguments returned by the cell click event you can then work out which row and column the click was in and from that what action you want to take.
private void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e)
{
DataGridViewButton cell = (DataGridViewButtonCell)
dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex];
// Any additional logic you want
}
Beyond that however, I think you would really benefit from taking a step back and thinking about the differences between the winforms and webforms coding paradigms.
With webforms a lot of the way you code is dictated by the fact that everything is stateless. This is not the case with winforms, you have state and you can access most controls when you need to find out information about them.
For this reason, retrieving information like the currently selected cell in a DataGridView is trivial with winforms.
Also, in most cases with winforms you do not need specific buttons to edit or delete - you can edit directly in the grid, and use in inbuilt delete functionality (select the row and press the delete key).
A good place the get you past some of the bumps might be the DataGridView FAQ. It's an amazing resource for learing about the DataGridView
DataGridView.CurrentRow gets the selected row. Is that what you need?
If you are databound, you should have (via .CurrentRow) access to all the properties, by casting .CurrentRow.DataBoundItem to whatever type you need. Otherwise, just look at the cells, or set a .Tag against the rows when you add them.
Here's an example showing a data-bound DataGridView and a couple of buttons, pulling out data for the selected row:
using System;
using System.ComponentModel;
using System.Windows.Forms;
class Person
{
public string Name { get; set; }
[DisplayName("Eye Color")]
public string EyeColor { get; set; }
}
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
using (var form = new Form())
using (var grid = new DataGridView { Dock = DockStyle.Fill})
using (var btn1 = new Button { Dock = DockStyle.Bottom, Text = "Button 1"})
using (var btn2 = new Button { Dock = DockStyle.Bottom, Text = "Button 2" })
{
btn1.Click += delegate
{
form.Text = "Button 1 clicked";
if (grid.CurrentRow != null)
{
form.Text += ": " + ((Person)grid.CurrentRow.DataBoundItem).Name;
}
};
btn2.Click += delegate
{
form.Text = "Button 2 clicked";
if (grid.CurrentRow != null)
{
form.Text += ": " + ((Person)grid.CurrentRow.DataBoundItem).Name;
}
};
form.Controls.Add(btn1);
form.Controls.Add(btn2);
form.Controls.Add(grid);
var data = new BindingList<Person>
{
new Person { Name = "Fred", EyeColor = "green"},
new Person { Name = "Barney", EyeColor = "brown"},
new Person { Name = "Wilma", EyeColor = "blue"},
new Person { Name = "Betty", EyeColor = "blue"},
};
grid.DataSource = data;
Application.Run(form);
}
}
}
There are other ways of handling the click events, and usually much of the above would be done via the designer rather than like this (but it is very hard to show designer code in a snippet).