netlib.narod.ru< Назад | Оглавление | Далее >

Шестнадцатеричный калькулятор

Ниже приведена программа, создающая 29 элементов управления Button и реализующая шестнадцатеричный калькулятор с инфиксной записью. Программа HexCalc работает с 64-битными целыми числами без знака и может выполнять сложение, вычитание, умножение, деление, вычисление остатка, поразрядные И, ИЛИ, исключающее ИЛИ, а также поразрядный сдвиг. Вот как выглядит ее окно:


Рис. 12.15.

Работать с HexCalc можно с помощью мыши либо клавиатуры. Первым идет операнд (он вводится с помощью клавиатуры или кнопок), затем операция и второй операнд. Чтобы посмотреть результаты, надо щелкнуть по кнопке Equals либо нажать клавиши «=» или Enter. Изменять введенные данные можно, щелкнув по кнопке Back либо нажав клавишу Backspace. Поле ввода ввода можно очистить, щелкнув по нему или нажав клавишу Esc.

В этой программе я не прибегал к Visual C# .NET Windows Forms Designer. Огромное количество кнопок с одинаковыми координатами и размерами прямо-таки требует более методического подхода. После пары безуспешных попыток я решил создать класс CalcButton, являющийся подклассом Button, и реализовал в нем конструктор, в качестве аргументов принимающий родительский элемент кнопки, ее текст, местоположение и размер. Я также добавил в него поле cbKey, содержащее клавиатурный символ, связанный с этой кнопкой. Конструктор HexCalc содержит 29 операторов new CalcButton, создающих все кнопки. Для кнопок применяются традиционная система координат диалогового окна, но при этом напрямую вызывается короткая версия Scale, одинаково изменяющая масштаб во всех направлениях. Это позволяет сохранить большинство кнопок квадратными.

