1. Как создать вторую форму
Любая форма представляет из себя класс, унаследованный от Form.
Экземпляр главной формы создается в файле Program.cs по умолчанию.

namespace NS
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1()); // <- вот тут
        }
    }
}


Чтобы отобразить вторую форму, надо создать экземпляр этого класса (Form2), например в обработчике события главной 

private void button1_Click(object sender, EventArgs e)
{
    Form2 f = new Form2(); // создаем
    f.ShowDialog(); // показываем
    f.Show() // или так
}

При этом ShowDialog() блокирует главную форму, т.е. управление вернется в нее, только по закрытию второй формы, а Show() просто отображает вторую форму, т.е. будут доступны обе формы.

2. Как передать данный из одной формы в другую
Часто возникает необходимость передать данные из одной формы в другую, я придумал 7 способов, у каждого свои недостатки и достоинства.
2.1 Изменение модификатора доступа
В Form2 Установить модификатор доступа для контрола/поля public
В любом месте Form1

Form2 f = new Form2();
f.ShowDialog();
this.textBox1.Text = f.textBox1.Text;

+ Самый быстрый в реализации и удобный способ
– Противоречит всем основам ООП
– Возможна передача только из более поздней формы в более раннюю
– Форма f показывается только с использованием ShowDialog(), т.е. в первую форму управление вернется только по закрытию второй. Избежать этого можно, сохранив ссылку на вторую форму в поле первой формы

2.2 Использование открытого свойства/метода. Способ очень похож на первый
В классе Form2 определяем свойство (или метод)

public string Data
{
    get
    {
        return textBox1.Text;
    }
}

В любом месте Form1

Form2 f = new Form2();
f.ShowDialog();
this.textBox1.Text = f.Data;

+ Противоречит не всем основам ООП
– Минусы те же

2.3 Передача данных в конструктор Form2
Изменяем конструктор Form2

public Form2(string data)
{
    InitializeComponent();
    //Обрабатываем данные
    //Или записываем их в поле
    this.data = data;
}
string data;

А создаем форму в любом месте Form1 так:

Form2 f = new Form2(this.textBox1.Text);
f.ShowDialog();
//Или f.Show();

+ Простой в реализации способ
+ Не нарушает ООП
– Возможна передача только из более ранней формы в более позднюю

2.4 Передача ссылки в конструктор
Изменяем конструктор Form2

public Form2(Form1 f1)
{
    InitializeComponent();    
    //Обрабатываем данные
    //Или записываем их в поле
    string s = f1.textBox1.Text;
}

А создаем форму в любом месте Form1 так, т.е. передаем ей ссылку на первую форму

Form2 f = new Form2(this);
f.ShowDialog();
//Или f.Show();

+ Доступ ко всем открытым полям/функциям первой формы
+ Передача данных возможна в обе стороны
– Нарушает ООП

2.5 Используем свойство ‘родитель’
При создании второй формы устанавливаем владельца

Form2 f = new Form2();
f.Owner = this;
f.ShowDialog();

Во второй форме определяем владельца

Form1 main = this.Owner as Form1;
if(main != null)
{
    string s = main.textBox1.Text;
    main.textBox1.Text = "OK";
}

+ Доступ ко всем открытым полям/функциям первой формы
+ Передача данных возможна в обе стороны
+ Не нарушает ООП

2.6 Используем отдельный класс
Создаем отдельный класс, лучше статический, в основном namespace, т.е. например в файле Program.cs

static class Data
{
    public static string Value { get; set; }
}

Его открытые свойства/методы доступны из любой формы.

Data.Value = "111";

+ Самый удобный способ, когда данные активно используются несколькими формами.

2.7 Использование функций обратного вызова
2.7.1 Передача метода в конструктор
Создаем в основном namespace делегат

public delegate void MyDelegate(string data);

В Form1 создаем метод, который будет обрабатывать принятые данные

void func(string param)
{
    // обработка
}

Создаем вторую форму так:

Form2 f = new Form2(new MyDelegate(GetData));
f.ShowDialog();

При этом изменяем конструктор второй формы, чтобы он принимал делегат

MyDelegate d;
public Form2(MyDelegate sender)
{
    InitializeComponent();
    d= sender;
}

И в любой момент отправляем данные

d(textBox1.Text);
Полный код
//Program.cs
using System;
using System.Windows.Forms;
namespace NS
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
    }
    public delegate void MyDelegate(string data);
}
//Form1.cs
using System;
using System.Windows.Forms;
namespace NS
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        private void button1_Click(object sender, EventArgs e)
        {
            Form2 f = new Form2(new MyDelegate(func));
            f.ShowDialog();
        }
        void func(string param)
        {
            MessageBox.Show(param + "!");
        }
    }
}
//Form2.cs
using System;
using System.Windows.Forms;
namespace NS
{
    public partial class Form2 : Form
    {
        private MyDelegate d;
        public Form2(MyDelegate sender)
        {
            InitializeComponent();
            d = sender;
        }
        private void button1_Click(object sender, EventArgs e)
        {
            d(textBox1.Text);
        }
    }
}

