Problems with creating controls dynamically - c#

I'm having some issues with creating labels dynamically. I'm trying to create a 15x15 grid with an "X" label per cell, and the code is sort of working, it creates the first label but not the others, I've tried debugging the code with breakpoints and it's calling the Controls.Add the right amount of times but it's only creating one label. Here's the code:
class PlayingGrid
{
const int MAX_CELLS = 15;
Cell[,] grids = new Cell[MAX_CELLS, MAX_CELLS];
public PlayingGrid()
{
for (int y = 1; y < MAX_CELLS; y++)
{
int yPoint = y * 12;
for (int x = 1; x < MAX_CELLS; x++)
{
int xPoint = x * 12;
grids[y, x] = new Cell(new Point(xPoint, yPoint));
}
}
}
}
class Cell
{
#region Fields & Properties
public Letter Letter;
public Point Point;
public static GameForm refForm;
#endregion
//Default constructor will create an empty cell
public Cell(Point point)
{
Letter = new Letter(LatinAlphabet.Empty);
this.Point = point;
refForm.Invoke(new Action(()=> refForm.Controls.Add(new Label() { Text = "X", Location = point })));
}
}

Set label AutoSize property to true:
new Label() {AutoSize =true, Text = "X", Location = point }
Because with default size all label hided in below of last label.

Related

Why dont buttons show up in the grid when I create them using a for loop in C# WPF?