HexCalc.cs

  //----------------------------------------
  // HexCalc.cs (C) 2001 by Charles Petzold
  //----------------------------------------
  using System;
  using System.Drawing;
  using System.Windows.Forms;

  class HexCalc: Form
  {
      Button btnResult;
      ulong  ulNum       = 0;
      ulong  ulFirstNum  = 0;     
      bool   bNewNumber  = true;
      char   chOperation = '=';

      public static void Main()
      {
          Application.Run(new HexCalc());
      }
      public HexCalc()
      {
          Text = "Hex Calc";
          Icon = new Icon(GetType(), "HexCalc.HexCalc.ico");
          FormBorderStyle = FormBorderStyle.FixedSingle;
          MaximizeBox = false;

          new CalcButton(this, "D",       'D',  8,  24, 14, 14);
          new CalcButton(this, "A",       'A',  8,  40, 14, 14);
          new CalcButton(this, "7",       '7',  8,  56, 14, 14);
          new CalcButton(this, "4",       '4',  8,  72, 14, 14);
          new CalcButton(this, "1",       '1',  8,  88, 14, 14);
          new CalcButton(this, "0",       '0',  8, 104, 14, 14);
          new CalcButton(this, "E",       'E', 26,  24, 14, 14);
          new CalcButton(this, "B",       'B', 26,  40, 14, 14);
          new CalcButton(this, "8",       '8', 26,  56, 14, 14);
          new CalcButton(this, "5",       '5', 26,  72, 14, 14);
          new CalcButton(this, "2",       '2', 26,  88, 14, 14);
          new CalcButton(this, "Back", '\x08', 26, 104, 32, 14);
          new CalcButton(this, "C",       'C', 44,  40, 14, 14);
          new CalcButton(this, "F",       'F', 44,  24, 14, 14);
          new CalcButton(this, "9",       '9', 44,  56, 14, 14);
          new CalcButton(this, "6",       '6', 44,  72, 14, 14);
          new CalcButton(this, "3",       '3', 44,  88, 14, 14);
          new CalcButton(this, "+",       '+', 62,  24, 14, 14);
          new CalcButton(this, "-",       '-', 62,  40, 14, 14);
          new CalcButton(this, "*",       '*', 62,  56, 14, 14);
          new CalcButton(this, "/",       '/', 62,  72, 14, 14);
          new CalcButton(this, "%",       '%', 62,  88, 14, 14);
          new CalcButton(this, "Equals",  '=', 62, 104, 32, 14);
          new CalcButton(this, "&&",      '&', 80,  24, 14, 14);
          new CalcButton(this, "|",       '|', 80,  40, 14, 14);
          new CalcButton(this, "^",       '^', 80,  56, 14, 14);
          new CalcButton(this, "<",       '<', 80,  72, 14, 14);
          new CalcButton(this, ">",       '>', 80,  88, 14, 14);

          btnResult = 
               new CalcButton(this, "0", '\x1B', 8, 4, 86, 14);

          foreach (Button btn in Controls)
              btn.Click += new EventHandler(ButtonOnClick);

          ClientSize = new Size(102, 126);
          Scale(Font.Height / 8f);
      }
      protected override void OnKeyPress(KeyPressEventArgs kpea)
      {
          char chKey = Char.ToUpper(kpea.KeyChar);

          if (chKey == '\x0D')
              chKey = '=';

          for (int i = 0; i < Controls.Count; i++)
          {
              CalcButton btn = (CalcButton) Controls[i];

              if (chKey == btn.chKey)
              {
                  InvokeOnClick(btn, EventArgs.Empty);
                  break;
              }
          }
      }
      void ButtonOnClick(object obj, EventArgs ea)
      {
          CalcButton btn = (CalcButton) obj;

          if (btn.chKey == '\x08')
              ulNum /= 16;

          else if (btn.chKey == '\x1B')
              ulNum = 0;

          else if (Char.IsLetterOrDigit(btn.chKey))    // Цифра
          {
              if (bNewNumber)
              {
                  ulFirstNum = ulNum;
                  ulNum = 0;
                  bNewNumber = false;
              }

              if (ulNum <= ulong.MaxValue >> 4)
                  ulNum = 16 * ulNum + 
                          (ulong)(btn.chKey -
                          (Char.IsDigit(btn.chKey) ? '0' : 'A' - 10));
          }
          else                                         // Операция
          {
              if(!bNewNumber)
              {
                  switch(chOperation)
                  {
                  case '=': ulNum = ulNum; break;
                  case '+': ulNum = ulFirstNum + ulNum; break;
                  case '-': ulNum = ulFirstNum - ulNum; break;
                  case '*': ulNum = ulFirstNum * ulNum; break;
                  case '&': ulNum = ulFirstNum & ulNum; break;
                  case '|': ulNum = ulFirstNum | ulNum; break;
                  case '^': ulNum = ulFirstNum ^ ulNum; break;
                  case '<': ulNum = ulFirstNum << (int)ulNum; break;
                  case '>': ulNum = ulFirstNum >> (int)ulNum; break;
                  case '/': ulNum = ulNum != 0 ? 
                                    ulFirstNum / ulNum : ulong.MaxValue; 
                            break;
                  case '%': ulNum = ulNum != 0 ? 
                                    ulFirstNum % ulNum : ulong.MaxValue; 
                            break;
                  default:  ulNum = 0; break;
                  }
              }
              bNewNumber = true;
              chOperation = btn.chKey;
          }
          btnResult.Text = String.Format("{0:X}", ulNum);
      }
  }
  class CalcButton: Button
  {
      public char chKey;

      public CalcButton(Control parent, string str, char chkey, 
                        int x, int y, int cx, int cy)
      {
          Parent   = parent;
          Text     = str;
          chKey    = chkey;
          Location = new Point(x, y);
          Size     = new Size(cx, cy);
          SetStyle(ControlStyles.Selectable, false);
      }
  }

Рис. 12.16.

В HexCalc довольно хитро реализован клавиатурный интерфейс. Мне не хотелось, чтобы кнопки получали фокус ввода. Штриховая линия вокруг кнопки, обозначающая, что кнопка имеет фокус ввода, смотрелась не очень хорошо. Кроме того, перемещать фокус ввода между кнопками посредством клавиши Tab, было бы бессмысленно. И наконец, клавиш, которые воспринимаются этой программой, больше, чем кнопок.

Чтобы переправить клавиатурные события форме, у каждой кнопки стиль Selectable установлен в false, благодаря чему кнопки не получают фокус ввода.

Метод OnKeyPress проходит по всем элементам массива Controls и находит кнопку, соответствующую нажатой клавише. Затем вызывается метод InvokeOnClick, имитируя событие кнопки Click. Таким образом, метод ButtonOnClick обрабатывает щелчки по кнопкам и нажатия их клавиатурных эквивалентов.


netlib.narod.ru< Назад | Оглавление | Далее >

Сайт управляется системой uCoz