I want to allow the user to tell me how many rows and columns of buttons to draw. Then draw the buttons. When a button is clicked, it displays the row # and column # of that button in a msgbox.

I would create a button using the following code:
Button myText = new Button();
myText.Location = new Point(25,35);
myText.Text = "First";
this.Controls.Add (myText);

First of all, how would I create a click event if the button name isn't available until runtime? If a button is created in the design view, to get the click event one would double-click on the button.

Would I need a 2d array to store the column/row numbers to retrieve. But how would I know which button is clicked to retrieve the info?

Thanks.

Dani AI

Generated

A concise, practical summary that complements the existing replies (, , , , ).

For WinForms the cleanest pattern is to keep layout and data separate: use a layout container (TableLayoutPanel or FlowLayoutPanel) to place buttons and store the button coordinates in the control itself (use Tag) instead of parsing the Name. When wiring the Click handler at runtime prefer a small lambda or a single handler that reads Tag. To avoid the common loop-closure pitfall, copy loop indices to locals before attaching a lambda.

for (int r = 0; r < rows; r++) {
  for (int c = 0; c < cols; c++) {
    int rr = r, cc = c;
    var b = new Button();
    b.Tag = new Point(cc, rr);           // store coords
    tableLayoutPanel.Controls.Add(b, cc, rr);
    b.Click += (s,e) => {
      var p = (Point)((Button)s).Tag;
      MessageBox.Show(string.Format("Row {0}, Col {1}", p.Y+1, p.X+1));
    };
  }
}

For ASP.NET WebForms, dynamic controls must be recreated on every postback (rebuild them in Page_Init) and give each button a stable ID. Put coordinates into CommandArgument (or DataItem) and parse them in the server Click handler so viewstate/events work reliably.

Troubleshooting tips: give runtime buttons unique IDs, call SuspendLayout/ResumeLayout when adding many WinForms controls to reduce flicker, remove or Dispose old dynamic controls before regenerating, and prefer TableLayoutPanel (WinForms) or a server Grid/Repeater (ASP.NET) for maintainable code. demonstrated a working index approach; correctly noted runtime event wiring — using Tag/CommandArgument avoids fragile string parsing and keeps the logic straightforward.

Recommended Answers

All 4 Replies

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Text.RegularExpressions;

namespace WindowsApplication1
{
	/// <summary>
	/// Summary description for Form1.
	/// </summary>
	public class Form1 : System.Windows.Forms.Form
	{
		private System.Windows.Forms.TextBox textBox1;
		private System.Windows.Forms.TextBox textBox2;
		private System.Windows.Forms.Button button1;
		/// <summary>
		/// Required designer variable.
		/// </summary>
		private System.ComponentModel.Container components = null;

		public Form1()
		{
			//
			// Required for Windows Form Designer support
			//
			InitializeComponent();

			//
			// TODO: Add any constructor code after InitializeComponent call
			//
		}

		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if (components != null) 
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}

		#region Windows Form Designer generated code
		/// <summary>
		/// Required method for Designer support - do not modify
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			this.textBox1 = new System.Windows.Forms.TextBox();
			this.textBox2 = new System.Windows.Forms.TextBox();
			this.button1 = new System.Windows.Forms.Button();
			this.SuspendLayout();
			// 
			// textBox1
			// 
			this.textBox1.Location = new System.Drawing.Point(16, 16);
			this.textBox1.Name = "textBox1";
			this.textBox1.Size = new System.Drawing.Size(48, 20);
			this.textBox1.TabIndex = 0;
			this.textBox1.Text = "textBox1";
			// 
			// textBox2
			// 
			this.textBox2.Location = new System.Drawing.Point(72, 16);
			this.textBox2.Name = "textBox2";
			this.textBox2.Size = new System.Drawing.Size(56, 20);
			this.textBox2.TabIndex = 1;
			this.textBox2.Text = "textBox2";
			// 
			// button1
			// 
			this.button1.Location = new System.Drawing.Point(152, 16);
			this.button1.Name = "button1";
			this.button1.TabIndex = 2;
			this.button1.Text = "button1";
			this.button1.Click += new System.EventHandler(this.button1_Click);
			// 
			// Form1
			// 
			this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
			this.ClientSize = new System.Drawing.Size(240, 46);
			this.Controls.Add(this.button1);
			this.Controls.Add(this.textBox2);
			this.Controls.Add(this.textBox1);
			this.Name = "Form1";
			this.Text = "Form1";
			this.ResumeLayout(false);

		}
		#endregion

		/// <summary>
		/// The main entry point for the application.
		/// </summary>
		[STAThread]
		static void Main() 
		{
			Application.Run(new Form1());
		}

		private void button1_Click(object sender, System.EventArgs e)
		{
			// This is the generate button routine
			int count = 0;
			System.Collections.ArrayList al = new ArrayList();
			foreach (Control c in this.Controls)
			{
				if (c.GetType() == typeof(Button) && c.Name != "button1")
				{
					al.Add(c);
				}
			}

			for (int i = 0; i < al.Count; i++)
				this.Controls.Remove((Control)al[i]);

			// Resize to start size
			this.Size = new Size(248, 80);

			// First one is columns
			int columns = Convert.ToInt32(textBox1.Text);
			int rows = Convert.ToInt32(textBox2.Text);
			int locX = 16, locY = 56;
			// We assume space between is 20
			for (int i = 0; i < columns * rows; i++)
			{
				Button b = new Button();
				b.Name = "b" + i.ToString();
				b.Size = new Size(10, 10);
				b.Location = new Point(locX, locY);
				if (((i + 1) % columns) == 0)
				{
					locX = 16;
					locY += b.Size.Height + 20;
				}
				else
				{
					locX += b.Size.Width + 20;
				}

				this.Controls.Add(b);
				b.Click +=new EventHandler(b_Click);

				if ((b.Location.X + b.Width) > this.ClientSize.Width)
				{
					this.Width += ((b.Location.X + b.Width) - this.ClientSize.Width) + 10;
				}
				if ((b.Location.Y + b.Height) > this.ClientSize.Height)
				{
					this.Height += ((b.Location.Y + b.Height) - this.ClientSize.Height) + 10;
				}
			}
		}

		private void b_Click(object sender, EventArgs e)
		{
			string s =  ((Control)sender).Name;
			s = new Regex("\\d+").Match(s).Value;
			int num = Convert.ToInt32(s);
			int columns = Convert.ToInt32(textBox1.Text);
			int rows = Convert.ToInt32(textBox2.Text);
			s = string.Format("Column: {0}, Row: {1}", (num % columns) + 1, (num / columns) + 1);
			MessageBox.Show(this, s);
		}
	}
}

this is just a quick hack, but it might be what you're searching for...
to compile you can put that code in a file, call it
then compile it on commandline via: csc /target:winexe

You can assign events at runtime easily, by

button1.Click+=new EventHandler(myClickFunction);

in fact you can find this by typing like button1.click = new and it prompts you :)

This is something similar to what you are doing, working with .

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.