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

 namespace cForm
 {
 public partial class cForm : Form
 {
 // Средства для отображения объектов на плоскости.
 Color backColor;
 Pen greedPen;
 Pen pen0;
 SolidBrush br;

 // Действующие лица. Массив объектов xPoint.
 xPoint[] pt;

 // Количество точек.
 const int nPT = 10000;

 // Синхронизаторы изменения направления перемещения объектов.
 public int NX, NY;

 // Предельные характеристики размеров клиентской области. 
 const int minHeight = 100;
 const int minWidth = 100;
 const int maxHeight = 950;
 const int maxWidth = 950;

 // Средство масштабирования. Всеобщая единица измерения.
 private double rPoint;

 // Абсолютные неизменяемые характеристики рамки окна приложения.
 int borderHeight;
 int borderWidth;

 // Область рисования. Изображение формируется в памяти.
 // На "поверхности" битовой карты
 // (объект - представитель класса Graphics, 
 // создается в результате выполнения метода FromImage),
 // средствами векторной графики (методы класса Graphics) 
 // рисуется картинка.
 // Изображение проецируется на поверхность элемента управления 
 // mainPictureBox 
 // путем модификации свойства Image этого элемента управления.
 private Bitmap bmp;

 // Таймер. Фактически представляет собой делегат,
 // который через фиксированные интервалы
 // времени активизирует соответствующее событие
 // (передает управление методу mainTimer_Tick).
 private System.Windows.Forms.Timer mainTimer;

 // Конструктор формы.
 public cForm()
 {
 int i;

 // Цвет фона картинки. Используется при перерисовке изображения.
 backColor = Color.FromArgb(255,0,0,0);

 // Средства для рисования.
 greedPen = new Pen(Color.FromArgb(50, 0, 0, 255), 1);
 pen0 = new Pen(Color.FromArgb(255,0,0,0),1);
 br = new SolidBrush(Color.FromArgb(0, 0, 0, 0));

 // Обязательно смотреть код начальной инициализации элементов формы!
 InitializeComponent();

 // Определение неизменяемых характеристик окна приложения (формы).
 GetBordersGabarits();

 // Начальная коррекция размеров клиентской области окна приложения.
 // "Прямой" вызов метода - обработчика события изменения размеров 
 // окна приложения.
 bForm00_Layout(null, null);

 // Определение предельных габаритов окна приложения. 
 SetMinMaxSize();

 // Значения синхронизаторов.
 // Неплохо приспособить специальные элементы управления,
 // которые позволяли бы изменять эти значения в интерактивном режиме!
 NX = 50;
 NY = 50;

 // Инициализация популяции.
 // Все располагаются в одном месте – в центре клиентской области окна.
 // Одинаковые значения двух последних параметров приводят к тому, что
 // члены популяции синхронно изменяют направление движения. 
 pt = new xPoint[nPT];
 for (i = 0; i < pt.Length; i++)
 {
 pt[i] = new xPoint(
   (int)(this.ClientSize.Width  / (2*rPoint)),
   (int)(this.ClientSize.Height / (2*rPoint)),
    0, //NX, // xPoint.rnd.Next(0, NX+1), // А такие значения нарушают 
    0  //NY  // xPoint.rnd.Next(0, NY+1) // синхронность поведения объектов.
    );
 }

 // Процесс пошел!
 mainTimer.Start();
 }

 // Определение относительной величины rPoint реализовано как свойство.
 // Замечательная особенность свойства!
 // Оно имеет явную спецификацию возвращаемого значения.
 // И точно такого же типа должен быть единственный параметр value!
 private Size rPointSet
 {            
 set
 { 
 Size s = value;
 rPoint = Math.Sqrt(2.0*s.Width*s.Height) * 0.002;            
 }        
 }

 // А вот перегрузить свойство нельзя! Невидимый параметр value 
 // и возвращаемое значение свойства должны быть одного типа. 
 // Именно поэтому МНЕ пришлось вводить пару свойств.
 public double rPointGet
 {
 get
 {
 return rPoint;
 }
 }        

 // Обработчик события перерисовки формы.
 // В списке параметров ссылка на объект - инициализатор события,
 // а также ссылка на объект – носитель информации об инкапсуляторе
 // данной поверхности рисования.
 // Способ получения этой информации простой – через свойство Graphics:
 //      Graphics gr = e.Graphics;
 // И знай рисуй себе!
 // Однако эта ссылка нам не понадобится, поскольку мы рисуем
 // не на ПОВЕРХНОСТИ ЭЛЕМЕНТА УПРАВЛЕНИЯ,
 // а в памяти, на ПОВЕРХНОСТИ БИТОВОЙ КАРТЫ. 
 private void bForm00_Paint(object sender, PaintEventArgs e)
 {
 xRedraw(); 
 }
               
 // Событие Layout возникает при удалении или добавлении дочерних
 // элементов управления,
 // изменении границ элемента управления и других изменениях, 
 // в том числе и изменении размеров формы, которые могут повлиять 
 // на макет элемента.
 // Рекомендуется использовать именно это событие,
 // а не события ..._Resize или ..._SizeChanged. Как сказано 
 // в руководстве, событие Layout возникает в ответ на события
 // ..._Resize и ..._SizeChanged,
 // а также в других ситуациях, когда может понадобиться применение макета.
 private void bForm00_Layout(object sender, LayoutEventArgs e)
 {
 // Использование сразу ДВУХ величин – длины и ширины клиентской 
 // области окна!
 // Вот оптимальное решение проблемы поддержки ПОСТОЯННОГО соотношения
 // характеристик клиентской области  
 // (среднее арифметическое для поддержки равенства сторон).
 // ПЛЮС поправка на размеры рамки окна приложения. 

 Size = new System.Drawing.Size
     (
     (ClientSize.Width + ClientSize.Height)/2 + borderWidth,
     (ClientSize.Width + ClientSize.Height)/2 + borderHeight
     );

 // После коррекции размеров окна – устанавливаем значение величины 
 // rPoint, которое производится через обращение к свойству rPointSet.   
 rPointSet = this.ClientSize;

 // Новая битовая карта под новый размер клиентской области.
 bmp = new Bitmap(this.ClientSize.Width, this.ClientSize.Height);

 // Объявляется недопустимой конкретная область элемента управления 
 // (здесь форма) и происходит отправка соответствующего сообщения
 // (Paint) элементу управления (форме). При этом значение 
 // параметра (true) 
 // объявляет недопустимыми назначенные элементу управления 
 // дочерние элементы.
 // Для каждого элемента автоматически формируется 
 // объект-представитель
 // класса Graphics, инкапсулирующий поверхность для рисования.
 // Для активизации процесса перерисовки соответствующего 
 // элемента управления требуется объявить метод – обработчик события
 // (в нашем случае это bForm00_Paint).
 this.Invalidate(false); 
 }

 // Обработчик события таймера:
 // всем амебам надлежит переопределить собственные координаты и
 // перерисовать картинку! Здесь также можно было бы объявить 
 // недействительной и требующей перерисовки поверхность формы.
 // Это просто: this.Invalidate(false);
 // Однако никто не помешает нам сделать все напрямую, через метод xRedraw.  
 private void mainTimer_Tick(object sender, EventArgs e)
 {
 xPoint.setNextPosition(this, pt);
 xRedraw();
 }

 // Перерисовка.
 private void xRedraw()
 {
 // Поверхность для рисования на битовой карте!
 Graphics gr = Graphics.FromImage(bmp);

 // Очистить поверхность и закрасить ее в ненавязчивый черный цвет!
 gr.Clear(backColor);

 // Нарисовать решетку!
 DrawGreed(gr);

 // Нарисовать объекты!
 foreach (xPoint xp in pt)
 {
 // Сообщили кисти цвет объекта... 
 br.Color = xp.xColor;
 // Закрасили объект.
 gr.FillEllipse(br,
                (float)((xp.p.X * rPoint) - rPoint),
                (float)((xp.p.Y * rPoint) - rPoint),
                (float)(2 * rPoint),
                (float)(2 * rPoint));
 // Сообщили перу цвет объекта... 
 pen0.Color = xp.xColor;
 // Нарисовали контур амебы...
 gr.DrawEllipse(pen0,
                (float)((xp.p.X * rPoint) - rPoint),
                (float)((xp.p.Y * rPoint) - rPoint),
                (float)(2 * rPoint),
                (float)(2 * rPoint));
 }

 // Рисование закончено. Подготовить объект-поверхность к удалению.
 gr.Dispose();

 // Отобразить картинку!
 // Это всего лишь модификация свойства Image элемента управления PictureBox. 
 this.mainPictureBox.Image = bmp;
 }

 // На поверхности битовой карты (параметр gr) рисуется координатная сетка. 
 private void DrawGreed(Graphics gr)
 {
 float i;
 float w = (float)(this.ClientSize.Width/25.0F);
 float h = (float)(this.ClientSize.Height/25.0F);
 float W = this.ClientSize.Width;
 float H = this.ClientSize.Height;

 for (i = h; i < H; i += h)
 {
 gr.DrawLine(this.greedPen, 0, i, W, i);
 }

 for (i = w; i < W; i += w)
 {
 gr.DrawLine(this.greedPen, i, 0, i, H);
 }
 }
 }
 }

Листинг 17.3.
Закрыть окно