Trying to make a board for a Connect 4 game using buttons however after creating a method to create the actual board itself by creating buttons for each individual cell in the grid,nothing shows up.
public partial class MainWindow : Window
{
private readonly int[,] _board;
private readonly int _height;
private readonly bool _isComp;
private readonly Brush _p1Color;
private readonly string _p1Name;
private readonly Ellipse _p1Sym;
private readonly int _width;
private readonly int _win;
private Ellipse mycircle;
private bool _isFalling;
private bool turnPlayer1;
private int _turns;
private string _winner;
public string Mode;
public int[,] BoardArray = new int[5, 6];
public MainWindow()
{
}
public MainWindow (int row, int column , int win, Ellipse newP1, Ellipse newP2, Brush p1Color, Brush p2Color,
string p1Name, string p2Name, bool isComp, int time, string mode, Brush back, Uri music)
{
InitializeComponent();
turnPlayer1 = true;
for (int loopColumn = 0; loopColumn <= 6; loopColumn++)
{
for (int loopRow = 0; loopRow <= 5; loopColumn++)
{
BoardArray[loopRow, loopColumn] = 0;
}
}
row = 6;
column = 7;
CreateBoard(column, row);
mycircle = newP1;
_p1Color = p1Color;
_p1Name = p1Name;
_win = win;
_winner = "";
_isComp = isComp;
Mode = mode;
}
private void buttonClicked(Button btn)
{
var rowDef = Grid.GetRow(btn);
var columnDef = Grid.GetColumn(btn);
var col = columnDef;
DropCounter(col);
}
private int EmptyRow(int column)
{
for(int row = 6-1;row >=0;row-- )
{
var arrayRow = row; ;
var arrayCol = column;
if(BoardArray[row,column]== 0)
{
return row;
}
}
return -1;
}
private void DropCounter(int column)
{
var freeRow = EmptyRow(column);
if (freeRow == -1) return;
mycircle = new Ellipse();
mycircle.Stroke = System.Windows.Media.Brushes.Black;
mycircle.Fill = System.Windows.Media.Brushes.DarkBlue;
mycircle.Height = 100;
mycircle.Width = 100;
Grid.SetColumn(mycircle, 1);
Grid.SetRow(mycircle, 1);
myGrid.Children.Insert(0,mycircle);
}
private void Btn_Click(object sender, RoutedEventArgs e)
{
if (sender is Button btn && EmptyRow(Grid.GetColumn(btn)) != -1) buttonClicked(btn);
}
private void CreateBoard(int column,int rows)
{
for (var row = 0; row < rows; row++)
{
for (var col = 0; col < column; column++)
{
Button button = new Button();
{
var brush = new ImageBrush();
brush.ImageSource = new BitmapImage(new Uri("gridsingle.png.bmp", UriKind.Relative));
button.Background = brush;
Name = "btn_" + row + "_" + column;
};
button.Click += Btn_Click;
Grid.SetRow(button, row);
Grid.SetColumn(button, column);
myGrid.Children.Add(button);
}
}
}
}
}
Apologies for the messy code,this is a HW assignment for highschool and programming isnt my favourite part of the course
Not really sure whats the main cause as I am implementing the same code as my friend yet his buttons all load on his window.
I'll be honest, there are actually a whole lot of things wrong with this. Normally I'd recommend throwing it out and starting again, but I'm guessing time is a factor, so I'll do my best to help.
First of all, this doesn't look right:
public MainWindow()
{
}
public MainWindow(int row, int column, int win, Ellipse newP1, Ellipse newP2, Brush p1Color, Brush p2Color, string p1Name, string p2Name, bool isComp, int time, string mode, Brush back, Uri music)
{
Ordinarily, the first constructor is the one that gets called, and since your implementation is empty, it explains why you aren't seeing anything. Looking at your code, the parameters being passed into your second constructor aren't actually being used, so get rid of both constructors and just keep the logic needed to initialize your board:
public MainWindow()
{
InitializeComponent();
turnPlayer1 = true;
for (int loopColumn = 0; loopColumn <= 6; loopColumn++)
{
for (int loopRow = 0; loopRow <= 5; loopRow++)
{
BoardArray[loopRow, loopColumn] = 0;
}
}
var row = 6;
var column = 7;
CreateBoard(column, row);
_winner = "";
}
You'll notice there was a bug in your original code which I've fixed:
for (int loopColumn = 0; loopColumn <= 6; loopColumn++)
{
for (int loopRow = 0; loopRow <= 5; loopColumn++)
That second loop should be incrementing loopRow, not loopColumn. (As a side note, you've hard-coded these loops to 6 columns, and 5 rows, they should instead be using the values in the column and row variables).
Next, there's a bug in your CreateBoard function:
private void CreateBoard(int column, int rows)
{
for (var row = 0; row < rows; row++)
{
for (var col = 0; col < column; column++)
The column variable is storing the total number of columns, so you should be doing col++, not column++. Similarly, when you set the Grid column for the button, you should be setting it to col, not column:
button.Click += Btn_Click;
Grid.SetRow(button, row);
Grid.SetColumn(button, column); // <---- this is wrong
myGrid.Children.Add(button);
The code in your DropCounter function also has problems:
Grid.SetColumn(mycircle, 1);
Grid.SetRow(mycircle, 1);
myGrid.Children.Insert(0, mycircle);
Notice you're always setting the column and row to 1, you should be setting them to column and freeRow.
Last of all, you're never updating BoardArray to make note of the fact that tokens are being dropped, so you'll just keep dropping them on the bottom row. You need to update it at the end of your DropCounter function so that your EmptyRow function works correctly:
BoardArray[freeRow, column] = 1;

Swapping control position in TableLayoutPanel

EDIT:
Okay so for clarity I will improve my information a bit more:
I have a TableLayoutView (I will call this tlv) that has 5 fixed columns, and x + 2 rows. The first row of tlv contains labels in each cell for header purposes. I dynamically add more rows onto tlv and so that it why it has a variable amount of rows (plus the initial header row which is never removed).
As another tiny small complication, I also keep an empty row entry at the bottom of tlv which I must keep because I use it for other functionality.
To visualise what I have just said, this is an example of tlv consisting with 4 entries (numbered), the header row (H's) and the placeholder row (P's).
HHHHH
11111
22222
33333
44444
PPPPP
I want to go from that, to say, if I wanted to swap entry 2 and 3 the output would be:
HHHHH
11111
33333
22222
44444
PPPPP
The code I have so far is as follows:
for (int j = 0; j < 5; j++)
{
TableLayoutPanelCellPosition tablePosition1 = new
TableLayoutPanelCellPosition(j, rowIndex + 1);
Control moveControl1 = queuedFiles.GetControlFromPosition(j, rowIndex);
queuedFiles.SetCellPosition(moveControl1, tablePosition1);
TableLayoutPanelCellPosition tablePosition2 = new
TableLayoutPanelCellPosition(j, rowIndex);
Control moveControl2 = queuedFiles.GetControlFromPosition(j, rowIndex + 1);
queuedFiles.SetCellPosition(moveControl2, tablePosition2);
if (j.Equals(0))
{
moveControl1.Text = (rowIndex + 1).ToString();
moveControl2.Text = (rowIndex).ToString();
}
}
However this code doesn't work and for the example above it produces:
HHHHH
11111
33222
22333
44444
PPPPP
What I believe is happening is tlv is automatically organising it's contents itself during the process of moving them around (maybe to fill vacant spaces?).
rowIndex above is the index of the target row which must be swapped with the row below it. I don't need to worry about checking if there is only 1 row or if it is the last row because I have done that already. Ignore changing the text too, I just need a pointer as to how I can achieve the intended result!
Thank you for absolutely any help you can give :)
The following code does the job:
Code
using System;
using System.Linq;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
// some content
var panel = new TableLayoutPanel
{
Dock = DockStyle.Fill,
ColumnCount = 5,
RowCount = 2
};
for (var y = 0; y < 2; y++)
for (var x = 0; x < 5; x++)
{
var control = new Button {Text = $#"X = {x}, Y = {y}"};
panel.Controls.Add(control, x, y);
}
// swap button
var button = new Button
{
Dock = DockStyle.Fill,
Text = #"Clicky !"
};
button.Click += (o, args) =>
{
var dictionary = panel.Controls
.Cast<Control>()
.ToDictionary(k => k, v => panel.GetCellPosition(v));
foreach (var pair in dictionary)
{
var position = pair.Value;
position.Row ^= 1; // simple row swap
panel.SetCellPosition(pair.Key, position);
}
};
// add to form
var container = new SplitContainer
{
Dock = DockStyle.Fill,
Orientation = Orientation.Horizontal,
SplitterWidth = 5,
BorderStyle = BorderStyle.Fixed3D
};
container.Panel1.Controls.Add(panel);
container.Panel2.Controls.Add(button);
Controls.Add(container);
}
}
}
Before
After
Note
Next time you ask a question, post a Minimal, Complete, and Verifiable example to maximize your chances of getting an answer !
As on why your code didn't work, see previous sentence, e.g what was rowIndex etc ?
Edit
using System;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
const int cols = 5;
const int rows = 6;
// setup layout
var tlp = new TableLayoutPanel
{
ColumnCount = cols,
RowCount = rows,
Dock = DockStyle.Fill,
GrowStyle = TableLayoutPanelGrowStyle.FixedSize
};
for (var i = 0; i < cols; i++)
tlp.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100.0f / cols));
// add header
var label = new Label
{
Text = #"My Header",
BackColor = Color.Red,
Dock = DockStyle.Fill,
TextAlign = ContentAlignment.MiddleCenter
};
tlp.Controls.Add(label);
tlp.SetColumn(label, 0);
tlp.SetRow(label, 0);
tlp.SetColumnSpan(label, cols);
// add some cells
var yMin = 1;
var yMax = 5;
var xMin = 0;
var xMax = cols;
for (var y = yMin; y < yMax; y++)
for (var x = xMin; x < xMax; x++)
{
var color = Color.FromArgb(
255 / (xMax - xMin) * (x - xMin),
128,
255 / (yMax - yMin) * (y - yMin)
);
var label1 = new Label
{
Text = $#"X = {x}, Y = {y}",
BackColor = color,
ForeColor = Color.White,
Dock = DockStyle.Fill,
TextAlign = ContentAlignment.MiddleCenter,
Margin = DefaultMargin
};
tlp.Controls.Add(label1, x, y);
}
// add swapper
var button = new Button
{
Text = #"Clicky !",
Dock = DockStyle.Fill
};
button.Click += (o, args) =>
{
var srcRow = 2;
var tgtRow = 3;
var controls = tlp.Controls.Cast<Control>().ToArray();
var array1 = controls.Where(s => tlp.GetRow(s) == srcRow).ToArray();
var array2 = controls.Where(s => tlp.GetRow(s) == tgtRow).ToArray();
foreach (var control in array1)
tlp.SetCellPosition(control, new TableLayoutPanelCellPosition(tlp.GetColumn(control), tgtRow));
foreach (var control in array2)
tlp.SetCellPosition(control, new TableLayoutPanelCellPosition(tlp.GetColumn(control), srcRow));
};
// pack things up
var sc = new SplitContainer
{
Orientation = Orientation.Horizontal,
BorderStyle = BorderStyle.Fixed3D,
Dock = DockStyle.Fill
};
sc.Panel1.Controls.Add(tlp);
sc.Panel2.Controls.Add(button);
Controls.Add(sc);
}
}
}

