Пользовательский интерфейс Windows Forms для C#
Упражнение 2. Простой калькулятор
Разработаем калькулятор, выполняющий простые действия над целыми и вещественными числами в десятичной форме.
- 
 Поместите
      на форму элементы из вкладки Common Controls   панели Toolbox       в
      соответствии с таблицей, чтобы пользовательский интерфейс
      формы был таким Поместите
      на форму элементы из вкладки Common Controls   панели Toolbox       в
      соответствии с таблицей, чтобы пользовательский интерфейс
      формы был таким
Здесь мы использовали некоторые префиксы при назначении имен элементам управления. Для лучшей узнаваемости в коде программы элементов управления и их типа рекомендуется использовать общеупотребительные префиксы, которые приведены в таблице
static void Main()
    {
      Application.Run(new Calculate());
    }
                            Листинг
                        25.10.
                        Добавление к форме Calculate точки входа         
- 
 Выполните
      команду меню WinFormsApp Properties  и
      для вкладки Application  установите в свойстве Startup
      Object  форму Calculate  стартовой Выполните
      команду меню WinFormsApp Properties  и
      для вкладки Application  установите в свойстве Startup
      Object  форму Calculate  стартовой
- 
 Защитите
        интерфейс формы Calculate  от случайных
        изменений, выполнив команду Format/Lock
        Controls  меню оболочки Защитите
        интерфейс формы Calculate  от случайных
        изменений, выполнив команду Format/Lock
        Controls  меню оболочки
Теперь нужно реализовать функциональность интерфейса. Прежде всего нужно защитить поля ввода от всех символов, кроме цифр, одного знака минус и одной точки. Стрелки и клавиша Del в текстовых полях функционируют, но работу клавиши Backspace нужно учесть явно.
- 
 Объявите
       в конце класса Calculate   логическое
      поле isNumeric  для
      отфильтровывания нецифровых символов, которые пользователь
      может попытаться ввести в текстовые поля Объявите
       в конце класса Calculate   логическое
      поле isNumeric  для
      отфильтровывания нецифровых символов, которые пользователь
      может попытаться ввести в текстовые поля
private bool isNumber = false;Листинг 25.11. Добавим логическую переменную isNumeric
- 
 Выделите
      текстовое поле txtFirst  и через панель Properties  в
      режиме Events  создайте обработчик для события KeyDown.
      Заполните обработчик так Выделите
      текстовое поле 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. Заполните обработчик
      так Выделите
      текстовое поле 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.
      Обработчики подключите через раскрывающиеся списки 
      полей значений Выделите
      текстовое поле txtSecond  и через панель Properties  в
      режиме Events  подключите к событиям KeyDown  и KeyPress  соответствующие
      обработчики, только что созданные для поля txtFirst.
      Обработчики подключите через раскрывающиеся списки 
      полей значений
Здесь мы одни и те же обработчики используем для разных полей, поскольку необходимо выполнять одинаковые действия. Задача одна - не допускать для ввода в поля никакие другие символы, кроме цифр, одного минуса и одной точки. Возможность ввода цифр реализована для основной ( keyboard ) и дополнительной ( keypad ) частей клавиатуры. Параметр sender передает в обработчик ссылку на активное текстовое поле, вызвавшее событие редактирования.
Конечно, программа будет более понятна, если для каждого элемента создавать свой обработчик. Но очень часто, как в нашем случае, преобладающая часть кода может быть совершенно одинакова в каждом обработчике. Тогда имеет смысл объединять несколько индивидуальных обработчиков в один. Какой подход выбрать - зависит от конкретной задачи и предпочтений программиста.
- 
 Добавьте
      в конец класса Calculate  объявления числовых
      переменных, с которыми мы будем производить простые арифметические
      операции Добавьте
      в конец класса Calculate  объявления числовых
      переменных, с которыми мы будем производить простые арифметические
      операции
// Объявляем числовые переменные как поля-члены класса
    // Их можно объявить как локальные и внутри обработчика btn_Click()
    // Но оставляем область видимости класс, вдруг где-нибудь еще пригодятся!
    private double numFirst, numSecond, numResult;
                            Листинг
                        25.14.
                        Добавление числовых переменных-членов класса Calculate 
- 
 Установите
          в панели Properties  режим Events,
          найдите событие Click  и введите вручную       в
    поле значений этого события имя обработчика btn_Click Установите
          в панели 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  создайте
      обработчик, который заполните так В
      режиме 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. Для
      этого выполните команду меню оболочки Project/WinFormsApp
      Properties  и в появившемся диалоговом окне установите
для вкладки Application   свойство Startup
Object  в
значение WinFormsApp.Program
- 
 Откройте
      файл Start.cs  в режиме View Code.
      Через раскрывающийся список Members  в
      верхней части окна редактора кода найдите обработчик события generalList_SelectedIndexChanged(),
      который мы создали ранее, и дополните его вызовом окна
      калькулятора Откройте
      файл 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)
      Простой калькулятор " В
      панели Solution Explorer  оболочки щелкните
      дважды на пиктограмме файла Start.cs,
      чтобы открыть его в режиме View
      Designer,
      выделите элемент списка в клиентской области формы и через
      панель Properties  добавьте в свойство Items  строку
      " 2)
      Простой калькулятор "
- 
 Запустите
      приложение и проверьте его работоспособность Запустите
      приложение и проверьте его работоспособность
 
                             


