Опубликован: 13.07.2010 | Доступ: свободный | Студентов: 889 / 20 | Оценка: 4.40 / 4.20 | Длительность: 77:34:00
Самостоятельная работа 25:

Пользовательский интерфейс Windows Forms для C#

Упражнение 2. Простой калькулятор

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

  • Добавьте к проекту новую форму командой Project/Add Windows Form и задайте ей имя Calculate

  • Поместите на форму элементы из вкладки Common Controls панели Toolbox в соответствии с таблицей, чтобы пользовательский интерфейс формы был таким


Таблица свойств формы Calculate
Элемент Свойство Значение
Form Name Calculate
  StartPosition Manual
  Location 0; 0
  Text Простой калькулятор
  Icon Выбрать по усмотрению...(можно из Source)
  FormBorderStyle FixedDialog
Label Name lblFirst
  Text Первое число:
  TextAlign MiddleRight
Label Name lblSecond
  Text Второе число:
  TextAlign MiddleRight
Label Name lblResult
  Text Результат:
  TextAlign MiddleRight
TextBox Name txtFirst
  Text пусто
TextBox Name txtSecond
  Text пусто
TextBox Name txtResult
  Text пусто
  ReadOnly True
  TabStop False
Button Name btnIncrement
  Text +
  Font/Bold True
  Font/Size 20
  Size 40; 40
Button Name btnDecrement
  Text -
  Font/Name Courier New
  Font/Bold True
  Font/Size 20
  Size 40; 40
Button Name btnIncrease
  Text *
  Font/Name Courier New
  Font/Bold True
  Font/Size 20
  Size 40; 40
Button Name btnDivide
  Text :
  Font/Name Courier New
  Font/Bold True
  Font/Size 20
  Size 40; 40
Button Name btnExit
  Text Выход

Здесь мы использовали некоторые префиксы при назначении имен элементам управления. Для лучшей узнаваемости в коде программы элементов управления и их типа рекомендуется использовать общеупотребительные префиксы, которые приведены в таблице

Рекомендуемые префиксы для некоторых элементов управления
Объект Префикс
Button btn
CheckBox chk
ComboBox cmb
DataGrid dat
Form frm
Label lbl
ListBox lst
MenuStrip mnu
PictureBox pic
RadioButton rad
TextBox txt
  • Добавьте к классу Calculate функцию Main() для автономной отладки формы
static void Main()
    {
      Application.Run(new Calculate());
    }
Листинг 25.10. Добавление к форме Calculate точки входа
  • Выполните команду меню WinFormsApp Properties и для вкладки Application установите в свойстве Startup Object форму Calculate стартовой
  • Защитите интерфейс формы Calculate от случайных изменений, выполнив команду Format/Lock Controls меню оболочки

Теперь нужно реализовать функциональность интерфейса. Прежде всего нужно защитить поля ввода от всех символов, кроме цифр, одного знака минус и одной точки. Стрелки и клавиша Del в текстовых полях функционируют, но работу клавиши Backspace нужно учесть явно.

  • Объявите в конце класса Calculate логическое поле isNumeric для отфильтровывания нецифровых символов, которые пользователь может попытаться ввести в текстовые поля
private bool isNumber = false;
Листинг 25.11. Добавим логическую переменную isNumeric
  • Выделите текстовое поле txtFirst и через панель Properties в режиме Events создайте обработчик для события KeyDown. Заполните обработчик так
private bool isNumber = false;
  
    private void txtFirst_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
    {
      isNumber = 
        e.KeyCode >= Keys.D0 && e.KeyCode <= Keys.D9 // keyboard - основная клавиатура
        || e.KeyCode >= Keys.NumPad0 && e.KeyCode <= Keys.NumPad9 // keypad - дополнительная клавиатура
        || e.KeyCode == Keys.Back;
    }
Листинг 25.12. Обработчик события KeyDown для текстовых полей txtFirst и txtSecond
  • Выделите текстовое поле txtFirst и через панель Properties в режиме Events создайте обработчик для события KeyPress. Заполните обработчик так
