Dynamic Button Creation & Placing Them in a Predefined Order Using C#

Dynamic button creation & placing them in a predefined order using c#

You can use a TableLayoutPanel and create your buttons dynamically and add them to the panel.

For example:

private void Form1_Load(object sender, EventArgs e)
{
var rowCount = 3;
var columnCount = 4;

this.tableLayoutPanel1.ColumnCount = columnCount;
this.tableLayoutPanel1.RowCount = rowCount;

this.tableLayoutPanel1.ColumnStyles.Clear();
this.tableLayoutPanel1.RowStyles.Clear();

for (int i = 0; i < columnCount; i++)
{
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100 / columnCount));
}
for (int i = 0; i < rowCount; i++)
{
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100 / rowCount));
}

for (int i = 0; i < rowCount* columnCount; i++)
{
var b = new Button();
b.Text = (i+1).ToString();
b.Name = string.Format("b_{0}", i + 1);
b.Click += b_Click;
b.Dock = DockStyle.Fill;
this.tableLayoutPanel1.Controls.Add(b);
}
}

void b_Click(object sender, EventArgs e)
{
var b = sender as Button;
if (b != null)
MessageBox.Show(string.Format("{0} Clicked", b.Text));
}

Sample Image

Note:

  • Using TableLayoutPanel.Controls.Add(control) we can add controls sequentially to the panel.
  • Using TableLayoutPanel.Controls.Add(control, columnIndex, rowIndex) we can add controls at specific cells.

C# winforms dynamically created button controls

Put a class variant to store Button - TextBox pair, and delete respectively. Quick tested code below - not fine tuned for memory leak / logic etc, just a sample code that fulfills your requirement.

Dictionary<Button, TextBox> pair = new Dictionary<Button, TextBox>(); // A dictionary to store the Button - TextBox pair

private void button1_Click(object sender, EventArgs e) {
int n = 3; // Added for testing
int space = 0; // Added for testing

int UsernameX, UsernameY, RemoveX, RemoveY;

UsernameX = 100; // Modified for testing
UsernameY = 45;
RemoveX = 400; // Modified for testing
RemoveY = 45;

for (int i = 0; i < n; i++) {
var Username = new TextBox();
Username.Size = new Size(233, 26);
Username.Location = new Point(UsernameX, UsernameY + space);
Username.Font = new Font("Arial", 10);
Username.Name = "Username";

var Remove = new Button();
Remove.Location = new Point(RemoveX, RemoveY + space);
Remove.Text = "Remove";
Remove.Font = new Font("Arial", 10);
Remove.Size = new Size(95, 23);
Remove.UseVisualStyleBackColor = true;
Remove.Click += new EventHandler(Remove_Click);
Remove.Name = "Remove";

Controls.Add(Username);
Controls.Add(Remove);

pair.Add(Remove, Username);

space += 35;
}
}
private void Remove_Click(object sender, EventArgs e) {
Controls.Remove((Button)sender); // Removes the delete button
Controls.Remove(pair[(Button)sender]); // Removes the textbox
pair.Remove((Button)sender); // Removes the entry in dictionary
}

Update: Another version for better memory and logic matters. Also shifts up the rest of controls up if it's the OP's desire.

int n = 3; // Added for testing, probably you already have it somewhere
int space = 0; // Added for testing, probably you already have it somewhere

// Moved for logic
// Value modified for testing
int UsernameX = 100;
int UsernameY = 45;
int RemoveX = 400;
int RemoveY = 45;

int SpaceDelta = 35; // Added for logic

List<Button> RemoveButtons = new List<Button>();
List<TextBox> UsernameTextBoxes = new List<TextBox>();

