Шаг 3. Перетаскивание блоков на новое место




В конструктор класса Form1 добавьте новый оператор:

groupBox1.AllowDrop = groupBox2.AllowDrop = groupBox3.AllowDrop = true;

В класс Form1 добавьте новые методы label_MouseDown и label_Move:

private void label_MouseDown(object sender, MouseEventArgs e)

{

if (e.Button == MouseButtons.Left)

DoDragDrop(sender, DragDropEffects.Move);

}

private void label_Move(Label lb, GroupBox gb)

{

lb.Parent = gb;

lb. Top = gb.Height - 2 - gb.Controls.Count * lb. Height;

}

В методе Form1Load в конец тела цикла for добавьте следующий оператор:

lb.MouseDown += label_MouseDown;

Определите обработчики событий DragEnter и DragDrop для компонента groupBoxl, после чего свяжите созданные обработчики с собы­тиями DragEnter и DragDrop компонентов groupBox2 и groupBox3.

private void groupBox1_DragEnter(object sender, DragEventArgs e)

{

e.Effect = DragDropEffects.Move;

}

private void groupBox1_DragDrop(object sender, DragEventArgs e)

{

Label lb = e.Data.GetData(typeof(Label)) as Label;

GroupBox gb = sender as GroupBox;

if (gb == lb.Parent)

return;

label_Move (lb, gb);

}

Результат: любой блок (т. е. метку типа Label) можно переместить на другую башню (т. е. в другой компонент GroupBox), причем перемещенный блок всегда будет располагаться на вершине башни.

Ошибка: переместить можно не только верхний, но и любой другой блок башни.

Исправление: измените метод groupBox1_DragEnter:

private void groupBoxl_DragEnter(object sender, DragEventArgs e)

{

Label lb = e.Data.GetData(typeof(Label)) as Label;

if (lb.Parent.Controls[lb.Parent.Controls.Count - 1]!= lb)

e.Effect = DragDropEffects.None;

else

e.Effect = DragDropEffects.Move;

}

Результат: теперь переместить можно только верхний блок башни, при по­пытке переместить нижние блоки курсор перетаскивания становится запре­щающим.

Осталось учесть дополнительное условие задачи: блок не может перемещаться на башню с верхним блоком меньшего размера. Для этого еще раз изменим метод groupBox1_DragEnter:

private void groupBox1_DragEnter(object sender, DragEventArgs e)

{

Label lb = e.Data.GetData(typeof(Label)) as Label;

int k = int.MaxValue;

GroupBox gb = sender as GroupBox;

if (gb.Controls.Count > 0)

k = gb.Controls[gb.Controls.Count - 1].Width;

if (lb.Parent.Controls[lb.Parent.Controls.Count - 1]!= lb || lb.Width > k)

e.Effect = DragDropEffects.None;

else

e.Effect = DragDropEffects.Move;

}

Результат: при перемещении блока учитывается дополнительное условие.

Ошибка: при изменении количества меток с помощью компонента numericUpDown1 удаляются только те метки, которые находятся в компоненте groupBox1.

Исправление: измените метод numericUpDownl_ValueChanged:

private void numericUpDown1_ValueChanged(object sender, EventArgs e)

{

label_Dispose(groupBox1);

label_Dispose (groupBox2);

label_Dispose (groupBox3);

Form1_Load(this, null);

}

Шаг 4. Восстановление начальной позиции и подсчет числа перемещений блоков

Разместите в форме Form1 кнопку button1, свойству Text кнопки присвойте значение Сброс и свяжите событие click кнопки button1 с существующим об­работчиком numericUpDown1_ValueChanged. Кроме того, разместите в форме Form1 метку label1 (ее свойство Text изменять не требуется). Расположите добавленные компоненты в соответствии с рис. 2.

Рис. 2. Вид формы Form1 для проекта TOWERS на промежуточном этапе разработки

В класс Form1 добавьте описания двух полей:

private int count;

private int minCount;

и вспомогательный метод:

private void Info()

{

label1.Text = string.Format("Число ходов: {0} ({1})", count, minCount);

}

В метод Form1_Load добавьте три оператора:

count = 0;

minCount = (int) Math.Round (Math.Pow (2, n)) - 1;

Info();

В метод label_Move добавьте два оператора:

count++;

Info ();

Результат: для восстановления начальной позиции с тем же количеством бло­ков следует нажать кнопку Сброс. Если при восстановлении начальной по­зиции требуется изменить число блоков, то по-прежнему достаточно указать новое значение в компоненте numericUpDown1 (нажимать кнопку Сброс в этом случае не требуется). Информация о числе ходов (т. е. перемещений блоков) выводится в тексте метки iabel1. Там же в скобках указывается количество ходов, минимально необходимое для решения задачи с данным числом блоков N (это количество равно 2N-1). Обратите внимание на то, что перемещения блока в пределах одного и того же компонента GroupBox при подсчете числа ходов не учитываются.

Для нахождения величины 2N была использована функция Pow класса Math. Поскольку она возвращает результат типа double, полученное значение долж­но преобразовываться к целому типу. Так как при преобразовании (int) про­исходит отбрасывание дробной части, мы предварительно выполняем округление полученного числа до ближайшего целого с помощью функции Round. Заметим, что после округления необходимость в преобразо­вании (int) сохраняется, так как функция Round возвращает значение типа double (хотя и с нулевой дробной частью).