2.7.2 Создание отдельного класса с делегатом
Создаем в основном namespace отдельный класс

public static class Data
{
    public delegate void MyEvent(string data);
    public static MyEvent EventHandler;
}

В первой форме добавляем обработчик

void func(string param)
{
    MessageBox.Show(param);
}

и инициализируем EventHandler

Data.EventHandler = new Data.MyEvent(func);

Вторую форму создаем обычным способом и вызываем из нее

Data.EventHandler(textBox1.Text);
Полный код
//Program.cs
using System;
using System.Windows.Forms;
namespace NS
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
    }
    public static class Data
    {
        public delegate void MyEvent(string data);
        public static MyEvent EventHandler;
    }
}
//Form1.cs
using System;
using System.Windows.Forms;
namespace NS
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            Data.EventHandler = new Data.MyEvent(func);
        }
        private void button1_Click(object sender, EventArgs e)
        {
            Form2 f = new Form2();
            f.ShowDialog();
        }
        void func(string param)
        {
            MessageBox.Show(param + "!");
        }
    }
}
//Form2.cs
using System;
using System.Windows.Forms;
namespace NS
{
    public partial class Form2 : Form
    {
        public Form2()
        {
            InitializeComponent();
        }
        private void button1_Click(object sender, EventArgs e)
        {
            Data.EventHandler(textBox1.Text);
        }
    }
}

+ Наиболее гибкий способ передачи данных
– Сложен в реализации и понимании

3. Как получить доступ к контролу из другого потока
.NET не позволяет обращаться к контролам напрямую из других потоков.
3.1 Простой и неправильный способ
Отменяем проверку, из какого потока используется контрол

System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;

Для одного раза может и сработать, но делать так крайне не рекомендуется.

3.2 Использование методов Invoke/BeginInvoke
Эти методы выполняют указанные делегаты в том потоке, в котором контрол был создан.
Invoke вызывает делегат синхронно, BeginInvoke – асинхронно.
Чтобы определить, требуется ли Invoke используйте свойство InvokeRequired.
Например, объявляем делегат

delegate void Del(string text);

и вызываем Invoke

textBox1.Invoke(new Del((s) => textBox1.Text = s), "newText");

Вместо объявления новых делегатов можно использовать готовые, Action или Func

Пример готового, потоко-безопасного метода

void SetTextSafe(string newText)
{
    if (textBox1.InvokeRequired) textBox1.Invoke(new Action<string>((s) => textBox1.Text = s), newText);
    else textBox1.Text = newText;
}

4. Как динамически добавить/удалить контрол.
4.1 Добавление
Пример динамического создания кнопки:

System.Windows.Forms.Button button1 = new System.Windows.Forms.Button(); // создаем контрол
button1.Location = new System.Drawing.Point(101, 50); // устанавливаем необходимые свойства
button1.Name = "button1";
button1.Size = new System.Drawing.Size(75, 23);
button1.TabIndex = 0;
button1.Text = "button1";
button1.UseVisualStyleBackColor = true;
button1.Click += new System.EventHandler(button1_Click); // button1_Click - функция обработчик события нажатия на кнопку
Controls.Add(button1); // добавляем на форму

Если непонятно как создать другие контролы – откройте функцию InitializeComponent, которая находится в конструкторе вашей формы. Это функция – код генерируемый дизайнером, посмотрите, как дизайнер создает тот или иной компонент и скопируйте код.
4.2 Удаление

Controls.Remove(button1);
button1.Dispose();

5. Как создать массив контролов.

Точно также, как обычный массив

TextBox[] tb = new TextBox[10];
for (int i = 0; i < tb.Length; i++)
{
    tb[i] = new System.Windows.Forms.TextBox();
    tb[i].Location = new System.Drawing.Point(101, 50 + i * 30);
    tb[i].Name = "textBox" + i.ToString();
    tb[i].Size = new System.Drawing.Size(75, 23);
    tb[i].TabIndex = i;
    tb[i].Text = "textBox" + i.ToString();                
    Controls.Add(tb[i]);
}

Теперь получить доступ к конкретному текстбоксу можно по индексу:

tb[2].Text = "newText";

6. Как получить доступ к контролу по имени.

(Controls["textBox1"] as TextBox).Text = "newText";

7. Как пройтись по всем однотипным котролам.

//Пример обхода всех TextBoxов
foreach (Control control in Controls)
{
    TextBox tb = control as TextBox;
    if (tb != null)
    {
        tb.Text = "Text";
    }
}