private void button1_Click(object sender, EventArgs e) {

Random rnd = new Random((int)DateTime.Now.Ticks); // Added for testing

for (int i = 0; i < n; i++) {
var Username = new TextBox();
Username.Size = new Size(233, 26);
Username.Location = new Point(UsernameX, UsernameY + space);
Username.Font = new Font("Arial", 10);
Username.Name = "Username";
Username.Text = $"{(int)(rnd.NextDouble() * 100000)}"; // Added for testing

var Remove = new Button();
Remove.Location = new Point(RemoveX, RemoveY + space);
Remove.Text = $"{(int)(rnd.NextDouble() * 100000)}"; // Modified for testing
Remove.Font = new Font("Arial", 10);
Remove.Size = new Size(95, 23);
Remove.UseVisualStyleBackColor = true;
Remove.Click += new EventHandler(Remove_Click);
Remove.Name = "Remove";

Controls.Add(Username);
Controls.Add(Remove);

RemoveButtons.Add(Remove);
UsernameTextBoxes.Add(Username);

space += SpaceDelta;
}
}
private void Remove_Click(object sender, EventArgs e) {
int idx = RemoveButtons.IndexOf((Button)sender);

// Remove button
RemoveButtons[idx].Dispose();
RemoveButtons.RemoveAt(idx);

// Remove textbox
UsernameTextBoxes[idx].Dispose();
UsernameTextBoxes.RemoveAt(idx);

// Shift controls up
for (int i = idx; i < RemoveButtons.Count; i ++) {
RemoveButtons[i].Top -= SpaceDelta;
UsernameTextBoxes[i].Top -= SpaceDelta;
}

space -= SpaceDelta;
}

How to use buttons and textbox created dynamically

Give this a go:

public partial class UserControl2 : UserControl
{
public UserControl2()
{
InitializeComponent();
}

private void btnAdd_Click(object sender, EventArgs e)
{

var color = ColorTranslator.FromHtml("#f0f0f0");
var color2 = ColorTranslator.FromHtml("#4285f4");
var r = new System.Random();
var p = new Panel();
var lbl = new Label();
var pnlTxtBox = new TextBox();
var pnlBtnStart = new Button();
var pnlBtnStop = new Button();
var pnlBtnClose = new Button();

p.Name = "panel" + (panel0.Controls.Count + 1);
p.BackColor = color;
p.Size = new Size(panel0.ClientSize.Width, 40);
p.BorderStyle = BorderStyle.Fixed3D;
p.Dock = DockStyle.Top;

lbl.Text = "Name";
lbl.ForeColor = color2;
lbl.Font = new Font("Verdana", lbl.Font.Size);
lbl.Size = new Size(75, 20);
lbl.Location = new Point(20, 13);
p.Controls.Add(lbl);

pnlTxtBox.Name = "txtBox";
pnlTxtBox.Size = new Size(120, 20);
pnlTxtBox.Location = new Point(100, 10);
p.Controls.Add(pnlTxtBox);

pnlBtnStart.Name = "btnStart";
pnlBtnStart.Text = "Start";
pnlBtnStart.Font = new Font("Verdana", pnlBtnStart.Font.Size);
pnlBtnStart.ForeColor = color2;
pnlBtnStart.Size = new Size(75, 20);
pnlBtnStart.Location = new Point(300, 10);
pnlBtnStart.Click += (s,e) => StartWasClicked(pnlTxtBox);
p.Controls.Add(pnlBtnStart);

pnlBtnStop.Name = "btnStop";
pnlBtnStop.Text = "Stop";
pnlBtnStop.Font = new Font("Verdana", pnlBtnStop.Font.Size);
pnlBtnStop.ForeColor = color2;
pnlBtnStop.Size = new Size(75, 20);
pnlBtnStop.Location = new Point(380, 10);
p.Controls.Add(pnlBtnStop);

pnlBtnClose.Name = "btnClose";
pnlBtnClose.Text = "Close";
pnlBtnClose.Font = new Font("Verdana", pnlBtnClose.Font.Size);
pnlBtnClose.ForeColor = color2;
pnlBtnClose.Size = new Size(75, 20);
pnlBtnClose.Location = new Point(500, 10);
pnlBtnClose.Click += (s,e) => CloseWasClicked(p);
p.Controls.Add(pnlBtnClose);

panel0.Controls.Add(p);
panel0.AutoScroll = false;
panel0.HorizontalScroll.Enabled = false;
panel0.HorizontalScroll.Visible = false;
panel0.HorizontalScroll.Maximum = 0;
panel0.AutoScroll = true;


}

void StartWasClicked(TextBox tb){
MessageBox.Show(tb.Text);
}

void CloseWasClicked(Panel p){
p.Visible = false;
}
}