Lee's algorithm - finding characters in table

I have problem to find the shortest path between two squares in the grid.
I would like to implement Lee's algorithm, but my struggle is to find the neighbors of specific coordinate in the table.
What I am not quite sure, where to move the cursor in the grid, when I label the neighbors with specific number. I have four movements in the grid, so I label everytime four neighbors of the current point, but where to move then?
Example:
My input:
Size of the grid: MxN
Characters which would be in the table. For example ABCDEF...(It is sort of Keyboard)
String which would be written by the table:
For example: BCD
Output of the program would be minimum of presses to write this specific string.
Start position of the cursor in the grid is in upper left corner. Presses are: up, down, left, right and ENTER - which will print the character
My starting aproach is: First find the position of the character of the string in the table. Then make a matrix with distances, which has in the beginning everywhere zeros. Then check if the character, which was found, is in upper left corner. If is, then number of presses is 1, find another character. Else label neighbors and get to the current letter. If you get to the current letter save number of presses and find next character from the position of the previous character.
See my button project below. Read comments. You can implement with a textboxes so you can put letters into the boxes, or a picture box where yo can added pictures to the cells.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace Buttons
{
public partial class Form1 : Form
{
const int ROWS = 5;
const int COLS = 10;
public Form1()
{
InitializeComponent();
this.Load += new System.EventHandler(this.Form1_Load);
}
public void Form1_Load(object sender, EventArgs e)
{
new MyButton(ROWS, COLS, this);
}
}
public class MyButton : Button
{
const int WIDTH = 50;
const int HEIGHT = 50;
const int SPACE = 5;
const int BORDER = 20;
public static List<List<MyButton>> buttons { get; set; }
public static List<MyButton> buttonList { get; set; }
public Form1 form1;
public int row { get; set; }
public int col { get; set; }
public Boolean[] neighbors { get; set; } //array 0 to 3, 0 top, 1 right, 2 bottom, 3 left with false is no wall true is wall
public MyButton()
{
}
public MyButton(int rows, int cols, Form1 form1)
{
buttons = new List<List<MyButton>>();
buttonList = new List<MyButton>();
this.form1 = form1;
for (int row = 0; row < rows; row++)
{
List<MyButton> newRow = new List<MyButton>();
buttons.Add(newRow);
for (int col = 0; col < cols; col++)
{
MyButton newButton = new MyButton();
newButton.Height = HEIGHT;
newButton.Width = WIDTH;
newButton.Top = row * (HEIGHT + SPACE) + BORDER;
newButton.Left = col * (WIDTH + SPACE) + BORDER;
newButton.row = row;
newButton.col = col;
newRow.Add(newButton);
buttonList.Add(newButton);
newButton.Click += new System.EventHandler(Button_Click);
form1.Controls.Add(newButton);
}
}
neighbors = new Boolean[4];
for (int i = 0; i < 0; i++)
{
neighbors[i] = true;
}
}
public void Button_Click(object sender, EventArgs e)
{
MyButton button = sender as MyButton;
MessageBox.Show(string.Format("Pressed Button Row {0} Column {1}", button.row, button.col));
}
}
}

