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
I have a Windows Forms Application that I need to assign an image to all 100 buttons, the problem is, that I need to do it randomly every time... Is there a faster way of doing this?
My first idea was to use the basic method of assigning that image to a variable and then assigning the image to the button:
Bitmap P_Farm = new Bitmap(#"IMAGE PATH.jpeg");
this.button1.Image = P_Farm;
But the problem with that is that I will need to do this for all 100 buttons.
this.button1.Image = P_Farm; // "P_Farm is just the path to the image"
this.button2.Image = P_Farm;
this.button3.Image = P_Farm;
this.button4.Image = P_Farm;
I want to keep my code as dry as possible.
The reason I can't just do it through the "Image" option in the "Properties" window is because eventually I will have a random image for every button on every load of the app. So first it will be
this.button1.Image = Z_Farm;
this.button2.Image = C_Farm;
this.button3.Image = P_Farm;
this.button4.Image = P_Farm;
then
this.button1.Image = P_Farm;
this.button2.Image = P_Farm;
this.button3.Image = Z_Farm;
this.button4.Image = Z_Farm;
I was wondering if it was possible to do something like reading every line in a text file but instead of the line changing with each try, the button changes
int i = 0;
while (true) // Something like this loop but changing not the line, but the button
{
this.button[i].image = P_Farm; // this obviously doesn't work
I++;
}
Hopefully this makes sense
Thanks a lot!
You can also loop through all the controls in your form, find the ones that are buttons and change their image that way. Of course you don't want to change them all. What I usually do is set a number to the Tag property:
foreach (Control control in Controls)
{
if (control is Button theButton && (int)theButton.Tag == 5)
{
theButton.Image = P_Farm;
}
}
This will not work if you have panels with buttons that you want to change too. You will have to write a recursive function that involves all the possible containers in your form such as panels.
If you want to change all the buttons in a container like a panel you would only change your foreach line to something like foreach (Control control in panel.Controls).
In the future, when you decide that not all buttons will have the same image, you could set an image based on the tag property like this:
foreach (Control control in Controls)
{
if (control is Button theButton && (int)theButton.Tag >= 5)
{
switch ((int) theButton.Tag)
{
case 100:
theButton.Image = P_Farm;
break;
case 101:
theButton.Image = Z_Farm;
break;
}
}
}
Because we are assuming that all buttons have an int in their tag property, you should add a number to all buttons, including those that should not change like your Cancel and Ok buttons. Something like a zero to exclude them from the image assignement.
I'm sure that there are better ways. I haven't done WinForms in a while.
I'm new working with C# and I'm asking on here because I didn't find a solution searching in google and other questions on SO, I will explain what my example application does:
When I run it it display a form with a textbox by default, this textbox always will be shown, after type some text and press enter it will generate a new textbox and a new button (all the controls even the default textbox are inside a panel), and the new textboxes have the same functionality as the default textbox, when I click on the button generated next to its textbox it removes the button itself and the textbox but after that if I remove some random textboxes it leaves a space between these controls, how can reorganize this content to dont let space between them?
As you can see in the image, can you tell me how can fix this or give me an advice to achieve this? thank you, by the way this is the method I use to generate the buttons and textboxes
private void GenerarTextBox()
{
panelContenedor.VerticalScroll.Value = panelContenedor.VerticalScroll.Minimum;
TextBox tb = new TextBox();
tb.Text = "Prueba " + id;
tb.Name = "txtBox" + id;
tb.KeyDown += new KeyEventHandler(TextBox_Keydown);
Button bt = new Button();
bt.Cursor = Cursors.Hand;
bt.Text = "X";
bt.Name = "btnPrueba" + id;
bt.Click += new EventHandler(ClickBotones);
Point p = new Point(20, 30 * id);
Point pb = new Point(130, 30 * id);
tb.Location = p;
bt.Location = pb;
panelContenedor.Controls.Add(tb);
panelContenedor.Controls.Add(bt);
tb.Focus();
id++;
}
And this to remove the textboxes and the buttons
private void ClickBotones(object sender, EventArgs e)
{
Button bt = sender as Button;
string nombreBoton = bt.Name;
string idBoton = nombreBoton.Substring(9);
string nombreTextBox = "txtBox" + idBoton;
foreach (Control item in panelContenedor.Controls.OfType<Control>())
{
if (item.Name == nombreTextBox)
{
panelContenedor.Controls.Remove(item);
panelContenedor.Controls.Remove(bt);
}
}
}
You could place your dynamic controls on a FlowLayoutPanel. Either directly or grouped together in a Panel or UserControl.
Set the FlowDirection property of the FlowLayoutPanel to TopDown. The FlowLayoutPanel will then arrange your controls automatically. You can also set the WrapContents property to False and AutoScroll to true to make the scroll bar appear.
Alternatively you can use FlowDirection = LeftToRight, place the text box and the button directly on the FlowLayoutPanel and let the child controls wrap (WrapContents = True). In the child controls, a new property FlowBreak appears. It can be set to True for the last control to appear in a row and let the next one wrap independently of the width of the FlowLayoutPanel.
You can also play with the Margin property of the child controls to control their layout in the FlowLayoutPanel as the Location property becomes useless.
The FlowLayoutPanel (as well as the Panel) is available in the Toolbox in the section "Containers".
When you delete the controls, you need to do a recalc of the positions. So when you have added them in sequence, you can go with:
bool repos = false;
Point p;
foreach (Control item in panelContenedor.Controls.OfType<Control>())
{
if (repos)
{
Point tmp = item.Location;
item.Location = p;
p = tmp;
}
if (item.Name == nombreTextBox)
{
panelContenedor.Controls.Remove(item);
panelContenedor.Controls.Remove(bt);
repos = true;
p = item.Location;
}
}
This question already has answers here:
Images in ListView subitem
(2 answers)
Closed 9 years ago.
I wish to make a list of items with pictures, the amount of items can vary from 1-60 and for each item I wish to also show data.
I believe the best way of going about this is using the ListView in c#.
is this true and if so how would I go about doing this?
i have also thought about using interactive images within a scrolling window
If you want to do this in the designer, you can take the following steps to add the
images to the ListView control:
Switch to the designer, click on the ImageList component on the Component Tray,
there will be a smart tag appear on the top-right corner of the ImageList.
Click the smart tag, and click "Choose Images" on the pane.
On the pop-up Image Collection Editor dialog, choose the images from the folder
your want.
Click OK to finish adding images to the ImageList.
Click the ListView on the form, there will be a smart tag appear on the top-right
corner.
Click the smart tag, you will find there're three ComboBoxes there, choose a
ImageList from the list as you want.
Click the "Add items" option on the smart tag, a ListViewItem Collection Editor
will appear, you can add items to the ListView, it's important here to set the
ImageIndex or ImageKey property, or the image won't appear.
Click OK to finish item editing, now you'll find the images are displayed on the
ListView.
If you want to add the images to the ListView by code, you can do something like this`
Code Snippet
private void Form10_Load(object sender, EventArgs e)
{
DirectoryInfo dir = new DirectoryInfo(#"c:\pic");
foreach (FileInfo file in dir.GetFiles())
{
try
{
this.imageList1.Images.Add(Image.FromFile(file.FullName));
}
catch{
Console.WriteLine("This is not an image file");
}
}
this.listView1.View = View.LargeIcon;
this.imageList1.ImageSize = new Size(32, 32);
this.listView1.LargeImageList = this.imageList1;
//or
//this.listView1.View = View.SmallIcon;
//this.listView1.SmallImageList = this.imageList1;
for (int j = 0; j < this.imageList1.Images.Count; j++)
{
ListViewItem item = new ListViewItem();
item.ImageIndex = j;
this.listView1.Items.Add(item);
}
}
Source
How do I build an array of buttons in a Winforms application?
What I am trying to do is this: I have a lot of buttons in a sort of calendar arrangement, that are indicating time slots. IE: Monday0700Button, Monday0730Button, Monday0800Button, and so on in 30min intervals.
I have a xml database, where one of the fields for appointments is <Duration> When the duration = 0.5hrs, and the <Time> field equals "07:00am", to color the 'Monday0700Button'. When the Duration is 1.0hrs, I want it to populate 'Monday0700Button' as well as the following time slot button of 'Monday0730Button'.
Any ideas?
Thanks.
Yes, you can build a list of buttons like below.
List<Button> listOfButtons = new List<Button>();
listOfButtons.Add(yourButton);
Yes, it's no problem to build an array of Buttons, or any object.
You won't be able to see them in the Visual studio designer, but they'll work just fine.
A long time ago I used a 2-D array of buttons to build the UI for an calculator app. I had used an HP-15C for a long time, and missed it.
The array approach worked fine.
Button[] numberButtons=new Button[] { btn0, btn1, btn2, btn3, btn4, btn5, btn6, btn7, btn8, btn9, btnDecimalPt};
Button[] operationButtons=new Button[] { btnDiv, btnMult, btnSubtract, btnAdd };
foreach (var b in numberButtons)
b.Click += new System.EventHandler(this.Number_Click);
foreach (var b in operationButtons)
b.Click += new System.EventHandler(this.Operation_Click);
// etc
Button[][] allButtons=
{
new Button[] {btnSqrt, btnExp, btn10x, btnPow,btnMultInverse, btnCHS, null, null, null, null},
new Button[] {btnN, btnInterest, btnPMT, btnPV, btnFV, null, btn7, btn8, btn9, btnDiv},
new Button[] {btnLn, btnLog, btnSine, btnCosine, btnTangent, btnPi, btn4, btn5, btn6, btnMult},
new Button[] {btnRoll, btnSwap, btnCLRfin, btnCLX, btnCLR, btnEnter, btn1, btn2, btn3, btnSubtract},
new Button[] {btnInt, btnFrac, btnFix, btnStore, btnRecall, null, btn0, btnDecimalPt, btnNotUsed, btnAdd}
};
// programmatically set the location
int col,row;
for(row=0; row < allButtons.Length; row++)
{
Button[] ButtonCol= allButtons[row];
for (col=0; col < ButtonCol.Length; col++)
{
if (ButtonCol[col]!=null)
{
ButtonCol[col].TabIndex = col + (row * allButtons.Length) +1;
ButtonCol[col].Font = font1;
ButtonCol[col].BackColor = System.Drawing.SystemColors.ControlDark;
ButtonCol[col].Size=new System.Drawing.Size(stdButtonWidth, stdButtonHeight);
ButtonCol[col].Location=new Point(startX + (col * stdButtonWidth),
startY + (row * stdButtonHeight) ) ;
}
}
}
Yep, definitely possible, but probably unnecessary.
If I understand you correctly, you should be able to add a FlowLayoutPanel to your Form and then loop through your XML, instantiating a new Button, as necessary. Wire up the event handler for the Click event, then add the button to the FlowLayoutPanel by calling the Add() method off of the Controls property on your FlowLayoutPanel.
while (reader.Reader())
{
// Parse XML here
// Instantiate a new button that will be added to your FlowLayoutPanel
Button btn = new Button();
// Set button properties, as necessary
btn.Text = "Foo";
btn.Click += new EventHandler(SomeButton_Click);
// Add the button to the FlowLayoutPanel
flowLayoutPanel.Controls.Add(btn);
}
While a FlowLayoutPanel makes it easy to do the layout for your buttons, it might not work for you. If that's the case, you will have to work out the X and Y coordinates for your buttons as you loop through the XML.
One problem that you will encounter with the above approach is that it always calls the exact same event handler. As a result, you will have to come up with a way to determine which button has been clicked. One approach might be to extend the Button control to provide additional properties that can be used to acknowledge the time period.
Buttons, like all GUI elements, are objects just like any other (that also happen to be displayable). So yes, you can have arrays, lists, dictionaries - whatever you want containing buttons. Taylor L's response has some sample code.
Yes, this is possible, as Taylor L demonstrated. The only catch is that VB6-style control arrays, created by copying and pasting the control, can no longer be done in the forms editor.