How to refer to each button when u dynamically create an uknown amount of them?

this.Controls.Add(b);

If you are adding the buttons to this.Controls, you should be able to access all of them using LINQ. To add its functionality, use:

using System.Linq;

Then, you can get all the buttons in the control:

var buttons = this.Controls.OfType<Button>();

Or all the buttons which names start with the letter 'b':

var buttons = this.Controls.OfType<Button>().Where(x => x.Name.StartsWith("b"));

After that, you are able to search in the list of buttons using LINQ:

var b0 = buttons.Where(x => x.Name == "b0").SingleOrDefault();

Or search the entire ControlCollection in this without creating the 'buttons' variable. Use the SingleOrDefault() method to return one button with a specific name:

var b0 = this.Controls.OfType<Button>().Where(x => x.Name == "b0").SingleOrDefault();

When you then would want to change the button's Text or any other property, simply use:

b0.Text = "New Text";

To learn more about LINQ visit this page. All the LINQ methods are available here.

If you wouldn't want to use LINQ for retrieving the buttons, you can simply select all the buttons using a foreach loop and the is keyword for checking the type:

var buttons = new List<Button>();

foreach (var control in this.Controls)
{
if (control is Button button)
{
buttons.Add(button);
}
}

Or select all the buttons which names start with the letter 'b':

var buttons = new List<Button>();

foreach (var control in this.Controls)
{
if (control is Button button && button.Name.StartsWith("b"))
{
buttons.Add(button);
}
}

You can also select a button with a specific name this way:

Button b0 = null;

foreach (var control in this.Controls)
{
if (control is Button button && button.Name == "b0")
{
b0 = button;
break; // break keyword to stop the loop if the button is found
}
}

Then, you are able to edit the properties of the buttons 'b0', although you have to make sure the button is not null:

if (b0 is not null)  // checks if button exists
{
b0.Text = "New Text";
}

How can I load and sort button controls dynamically, based on changing data?

Have you tried using flowLayoutPanel?
it is a control panel that will align and position all controls inside it automatically for yourself it has 4 direction in the property FlowDirection, try using that and I think you will just have to use this configurations

flowLayoutPanel.FlowDirection = FlowDirection.LeftToRight;
flowLayoutPanel.WrapContents = true;

you will just have to calculate how many width 5 buttons have, plus its magin then set it to be the width of your flowLayoutPanel

Defining an event handler where the sender was dynamically added

You can add a handler to Click event of your buttons and use sender parameter.

Also probably the index of button in the array is important for you, so you can store array index of the button in Tag property and use it later.

In the for loop:

var button = PlayButtonArray[ButtonRowindex, ButtonColindex];
button.Tag= new Point(ButtonRowindex, ButtonColindex);
button.Click += Button_Click;

Code for Button_Click:

private void Button_Click(object sender, EventArgs e)
{
var button = sender as Button;
//You can manipulate button here

//Also to extract the button index in array:
var indexes = (Point)button.Tag;

MessageBox.Show(string.Format("This is the button at {0}, {1}", indexes.X, indexes.Y));
}

C# Repeater dynamic Button

It works for me now, I used the LinkButton instead of Button.
Also added OnItemCommand="ItemCommand" in repeater.
And finally it will goto ItemCommand event when LinkButton clicked.
It gets the value from CommandArgument which set in dynamic LinkButton

<asp:Repeater ID="rpt" runat="server" OnItemCommand="ItemCommand">
<ItemTemplate>
<asp:LinkButton runat="server" CommandArgument='<%# Eval("lbtnUploadCommandArgument")%>' CommandName="ButtonEvent">Upload Files</asp:LinkButton>
</ItemTemplate>
</asp:Repeater>

protected void ItemCommand(Object Sender, RepeaterCommandEventArgs e)
{
string[] arr_Para = ((LinkButton)e.CommandSource).CommandArgument.Split(';');
}


Related Topics



Leave a reply



Submit