private void txtFirst_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e)
    {
      TextBox box = (TextBox) sender;// Явное преобразование типов
  
      switch(e.KeyChar) // Переключатель
      {
        case '-': // Разрешаем минус, если он первый
          if(box.Text.Length == 0)
            isNumber = true;
          break;
        case '.':  
          // Точка не должна быть первой
          if(box.Text.Length == 0)
            break;
          // Точка не должна следовать сразу за минусом
          if(box.Text[0] == '-' && box.Text.Length == 1)
            break;
          // Точка должна быть одна
          if(box.Text.IndexOf('.') == -1)
            isNumber = true; // Еще не было точек
          break;
      }
  
      // Запрещаем в текстовом поле лишние символы
      if(!isNumber)
        e.Handled = true;
    }
Листинг 25.13. Обработчик события KeyPress для полей txtFirst и txtSecond
  • Выделите текстовое поле txtSecond и через панель Properties в режиме Events подключите к событиям KeyDown и KeyPress соответствующие обработчики, только что созданные для поля txtFirst. Обработчики подключите через раскрывающиеся списки полей значений


Здесь мы одни и те же обработчики используем для разных полей, поскольку необходимо выполнять одинаковые действия. Задача одна - не допускать для ввода в поля никакие другие символы, кроме цифр, одного минуса и одной точки. Возможность ввода цифр реализована для основной ( keyboard ) и дополнительной ( keypad ) частей клавиатуры. Параметр sender передает в обработчик ссылку на активное текстовое поле, вызвавшее событие редактирования.

Конечно, программа будет более понятна, если для каждого элемента создавать свой обработчик. Но очень часто, как в нашем случае, преобладающая часть кода может быть совершенно одинакова в каждом обработчике. Тогда имеет смысл объединять несколько индивидуальных обработчиков в один. Какой подход выбрать - зависит от конкретной задачи и предпочтений программиста.

  • Добавьте в конец класса Calculate объявления числовых переменных, с которыми мы будем производить простые арифметические операции
// Объявляем числовые переменные как поля-члены класса
    // Их можно объявить как локальные и внутри обработчика btn_Click()
    // Но оставляем область видимости класс, вдруг где-нибудь еще пригодятся!
    private double numFirst, numSecond, numResult;
Листинг 25.14. Добавление числовых переменных-членов класса Calculate
  • Перейдите в режим Design формы Calculate и выделите все четыре кнопки с операциями


  • Установите в панели Properties режим Events, найдите событие Click и введите вручную в поле значений этого события имя обработчика btn_Click
  • В созданный оболочкой общий обработчик для всех выделенных кнопок введите следующий код
// Один обработчик для всех кнопок-операций
    private void btn_Click(object sender, System.EventArgs e)
    {
      // Копируем текстовые поля в локальные переменные
      string strFirst = string.Copy(txtFirst.Text);
      string strSecond = string.Copy(txtSecond.Text);
  
      // Замена в строке точки символом запятой
      // для корректного преобразования в число
      int pos = strFirst.IndexOf('.');
      if(pos != -1)
      {
        strFirst = strFirst.Substring(0, pos)
          + ','
          + strFirst.Substring(pos + 1);
      }
      pos = strSecond.IndexOf('.');
      if(pos != -1)
      {
        strSecond = strSecond.Substring(0, pos)
          + ','
          + strSecond.Substring(pos + 1);
      }
  
      // Преобразуем текст в число для выполнения операций
      if(txtFirst.Text.Length > 0)
        numFirst = Convert.ToDouble(strFirst);
      else 
        numFirst = 0.0D;
      if(txtSecond.Text.Length > 0)
        numSecond = Convert.ToDouble(strSecond);
      else 
        numSecond = 0.0D;
  
      // Выполняем нужную операцию
      string btnText = "";// Создали строковую переменную
      bool divideFlag = false;// Флаг деления на ноль
      Button btn = (Button) sender;// Явное приведение типов для распознавания кнопок
      switch(btn.Name)// Переключатель
      {
        case "btnIncrement":// Операция сложения
          btnText = "\"+\"";// Экраны кавычек
          numResult = numFirst + numSecond;
          break;
        case "btnDecrement":// Операция вычитания
          btnText = "\"-\"";// Экраны кавычек
          numResult = numFirst - numSecond;
          break;
        case "btnIncrease":// Операция умножения
          btnText = "\"*\"";// Экраны кавычек
          numResult = numFirst * numSecond;
          break;
        case "btnDivide":// Операция деления
          btnText = "\":\"";// Экраны кавычек
          // Проверяем корректность деления
          if (Math.Abs(numSecond) < 1.0E-30)
          {
            MessageBox.Show(
              "Делить на ноль нельзя!", // Сообщение
              "Ошибка", // Заголовок окна
              MessageBoxButtons.OK, // Кнопка OK
              MessageBoxIcon.Stop);// Критическая иконка
            divideFlag = true;
          }
          else
            numResult = numFirst / numSecond;
          break;
      }
  
      // Для отображения в панели Output режима Debug
      System.Diagnostics.Debug.WriteLine("Нажата кнопка " + btnText);// Конкатенация
  
      // Отображение результата
      if(!divideFlag)
      {
        txtResult.Text = Convert.ToString(numResult);
        this.Validate(); // Обновить экран (можно убрать-излишне)
      }
    }
