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. |
Закрыть окно |