Описание проекта
Проект TOWERS представляет собой компьютерную реализацию известной логической задачи "Ханойские башни". Эта задача состоит в следующем: имеются три колышка, на один из которых нанизано (в порядке уменьшения размера) несколько дисков, образующих "башню"; требуется переместить всю башню на один из пустых колышков, пользуясь другим пустым колышком как вспомогательным. Переносить можно по одному диску, причем больший диск нельзя помещать на меньший. Для решения задачи с N дисками минимальное число переносов равно 2N-1.
В приложении TOWERS вместо колышков используются компоненты-контейнеры GroupBox, а вместо дисков перемещаются прямоугольные блоки (компоненты Label), создаваемые непосредственно в ходе выполнения программы. Перемещение блоков реализуется с помощью механизма drag&drop. Предусматриваются средства проверки правильности решения, а также демо-режим, показывающий, в каком порядке надо перемещать блоки, чтобы решить задачу.
Шаг 1. Создание начальной позиции
После создания проекта TOWERS разместите в форме Form1 три компонента типа GroupBox (они получат имена groupBox1 — groupBox3) и компонент типа NumericUpDown (он получит имя numeгicUpDown1). Настройте свойства формы Form1 и добавленных компонентов:
Компонент | Свойство | Значение |
Form1 | Text | Ханойские башни |
MaximizeBox | False | |
FormBorderStyle | FixedSingle | |
StartPosition | CenterScreen | |
groupBox1 | Text | Начальная позиция |
groupBox2 | пустая строка | |
groupBox3 | пустая строка | |
numericUpDown | Minimum | |
Maximum | ||
Value |
Расположите компоненты в соответствии с рис. 1:
Рис. 1. Вид формы Form1 для проекта TOWERS на начальном этапе разработки
Определите обработчик события Load для формы Form1.
private void Forml_Load(object sender, EventArgs e)
{
Random r = new Random();
int n = (int) numericUpDown1.Value, w = groupBox1.Width, h = groupBox1.Height;
for (int i = 0; i < n; i++)
{
Label lb = new Label();
lb.Parent = groupBox1;
lb.BorderStyle = BorderStyle .FixedSingle;
lb.Size = new Size((w - 10) * (n - i) / n, (h - 15) / n);
lb. Location =new Point((w - lb.Width) / 2, h - 2 - (i + 1) * lb.Height);
lb.BackColor = Color.FromArgb(r.Next(256), r.Next(256), r.Next (256));
}
}
Компоненты-метки Label создаются непосредственно при выполнении метода Form1_Load. После создания метки (с помощью конструктора new Label()) для нее определяется родительский компонент Parent, в котором будет изображена созданная метка, стиль ее границы, размер, положение и фоновый цвет. В качестве родительского компонента, определяемого свойством Parent, можно указать любой компонент-контейнер (в частности, форму). Родительский компонент отвечает за перерисовку всех своих дочерних компонентов. Если компонент не имеет родителя, то его невозможно отобразить на экране.
Результат: при запуске программы на компоненте groupBox1 рисуется башня из четырех разноцветных блоков — компонентов типа Label (цвет блоков определяется случайным образом).
Шаг 2. Перерисовка башни при изменении количества блоков
Определите обработчик события ValueChanged для компонента numericUpDown1.
private void numericUpDownl_ValueChanged(object sender, EventArgs e)
{
Form1_Load (this, null);
}
Результат: при изменении значения компонента numericUpDown1 создается новая башня с указанным числом блоков.
Ошибка: новая башня рисуется подстарой.
Исправление: в класс Form1 добавьте новый метод label_Dispose:
private void label_Dispose (GroupBox gb)
{
for (int i = gb.Controls.Count - 1; i >= 0; i--)
gb.Controls [i].Dispose();
}
В начало метода numericUpDownl_ValueChanged вставьте оператор:
label_Dispose(groupBox1);
Результат: теперь все блоки старой башни исчезают с экрана, а также освобождают свои ресурсы (благодаря вызову метода Dispose).
Недочет: в процессе формирования новой башни на экране (в верхней части компонента groupBox1) мелькают посторонние фрагменты изображений. Отмеченный недочет связан с тем, что после определения родительского компонента метка немедленно рисуется на нем, а все последующие изменения ее свойств приводят к ее перерисовке.
Исправление: в методе Form1_Load переместите оператор
lb.Parent = groupBox1;
в конец цикла (после оператора, определяющего фоновый цвет метки).
Результат: теперь все настройки свойств метки выполняются до определения ее родительского компонента, и поэтому не сопровождаются ее перерисовкой на экране (метка рисуется единственный раз после задания всех ее свойств).