Conway's Game of Life not updating correctly [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
I wrote a quick implementation of Conway's Game of Life, but it ran awfully slow mostly because my method of checking for neighbouring cells involved looping through the entire grid of cells again, now I've changed my method of checking for neighbouring cells but unfortunately it's not updating correctly anymore, it seems to work fine except that it doesn't create nearly as many new cells as it should.
Now I have spent a good few hours manually debugging the code, going through it with breakpoints and trying to compare the values and calls, but it SEEMS as if my GetNeighbours() method is working, so I concede to you, I can't figure out what's wrong on my own. I've submitted the code below for help.
EDIT: Some of you have pointed out that I can't copy my Grid.cells array the way I am doing it. I've changed it to use Array.Copy() instead but unfortunately it still doesn't work completely. I can't figure it out but it still doesn't seem to create new cells in all cases where it should.
MainForm.cs
public partial class MainFom : Form
{
Grid formGrid;
CancellationTokenSource tokenSrc = new CancellationTokenSource();
public MainFom()
{
InitializeComponent();
}
private void MainFom_Load(object sender, EventArgs e)
{
formGrid = new Grid();
for (int i = 0; i < 50; i++)
{
int xCoord = 10 * i + 12;
Controls.Add(new Label()
{
AutoSize = true,
Text = i.ToString(),
Location = new Point(xCoord, 0),
Font = new Font(Font.FontFamily, 6)
});
for (int s = 0; s < 50; s++)
{
int yCoord = 10 * s + 12;
Controls.Add(new Label()
{
AutoSize = true,
Text = s.ToString(),
Location = new Point(0, yCoord),
Font = new Font(Font.FontFamily, 6)
});
}
}
}
private void MainFom_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawImage(formGrid.toBitmap(), 0, 0);
e.Graphics.Dispose();
}
private void startBtn_Click(object sender, EventArgs e)
{
Task tempTask = Task.Factory.StartNew(
(x) =>
{
while (!tokenSrc.IsCancellationRequested)
{
formGrid.UpdateGrid();
Graphics graphics = this.CreateGraphics();
graphics.Clear(this.BackColor);
graphics.DrawImage(formGrid.toBitmap(), 0, 0);
graphics.Dispose();
}
}, tokenSrc);
startBtn.Hide();
Button stopBtn = new Button() { Text = "Stop", Location = startBtn.Location, Size = startBtn.Size };
this.Controls.Add(stopBtn);
stopBtn.Click += new EventHandler(
(x, y) =>
{
tokenSrc.Cancel();
stopBtn.Hide();
startBtn.Show();
tempTask.Wait();
tokenSrc = new CancellationTokenSource();
});
}
}
Grid.cs
class Grid
{
#region Properties/Fields
const int MAX_CELLS = 50;
Random RNG = new Random();
Cell[,] cells;
int generations = new int();
#endregion
public Grid()
{
cells = new Cell[MAX_CELLS, MAX_CELLS];
for (int x = 0; x < MAX_CELLS; x++)
{
int xCoord = 10 * x + 12;
for (int y = 0; y < MAX_CELLS; y++)
{
int yCoord = 10 * y + 12;
Point point = new Point(xCoord, yCoord);
if (RNG.Next(100) < 20) {
cells[x, y] = new Cell(point, true); }
else {
cells[x, y] = new Cell(point, false);
}
}
}
}
public void UpdateGrid()
{
Cell[,] copy = cells;
for (int x = 0; x < MAX_CELLS; x++)
{
for (int y = 0; y < MAX_CELLS; y++)
{
int neighboursCtr = GetNeighbours(x, y);
//Rule 1: Any live cell with fewer than two live neighbours dies, as if caused by under-population.
if (cells[x, y].IsAlive && neighboursCtr < 2)
{
copy[x, y].Kill();
}
//Rule 2: Any live cell with more than three live neighbours dies, as if by overcrowding.
if (cells[x, y].IsAlive && neighboursCtr > 3)
{
copy[x, y].Kill();
}
//Rule 3: Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.
if (!cells[x, y].IsAlive && neighboursCtr == 3)
{
copy[x, y].Alive();
}
}
}
cells = copy;
generations++;
}
public Bitmap toBitmap()
{
Bitmap gridBmp = new Bitmap(1000, 1000); // TODO: Find optimal size for bmp
Size cellSize = new Size(10, 10);
using (Graphics gfxObj = Graphics.FromImage(gridBmp))
{
// Draw grid here and Dispose() on Pen, gfxObj is implicitly disposed
Pen myPen = new Pen(Color.LightGray);
SolidBrush myBrush = new SolidBrush(Color.Black);
for (int x = 0; x < MAX_CELLS; x++)
{
for (int y = 0; y < MAX_CELLS; y++)
{
if (!cells[x, y].IsAlive)
{
gfxObj.DrawRectangle(myPen, new Rectangle(cells[x, y].point, cellSize));
} else
{
gfxObj.FillRectangle(myBrush, new Rectangle(cells[x, y].point, cellSize));
}
}
}
myPen.Dispose();
myBrush.Dispose();
}
return gridBmp;
}
private int GetNeighbours(int column, int row)
{
int neighbours = new int();
int[] starts = new int[] { Math.Max(0 ,column - 1), Math.Max(0, row - 1) };
int[] ends = new int[] { Math.Min(49, column + 1), Math.Min(49, row + 1) };
double colAndRow = column + row/10;
for (int x = starts[0]; x < ends[0]+1; x++)
{
for (int y = starts[1]; y < ends[1]+1; y++)
{
double xAndY = x + y/10;
if (cells[x, y].IsAlive && xAndY != colAndRow)
{
neighbours++;
}
}
}
return neighbours;
}
}
Cell.cs
struct Cell
{
public bool IsAlive { get; private set; }
public readonly Point point;
public Cell(Point point, bool isAlive) : this()
{
this.point = point;
IsAlive = isAlive;
}
public void Alive()
{
IsAlive = true;
}
public void Kill()
{
IsAlive = false;
}
}
The problem is in your UpdateGrid() method. You're simply assigning the reference for your original array to a new variable:
Cell[,] copy = cells;
But this is still the same object; in particular, there's no difference between calling copy[x, y].Kill() and cells[x, y].Kill(). So you're modifying your state during calculations, this affects your code's logic.
Make a copy of the original using Array.Copy and it should work correctly (there doesn't seem to be anything else wrong with your algorithm).
Arrays are reference types, which means
Cell[,] copy = cells;
doesn't what you probably intent to do. It's not a copy of the source array, so it will manipulate this whilst analyzing the neighbors which will lead to wrong results.
Use Array.Copy.
There are a lot of improvements that can be done.
Take a look at Optimizing Conway's 'Game of Life' and Hashlife
You can start by using LockBits to work faster with the bitmap.
You can use parallel programming to improve the loops: Save time with parallel FOR loop
You can also improve the algorithm avoiding the whole matrix scan each time, and instead maintain a list of the alive cells, and only step thru these cells and it's neighbors.
I've implemented such algorithm in the following C# Game of Life Code.

c# creating dynamic textbox in a second form

I am trying to write a code in order to create dynamic textboxes.
I have Function class and have a second form in my program named ProductForm.cs
What I wanna do is to read some data with a function named GetSpecs in my Function.cs and than inside GetSpecs I want to call a function in another class and send data to my other function under ProductForm.cs class.
I am getting blank form at the end.
a part of my GetSpecs function:
private String GetSpecs(String webData)
{
......
ProductForm form2 = new ProductForm();
form2.CreateTextBox(n);
}
ProductForm.cs
public void CreateTextBox(int i)
{
ProductForm form2 = new ProductForm();
form2.Visible = true;
form2.Activate();
int x = 10;
int y = 10;
int width = 100;
int height = 20;
for (int n = 0; n < i; n++)
{
for (int row = 0; row < 4; row++)
{
String name = "txtBox_" + row.ToString();
TextBox tb = new TextBox();
tb.Name = name;
tb.Location = new Point(x, y);
tb.Height = height;
tb.Width = width + row * 2;
x += 25 + row * 2;
this.Controls.Add(tb);
}
y += 25;
}
}
I get a blank form of ProductForm. Textboxes are not created or I cannot see them.
If I put textbox inside
private void ProductForm_Load(object sender, EventArgs e)
I can see textboxes.
You're creating showing a brand new ProductForm instance (in the form2 variable), then adding controls to this (which is never shown).
You are adding the controls to the current form: this.Controls.Add(tb);, you need to add them to the other form:
form2.Controls.Add(tb);

Categories