Auto arrange unlimited number of buttons on form - c#

I'm trying generate dynamically a bunch of buttons. The number of buttons is determined by how many pits are alive, and this is retrieved from a database.
Here is how it looks like :
Here is what I used to generate them :
private void Form1_Load(object sender, EventArgs e)
{
int numOf = pits.numOfPits();
Button[] genButtons = new Button[numOf];
int l = 1;
for (int i = 0; i < numOf; i++)
{
genButtons[i] = new Button();
genButtons[i].Width = 160;
genButtons[i].Height = 80;
Point location = new Point((i + 1) *200,75);
if (location.X > this.ClientSize.Width - 200)
{
location.X = l * 200;
location.Y += 100;
l++;
}
genButtons[i].Location = location;
this.Controls.Add(genButtons[i]);
}
}
The problem is that it will only work for 2 rows and it is kind of hard coded. How can I improve it to support an unlimited number of buttons.
You can ignore the limited "height" of form for the purpose of this question. I'll probably add some pagination later.

Add button controls to the FlowLayoutPanel. Extend the FlowLayoutPanel to include items per page, current page, and data to implement paging.
private void Form1_Load(object sender, EventArgs e)
{
FlowLayoutPanel flp = new FlowLayoutPanel();
for (int i = 0; i < pits.numOfPits(); ++i)
{
Button btn = new Button();
btn.Width = 160;
btn..Height = 80; //set padding or margin to appropriate values
flp.Controls.Add(btn);
}
this.Controls.Add(flp);
}

Related

Creating a new control property for certain amount of time in C# Winform

Say if I want to create a multiple button on my form based on a loop value of 3 then 3 buttons should be created into that form, in my case I have this textbox input that should determine the loop value on button click. What I've tried:
void Button4Click(object sender, EventArgs e)
{
int get_col_range = Convert.ToInt32(textBox3.Text);
for(int i=0; i<get_col_range; i++) {
Button btn = new Button();
btn.Text = Convert.ToString(i);
this.Controls.Add(btn);
}
}
I put on value of 2 on the TextBox input as test value but turned out that nothing happened why?.
Try the following on a form with no controls in a button click event.
int top = 10;
int heightPadding = 30;
for (int index = 0; index < 3; index++)
{
var button = new Button()
{
Text = $"Button {index}",
Name = $"Button{index}",
Location = new Point(10, top)
};
Controls.Add(button);
top += heightPadding;
}

Randomly put an image in specific places using C#

I am absolutely new with C#, so I hope my question is not completely off.
As you can see in the picture above, I have a form, in which there is a table (image) and a button. In the resources of the project, I have another image (black_rectangle.png), which is a black rectangle, exactly at the same size of each the table's cell. This is what I'm trying to achieve:
Each time the 'Again' button is clicked, I want the six black rectangles to cover two of the tree cells in each column, in a random manner. For example, after the first try, the table could look like this:
I'm basically stuck at the beginning:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Random rand = new Random();
List<PictureBox> items = new List<PictureBox>();
PictureBox newPic= new PictureBox();
newPic.Height = 50;
newPic.Width = 50;
newPic.BackColor = Color.Maroon;
int x = rand.Next(10, this.ClientSize.Width - newPic.Width);
int y = rand.Next(10, this.ClientSize.Height - newPic.Height);
newPic.Location = new Point(x, y);
}
}
Assuming the table (image) is in "pictureBox1", you could try something like:
private Random rnd = new Random();
private List<int> positions = new List<int> { 0, 1, 2 };
private List<PictureBox> prevBoxes = new List<PictureBox>();
private void button1_Click(object sender, EventArgs e)
{
prevBoxes.ForEach(pb => pb.Dispose()); // remove previous boxes
for(int col=0; col<3; col++)
{
positions = positions.OrderBy(i => rnd.Next()).ToList(); // shuffle positions
for(int i=0; i<2; i++)
{
PictureBox newPic = new PictureBox();
newPic.Height = 50;
newPic.Width = 50;
newPic.BackColor = Color.Maroon;
newPic.Location = new Point(pictureBox1.Left + (col * 50), pictureBox1.Top + (positions[i] * 50));
this.Controls.Add(newPic);
newPic.BringToFront();
prevBoxes.Add(newPic);
}
}
}
Output:

How to create labels for each element?