Шаг 5. Проверка решения задачи

Разместите в форме Form1 под имеющейся меткой label1 еще одну метку (label2), ее свойству Text присвойте значение Задача решена!, а свойству ForeColor — значение Green.

В метод Form1_Load добавьте оператор:

label2.Visible = false;

Добавьте в конец метода label_Move следующий фрагмент:

if (groupBox2.Controls.Count == numericUpDown1.Value || groupBox3.Controls.Count == numericUpDown1.Value)

label2.Visible = true;

Результат: задача считается решенной и об этом выводится сообщение «Задача решена!», если размер башни в конечной позиции (т. е. в компоненте groupBox2 или groupBox3) равен общему числу блоков.

Недочет: после решения задачи по-прежнему разрешено перемещение блоков.

Исправление: добавьте в начало метода groupBox1_DragEnter следующий фрагмент:

if (label2.Visible)

{

e.Effect = DragDropEffects.None;

return;

}

Результат: после решения задачи блоки нельзя перемещать.

Шаг 6. Выполнение задачи в демо-режиме

Разместите в форме Form1 еще одну кнопку (button2) и присвойте ее свой­ству Text значение демо-режим. Форма Form1 примет вид, приведенный на рис. 3.

Рис. 3. Окончательный вид формы Form1 для проекта TOWERS

В класс Formlдобавьте новый метод step:

private void Step(int n, GroupBox src, GroupBox dst, GroupBox tmp)

{

if (n == 0)

return;

Step(n - 1, src, tmp, dst);

if (button1. Enabled)

return;

label_Move (src.Controls [src.Controls.Count - 1 ] as Label, dst);

Application.DoEvents();

System. Threading. Thread. Sleep (1500 /((int) numericUpDown1.Value) - 1);

Step(n - 1, tmp, dst, src);

}

Метод step (n, src, dst, tmp) определяет действия, необходимые для ре­шения задачи с n блоками; при этом параметры src, dst и tmp типа GroupBox определяют начальную позицию башни src (source), конечную позицию dst (destination) и вспомогательную область tmp (temporary), используемую при перемещении блоков. Очевидно, что для решения задачи с n блоками достаточ­но выполнить три действия:

1. Переместить башню, состоящую из n-1 блока, из области src в область tmp, используя область dst как вспомогательную.

2. Переместить нижний, n-й блок из области src в область dst.

3. Переместить башню, состоящую из n - 1 блока, из области tmp в область dst, используя область src как вспомогательную.

При этом действия 1 и 3 сводятся к вызову того же метода step с первым па­раметром, равным n - 1. Таким образом, в методе реализуется рекурсивный алгоритм. Цепочку рекурсивных вызовов метода step следует прервать, когда параметр N станет равным 0. Кроме того, в методе step предусмотрен допол­нительный вариант выхода: в случае, если была повторно нажата кнопка Демо-режим (для проверки этого действия анализируется свойство Enabled компо­нента button1). Действие 2 реализовано с помощью метода labe1_Move. После вызова этого метода необходимо вызвать метод DoEvents класса Application, который обеспечит перерисовку перемещенной метки на новом месте, а также позволит обработать нажатие кнопки Демо-режим, если пользователь захочет досрочно выйти из демонстрационного режима. Наконец, вызывается метод sleep класса Thread, приостанавливающий выполнение программы на ука­занный промежуток времени (этот промежуток зависит от количества блоков).

Определите обра­ботчик события clickдля кнопки button2:

private void button2_Click(object sender, EventArgs e)

{

numericUpDown1.Enabled = button1.Enabled =! button1.Enabled;

if (!button1.Enabled)

{

if (groupBox1.Controls.Count!= numericUpDown1.Value)

numericUpDown1_ValueChanged(null, null);

Step((int) numericUpDown1.Value, groupBox1, groupBox3, groupBox2);

numericUpDown1.Enabled = button1.Enabled = true;

}

}

В начало метода label1_MouseDown добавьте следующий фрагмент:

if (! button1.Enabled)

return;

Результат: при нажатии кнопки Демо-режим программа переходит в демонстрационный режим, в котором показывается правильное решение задачи с указанным числом блоков (блоки перемещаются автоматически). В демо-режиме блокируются компонент numericUpDownl и кнопка Сброс, а также запрещено перетаскивание меток. Выход из демо-режима происходит после завершения решения задачи, а также при повторном нажатии кнопки Демо-режим (в последнем случае после выхода из демо-режима можно про­должать решать задачу самостоятельно).

Ошибка: при попытке завершить программу, находящуюся в демо-режиме, возникает ошибка времени выполнения ArgumentOutOfRangeException, свя­занная с тем, что ранее вызванные методы step продолжают выполняться уже после того, как свойства-коллекции Controls компонентов GroupBox были очи­щены в результате завершающих действий программы.

Исправление: определите обработчик события FormClosed для формы Form1:

private void Forml_FormClosed(object sender, FormClosedEventArgs e)

{

button1.Enabled = true;

}

Результат: теперь при закрытии формы кнопка button1 делается доступной, что позволяет немедленно завершить все рекурсивные вызовы метода step без обращения к коллекциям Controls.

Приведем изображение работающей программы после решения задачи с пя­тью блоками (рис. 4).

 

Рис. 4. Вид работающего приложения TOWERS

 



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2016-08-07 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: