Creating a collection of Controls with a loop - c#

So i'm basically attempting to create a grid out of pictureboxes through a for loop using the following code:
private void Form1_Load(object sender, EventArgs e)
{
for (int i = 0; i <= 5; i++)
{
for (int j = 0; j <= 5; j++)
{
PictureBox tile = new PictureBox() ;
tile.Size = new Size(49, 49);
tile.BackColor = Color.Firebrick;
tile.Location = new Point((100 + (50 * i)), (100 + (50 * j)));
Debug.WriteLine("PB created with index ["+i+","+j+"]");
}
}
}
But during runtime, the form appears blank, with no pictureboxes generated at all.
What did I do wrong?

You're not adding the control you just created to the form's Controls collection.
private void Form1_Load(object sender, EventArgs e)
{
for (int i = 0; i <= 5; i++)
{
for (int j = 0; j <= 5; j++)
{
PictureBox tile = new PictureBox() ;
tile.Size = new Size(49, 49);
tile.BackColor = Color.Firebrick;
tile.Location = new Point((100 + (50 * i)), (100 + (50 * j)));
Debug.WriteLine("PB created with index ["+i+","+j+"]");
// The control needs to be added to the form's Controls collection
Controls.Add(tile);
}
}
}

Related

which button is pressed in dynamically created button 2d array