I have the following code. On form load, I want to create multiples labels, the first should be on position (20,0), the second on the (40,0) and till the last label. But the program just shows the first label, I mean label 0, and that's all.
How to fix this?
private void Form1_Load(object sender, EventArgs e)
{
Label[] nmr = new Label[10];
for(int i=0; i<10; i++)
{
nmr[i] = new Label();
nmr[i].Text = "label " + i;
nmr[i].Left += 20;
this.Controls.Add(nmr[i]);
}
}
nmr[i].Left = 20 * (i+1);
will calculate the distance you want. Yet, you will only see one label because the first label is too long. So you have to adjust its size:
nmr[i].Size = new Size(40, 15);
Then you will see that 20 pixels is way too small as a distance; the labels will overlap
private void Form3_Load(object sender, EventArgs e)
{
Label[] nmr = new Label[10];
for (int i = 0; i < 10; i++)
{
nmr[i] = new Label();
nmr[i].Text = "label " + i;
nmr[i].Location = new Point(0, 25 * i);
this.Controls.Add(nmr[i]);
}
this.Height = this.Height + (25 * nmr.Count());
}
you also need to resize your form as well,
this code will help you,
In your loop, replace
nmr[i].Left +=20;
with
nmr[i].Left = 20 * (i + 1);
You should increase the Left value by 20 for each iteration. I also don't see why you are populating an array since you just add the Labels to the Controls collection. Try this:
private void Form1_Load(object sender, EventArgs e)
{
for (int i = 1; i < 11; i++)
{
var label = new Label();
label.Text = "label " + i;
label.Left += 20 * i;
this.Controls.Add(label);
}
}
Actually, you don't edit the right property. The right one would be control.Location which is a Point with the properties x and y.
To add them with 20 for every loop, you actually need to go like (20 * (i+1))
Example code that worked:
private void Form1_Load(object sender, EventArgs e)
{
Label[] nmr = new Label[10];
for (int i = 0; i < 10; i++)
{
nmr[i] = new Label();
nmr[i].Text = "label " + i;
nmr[i].Location = new Point(0, (20 * (i+1)));
this.Controls.Add(nmr[i]);
}
}
EDIT: Work on that 20 pt. Seems like the labels won't show right. Maybe try 30pt?
Try using Linq in order do generate Labels:
using System.Linq;
...
private void Form1_Load(object sender, EventArgs e) {
Label[] nmr = Enumerable
.Range(0, 10)
.Select(i => new Label() {
Text = $"label {i}",
Left = 20 + i * 20, // <- please, notice Left computation
Parent = this, })
.ToArray();
}

Create dynamic buttons in a grid layout - Create a magic square UI

I'm supposed to create a magic square in 2D using Windows Forms Application. It should look like this:
However, the user should be able to decide the size of the square (3x3, 5x5, 7x7, etc). I already wrote the code in a Console Application, but I don't know how to add the 2D graphics.
Somebody already asked this question (How do I put my result into a GUI?), and one of the answers was to use DataGridView, but I'm not sure if that's what I'm looking for, since I can't make it look like the picture.
Any ideas or advice?
You can use a TableLayoutPanel and add buttons to panel dynamically.
If you don't need interaction with buttons, you can add Label instead.
Create square dynamically:
public void CreateSquare(int size)
{
//Remove previously created controls and free resources
foreach (Control item in this.Controls)
{
this.Controls.Remove(item);
item.Dispose();
}
//Create TableLayoutPanel
var panel = new TableLayoutPanel();
panel.RowCount = size;
panel.ColumnCount = size;
panel.BackColor = Color.Black;
//Set the equal size for columns and rows
for (int i = 0; i < size; i++)
{
var percent = 100f / (float)size;
panel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, percent));
panel.RowStyles.Add(new RowStyle(SizeType.Percent, percent));
}
//Add buttons, if you have your desired output in an array
//you can set the text of buttons from your array
for (var i = 0; i < size; i++)
{
for (var j = 0; j < size; j++)
{
var button = new Button();
button.BackColor = Color.Lime;
button.Font = new Font(button.Font.FontFamily, 20, FontStyle.Bold);
button.FlatStyle = FlatStyle.Flat;
//you can set the text of buttons from your array
//For example button.Text = array[i,j].ToString();
button.Text = string.Format("{0}", (i) * size + j + 1);
button.Name = string.Format("Button{0}", button.Text);
button.Dock = DockStyle.Fill;
//If you need interaction with buttons
button.Click += b_Click;
panel.Controls.Add(button, j, i);
}
}
panel.Dock = DockStyle.Fill;
this.Controls.Add(panel);
}
If you need interaction with buttons
void button_Click(object sender, EventArgs e)
{
var button = (Button)sender;
//Instead put your logic here
MessageBox.Show(string.Format("You clicked {0}", button.Text));
}
As an example, you can call
CreateSquare(3);
Screenshot:
You can create a Form and add a TableLayoutPanel with this property
tableLayoutPanel1.Dock = DockStyle.Fill;
tableLayoutPanel1.BackColor = Color.Gold;
and this is the result
When you create Row and Column, to fit correctly set the percentage in this way:
After this you can add a Button or Label in each square.

Performance issue when adding controls to a panel in C#

I am creating a panel and then adding some labels/buttons to it to form a grid. The issue is that if I add more than say 25x25 items to the panel, there is a terrible performance hit. I can resize the form ok but when I scroll the panel to see all the labels the program lags, the labels/buttons tear or flicker, and sometimes it can make the program unresponsive. I have tried adding the controls to a "DoubleBufferedPanel" that I created. This seems to have no effect. What else could I do? Sorry for such a large code listing. I didn't want to waste anyone's time.
namespace GridTest
{
public partial class Form1 : Form
{
private const int COUNT = 50;
private const int SIZE = 50;
private Button[,] buttons = new Button[COUNT, COUNT];
private GridPanel pnlGrid;
public Form1()
{
InitializeComponent();
pnlGrid = new GridPanel();
pnlGrid.AutoScroll = true;
pnlGrid.Dock = DockStyle.Fill;
pnlGrid.BackColor = Color.Black;
this.Controls.Add(pnlGrid);
}
private void Form1_Load(object sender, EventArgs e)
{
int x = 0;
int y = 0;
int offset = 1;
for (int i = 0; i < COUNT; i++)
{
for (int j = 0; j < COUNT; j++)
{
buttons[i, j] = new Button();
buttons[i, j].Size = new Size(SIZE, SIZE);
buttons[i, j].Location = new Point(x, y);
buttons[i, j].BackColor = Color.White;
pnlGrid.Controls.Add(buttons[i, j]);
x = x + SIZE + offset;
}
x = 0;
y = y + SIZE + offset;
}
}
}
}
Also, the GridPanel class:
namespace GridTest
{
public class GridPanel : Panel
{
public GridPanel()
: base()
{
this.DoubleBuffered = true;
this.ResizeRedraw = false;
}
}
}
If you must add controls at run time, based on some dynamic or changing value, you might want to consider creating an image on the fly, and capturing mouse click events on its picturebox. This would be much quicker and only have one control to draw rather than hundreds. You would lose some button functionality such as the click animation and other automatic properties and events; but you could recreate most of those in the generation of the image.
This is a technique I use to offer users the ability to turn on and off individual devices among a pool of thousands, when the location in a 2-dimensional space matters. If the arrangement of the buttons is unimportant, you might be better offering a list of items in a listview or combobox, or as other answers suggest, a datagridview with button columns.
EDIT:
An example showing how to add a graphic with virtual buttons. Very basic implementation, but hopefully you will get the idea:
First, some initial variables as preferences:
int GraphicWidth = 300;
int GraphicHeight = 100;
int ButtonWidth = 60;
int ButtonHeight = 20;
Font ButtonFont = new Font("Arial", 10F);
Pen ButtonBorderColor = new Pen(Color.Black);
Brush ButtonTextColor = new SolidBrush(Color.Black);
Generating the image:
Bitmap ControlImage = new Bitmap(GraphicWidth, GraphicHeight);
using (Graphics g = Graphics.FromImage(ControlImage))
{
g.Clear(Color.White);
for (int x = 0; x < GraphicWidth; x += ButtonWidth)
for (int y = 0; y < GraphicHeight; y += ButtonHeight)
{
g.DrawRectangle(ButtonBorderColor, x, y, ButtonWidth, ButtonHeight);
string ButtonLabel = ((GraphicWidth / ButtonWidth) * (y / ButtonHeight) + x / ButtonWidth).ToString();
SizeF ButtonLabelSize = g.MeasureString(ButtonLabel, ButtonFont);
g.DrawString(ButtonLabel, ButtonFont, ButtonTextColor, x + (ButtonWidth/2) - (ButtonLabelSize.Width / 2), y + (ButtonHeight/2)-(ButtonLabelSize.Height / 2));
}
}
pictureBox1.Image = ControlImage;
And responding to the Click event of the pictureBox:
// Determine which "button" was clicked
MouseEventArgs em = (MouseEventArgs)e;
Point ClickLocation = new Point(em.X, em.Y);
int ButtonNumber = (GraphicWidth / ButtonWidth) * (ClickLocation.Y / ButtonHeight) + (ClickLocation.X / ButtonWidth);
MessageBox.Show(ButtonNumber.ToString());
I think you won't be able to prevent flickering having 625 buttons (btw, windows) on the panel. If such layout is mandatory for you, try to use the DataGridView, bind it to a fake data source containing the required number of columns and rows and create DataGridViewButtonColumns in it. This should work much more better then your current result ...

Categories

Resources