Листинг 25.15. Обработчик нажатия кнопок-операций btn_Click()

Здесь мы прежде всего скопировали значения полей ввода в промежуточные текстовые переменные, используя статический метод System.String. Copy() класса String (псевдоним string ). Затем мы проверяем наличие символа "точки" в этих переменных и при обнаружении производим замену на символ "запятая" с полным формированием новой строки. Мы не можем заменить символ "на месте" в существующей строке, обратившись к нему как элементу массива, поскольку индексная форма адресации в строках существует только для чтения.

Но мы можем, сформировав новую строку справа от знака присваивания, присвоить результат вновь той же самой ссылке, которая фактически будет адресоваться к новой области памяти, а старая строка, как брошенный фрагмент памяти, будет оставлена для сборщика мусора. Операция замены точки на запятую выполняется для того, чтобы корректно преобразовать текстовое значение поля ввода в числовое, иначе генерируется исключение.

Далее мы явно приводим переданную в обработчик ссылку типа object на нажатую кнопку к типу Button. В заголовке переключателя выделяем имя нажатой кнопки и выполняем соответствующую арифметическую операцию. Перед выполнением последней операции мы проверяем возможность возникновения случая " деления на ноль " и при его обнаружении предупреждаем пользователя выводом простого диалогового окна сообщений, отменяя саму операцию деления.

После переключателя мы располагаем оператор вывода строки System.Diagnostics.Debug.WriteLine() с выполненной операцией в панель оболочки Output. Этот класс предназначен для работы с программой в отладочном режиме Debug и автоматически изымается из кода при компиляции окончательного варианта приложения в режиме Release.

На последнем этапе, в коде обработчика, мы выводим результат в соответствующее текстовое поле с предварительным преобразованием типов. Затем мы даем команду системе обновить окно приложения, хотя в данном случае эта мера является излишней - система и так обновляет результат в текстовом поле.

Теперь создадим обработчик нажатия на кнопку закрытия формы.

  • В режиме Design двойным щелчком на кнопке btnExit создайте обработчик, который заполните так
private void btnExit_Click(object sender, System.EventArgs e)
    {
      this.Close();
    }
Листинг 25.16. Код закрытия формы калькулятора
  • Запустите проект и проверьте функциональность простого калькулятора

Теперь мы должны подключить разработанную форму калькулятора к основному окну приложения.

  • Установите в качестве стартовой форму Start. Для этого выполните команду меню оболочки Project/WinFormsApp Properties и в появившемся диалоговом окне установите для вкладки Application свойство Startup Object в значение WinFormsApp.Program
  • Откройте файл Start.cs в режиме View Code. Через раскрывающийся список Members в верхней части окна редактора кода найдите обработчик события generalList_SelectedIndexChanged(), который мы создали ранее, и дополните его вызовом окна калькулятора
private void generalList_SelectedIndexChanged(object sender, System.EventArgs e)
    {
      switch(generalList.SelectedIndex + 1)
      {
        case 1:
          Smiles  frm1 = new Smiles();
          frm1.ShowDialog();
          break;  
        case 2:
          Calculate frm2 = new Calculate();
          frm2.ShowDialog();
          break;
      }
    }
Листинг 25.17. Код вызова окна калькулятора в модальном режиме
  • В панели Solution Explorer оболочки щелкните дважды на пиктограмме файла Start.cs, чтобы открыть его в режиме View Designer, выделите элемент списка в клиентской области формы и через панель Properties добавьте в свойство Items строку " 2) Простой калькулятор "
  • Запустите приложение и проверьте его работоспособность