I have a 2d array of LEDButton : Button.
I want to find out the index [x,y] of each buttons the user clicks.
I am new to Windows Forms and not used to working outside of a console so these GUI objects are very unfamiliar to me.
private void Form1_Load(object sender, EventArgs e)
{
LEDButton[,] leds = new LEDButton[11, 11];
for (int x = 0; x < leds.GetUpperBound(0); x++)
{
listBox1.Items.Add("x = " + x);
for (int y = 0; y < leds.GetUpperBound(1); y++)
{
leds[x, y] = new LEDButton()
{
Name = String.Format("Button{0}{1}", x, y),
TabIndex = 40 * x + y,
Location = new Point(40 * y + 50, 40 * x + 50)
};
leds[x, y].pointx = x;
leds[x, y].pointy = y;
}
}
// add buttons to controls
for (int x = 0; x < leds.GetUpperBound(0); x++)
{
for (int y = 0; y < leds.GetUpperBound(1); y++)
{
Controls.Add(leds[x, y]);
leds[x, y].Click += Form1_Click;
}
}
public class LEDButton : Button
{
public const int LEDWidth = 20;
public const int LEDHeight = 20;
public int pointx = 0;
public int pointy = 0;
public LEDButton()
{
BackColor = Color.FromArgb(0, 64, 0);
ForeColor = Color.Black;
FlatStyle = FlatStyle.Flat;
Size = new Size(LEDWidth, LEDHeight);
UseVisualStyleBackColor = false;
this.Click += LEDButton_Click; //throws error
}
}
I think I found my answer with the help of Lars.
Code should be
private void Form1_Click(object? sender, EventArgs e)
{
LEDButton btn = sender as LEDButton;
listBox2.Items.Add(btn.Name);
}

Memory leaks in InkEdit control

I can't destroy InkEdit without memory leaks. The Dispose method helps a little, but still, a lot of unmanaged resources are not freed.
// Create 16 InkEdit components
for (int y = 0; y < 4; y++)
{
for (int x = 0; x < 4; x++)
{
var ie = new InkEdit { Left = x * 50, Top = y * 50, Width = 40, Height = 40 };
panel1.Controls.Add(ie);
}
}
The example was modified to dispose all controls correctly.
// 46 MiB of leaked RAM after every Create/Dispose iteration
while (panel1.Controls.Count > 0)
{
var lastIndex = panel1.Controls.Count - 1;
var control = panel1.Controls[lastIndex];
panel1.Controls.RemoveAt(lastIndex);
control.Dispose();
}
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
With GC.Collect:
Without GC.Collect:
Full sample:
https://github.com/dermeister0/InkEditTest
I used SimpleHelpers.ObjectPool package to reuse the controls.
SimpleHelpers.ObjectPool<InkEdit>.MaxCapacity = 16;
protected override void Dispose(bool disposing)
{
if (disposing)
{
SimpleHelpers.ObjectPool<InkEdit>.Clear();
components?.Dispose();
}
base.Dispose(disposing);
}
private void btnCreate_Click(object sender, EventArgs e)
{
for (int y = 0; y < 4; y++)
{
for (int x = 0; x < 4; x++)
{
InkEdit ie = SimpleHelpers.ObjectPool<InkEdit>.Get(() => new InkEdit());
ie.Left = x * 50;
ie.Top = y * 50;
ie.Width = 40;
ie.Height = 40;
panel1.Controls.Add(ie);
}
}
}
private void btnDisposeAndClear_Click(object sender, EventArgs e)
{
for (int i = panel1.Controls.Count - 1; i >= 0; i--)
{
var control = panel1.Controls[i] as InkEdit;
SimpleHelpers.ObjectPool<InkEdit>.Put(control);
}
}

Dynamicly created panels location distance

I'm beginner in programming and I want to create a little "game" with Panels.
(Later maybe I'll change for PictureBox, but for now it's OK)
Code:
private void Form1_Load(object sender, EventArgs e)
{
int size = 20;
int quantity = 10;
Random rnd = new Random();
for (int y = 0; y < quantity; y++)
{
for (int x = 0; x < quantity; x++)
{
Color randomColor = Color.FromArgb(
rnd.Next(256), rnd.Next(256), rnd.Next(256)
);
Panel panel = new Panel
{
Size = new Size(size, size),
Location = new Point(x * size, y * size),
BorderStyle = BorderStyle.FixedSingle,
BackColor = randomColor
};
Controls.Add(panel);
//panel.Click += Panel_Click;
}
}
}
I have two questions:
How can I set 5 pixel distance to each Panel from each other?
Should I place these panel creation in the constructor? I see people prefer that.
You have to include additional 5 pixels padding into Location:
...
int padding = 5;
...
Location = new Point(x * (size + padding), y * (size + padding))
...
Let's extract a method:
private void CreateGameField() {
int size = 20;
int padding = 5;
int quantity = 10;
Random rnd = new Random();
for (int y = 0; y < quantity; ++y)
for (int x = 0; x < quantity; ++x)
new Panel() {
Size = new Size(size, size),
Location = new Point(x * (size + padding), y * (size + padding)),
BorderStyle = BorderStyle.FixedSingle,
BackColor = Color.FromArgb(rnd.Next(256), rnd.Next(256), rnd.Next(256)),
Parent = this, // instead of Controls.Add(panel);
};
}
Then
private void Form1_Load(object sender, EventArgs e) {
CreateGameField();
}
FromLoad event handler is a good place to create the game field; if you want you can place CreateGameField() in the constructor, but put it after InitializeComponent():
public Form1() {
InitializeComponent();
CreateGameField();
}

How can I make n*n buttons using an array at runtime in C#?

It takes a number n from the user and makes n*n buttons for it on the form.
When I run the code, only the first row appears on form, instead of all n rows.
private void button1_Click(object sender, EventArgs e)
{
int n = Convert.ToInt32(textBox1.Text.ToString());
Button[,] v = new Button[n, n];
for (int i = 0; i < n; i++)
{
int top = 60;
int left = 160;
int width = 25;
for (int j = 0; j < n; j++)
{
Button c= new Button();
c.Top =top ;
c.Left= left;
c.Width = width;
v[i, j] = c;
left += c.Width + 2;
}
top += 2;
left = 160;
}
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
this.Controls.Add(v[i, j]);
}
}
}
You are resetting the top variable at each loop, your buttons are all in the same top position. Move the initialization of the top variable outside the first loop
private void button1_Click(object sender, EventArgs e)
{
int top = 60;
int n = Convert.ToInt32(textBox1.Text.ToString());
Button[,] v = new Button[n, n];
for (int i = 0; i < n; i++)
{
// int top = 60;
......
However, you are incrementing the top only by 2 pixel. This is not enough to avoid covering one row of button with the next row. You need to increment top by at least 25 pixels at the end of the inner loop.
....
top += 25;
left = 160;
}
You can also use your for loops to set the top and left property values (more than one variable can be defined and incremented in a for loop).
For each row, we increase the top value by the height + padding (padding is the gap between buttons), and for each col, we increase the left by width + padding.
We can also add each button to the controls collection as we create it rather than adding it to an array and then iterating again over that array, and we can add some input validation to the textbox.Text that we're assuming is a positive number, presenting the user with a message box if they entered something invalid.
Given this, we can simplify the code to something like:
private void button1_Click(object sender, EventArgs e)
{
int count;
// Validate input
if (!int.TryParse(textBox1.Text, out count) || count < 0)
{
MessageBox.Show("Please enter a positive whole number");
return;
}
// remove old buttons (except this one)
var otherButtons = Controls.OfType<Button>().Where(b => b != button1).ToList();
foreach (Button b in otherButtons)
{
Controls.Remove(b);
}
// Add button controls to form with these size and spacing values (modify as desired)
var width = 25;
var height = 25;
var padding = 2;
// For each row, increment the top by the height plus padding
for (int row = 0, top = padding; row < count; row++, top += height + padding)
{
// For each column, increment the left by the width plus padding
for (int col = 0, left = padding; col < count; col++, left += width + padding)
{
// Add our new button
Controls.Add(new Button
{
Top = top,
Left = left,
Width = width,
Height = height,
});
}
}
}

Can't take an instance using Controls.Add

I'm triyng to create buttons programmatically. I don't want to use Button btn = new Button();.. btn.height=.... control.Add(btn);. The code I'm using can add the buttons to the form but I can't take an instance so I can't create button.click event. Can anybody help me to solve it. The code I'm using below thanks.
int k = 0;
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 5; j++)
{
Controls.Add(
new Button()
{
Top = 50 + (50 * i),
Left = 50 + (50 * j),
Width = 50, Height = 50,
Text = (++k).ToString()
});
}
}
If you don't want to create a button, and subscribe directly to the event (i don't know why is this important), you can do this:
private void Method()
{
int k = 0;
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 5; j++)
{
Controls.Add(new Button() { Top = 50 + (50 * i), Left = 50 + (50 * j), Width = 50, Height = 50, Text = (++k).ToString() });
}
}
Controls.OfType<Button>().ToList().ForEach(x => x.Click += Button_Click);
}
private void Button_Click(object sender, System.EventArgs e)
{
MessageBox.Show((sender as Button).Text);
}

Categories

Resources