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

Отображение набираемых символов на экране

А теперь рассмотрим программу, которая позволяет вводить и редактировать текст при помощи класса Caret. По функциональности она очень близка к элементу управления TextBox, работающему в режиме обработки одной строки.

TypeAway.cs

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

  class TypeAway: Form
  {
      public static void Main()
      {
          Application.Run(new TypeAway());
      }

      protected Caret  caret;
      protected string strText = "";
      protected int    iInsert = 0;

      public TypeAway()
      {
          Text = "Type Away";
          BackColor = SystemColors.Window;
          ForeColor = SystemColors.WindowText;
          
          FontHeight = 24;

          caret = new Caret(this);
          caret.Size = new Size(2, Font.Height);
          caret.Position = new Point(0, 0);
      }

      protected override void OnKeyPress(KeyPressEventArgs kpea)
      {
          caret.Hide();
          Graphics grfx = CreateGraphics();
          grfx.FillRectangle(new SolidBrush(BackColor), 
                             new RectangleF(Point.Empty, 
                             grfx.MeasureString(strText, Font,
                             Point.Empty, StringFormat.GenericTypographic)));

          switch(kpea.KeyChar)
          {
          case '\b':
               if (iInsert > 0)
               {
                   strText = strText.Substring(0, iInsert - 1) +
                                               strText.Substring(iInsert);
                   iInsert -= 1;
               }
               break;

          case '\r':
          case '\n':
               break;

          default:
               if (iInsert == strText.Length)
                   strText += kpea.KeyChar;
               else
                   strText = strText.Substring(0, iInsert) +
                             kpea.KeyChar +
                             strText.Substring(iInsert);
               iInsert++;
               break;
          }
          grfx.TextRenderingHint = TextRenderingHint.AntiAlias;
          grfx.DrawString(strText, Font, new SolidBrush(ForeColor), 
                          0, 0, StringFormat.GenericTypographic);
          grfx.Dispose();

          PositionCaret();
          caret.Show();
      }

      protected override void OnKeyDown(KeyEventArgs kea)
      {
          switch (kea.KeyData)
          {
          case Keys.Left:
               if (iInsert > 0)
                   iInsert--;
               break;

          case Keys.Right:
               if (iInsert < strText.Length)
                   iInsert++;
               break;

          case Keys.Home:
               iInsert = 0;
               break;

          case Keys.End:
               iInsert = strText.Length;
               break;

          case Keys.Delete:
               if (iInsert < strText.Length)
               {
                   iInsert++;
                   OnKeyPress(new KeyPressEventArgs('\b'));
               }
               break;

          default:
               return;
          }
          PositionCaret();
      }

      protected void PositionCaret()
      {
          Graphics grfx = CreateGraphics();
          string str = strText.Substring(0, iInsert);
          StringFormat strfmt = StringFormat.GenericTypographic;
          strfmt.FormatFlags |= StringFormatFlags.MeasureTrailingSpaces;
          SizeF sizef = grfx.MeasureString(str, Font, Point.Empty, strfmt);
          caret.Position = new Point((int)sizef.Width, 0);
          grfx.Dispose();
      }

      protected override void OnPaint(PaintEventArgs pea)
      {
          Graphics grfx = pea.Graphics;
          grfx.TextRenderingHint = TextRenderingHint.AntiAlias;
          grfx.DrawString(strText, Font, new SolidBrush(ForeColor), 
                          0, 0, StringFormat.GenericTypographic);
      }
  }

Класс TypeAway создает объект типа Caret в своем конструкторе и инициализирует его размер и положение. При рисовании на форме программа должна лишь спрятать каретку до события Paint и вновь показать ее после этого события, а также установить позицию каретки в клиентской области.

Вводимая и редактируемая пользователем строка символов хранится в поле strText. Поле iInsert хранит точку вставки в эту строку. Например, после набора трех символов значение iInsert равно 3. Если затем нажать стрелку влево, это значение станет равным 2. Метод PositionCaret программы TypeAway отвечает за преобразование позиции символа в позицию пикселов. Результат этого преобразования служит для установки свойства Position объекта Caret.

Посмотрим, как TypeAway обрабатывает метод OnKeyPress. Кажется, в большинстве случаев программе достаточно просто вывести на форму новый символ, позиция пикселов которого соответствует текущей точке вставки, и добавить этот символ к значению поля strText. Но вместо этого программа стирает текст формы при помощи методов MeasureString и FillRectangle! Возможно, это выглядит как крайность, но это необходимо, если точка вставки находится не в конце строки или при выводе текста на языке, отличном от английского (в этом мы еще убедимся).

Метод OnKeyPress обрабатывает нажатие клавиши Backspace, удаляя из строки символ, расположенный перед точкой вставки. Этот метод игнорирует управляющие символы возврата каретки и перевода строки, и помещает все остальные символы в строку в точке вставки. После он выводит всю строку целиком и вызывает метод PositionCaret (я опишу его чуть ниже). Заметьте, что метод прячет каретку во время рисования на форме.

Метод OnKeyDown обрабатывает лишь клавиши управления курсором, перемещая точку вставки, и клавишу Delete, имитируя действие Backspace. Он тоже вызывает PositionCaret.

Метод PositionCaret отвечает за преобразование позиции точки вставки (iInsert) в координаты пикселов каретки. Он делает это при помощи MeasureString. К сожалению, версия MeasureString по умолчанию не обеспечивает точности, нужной для приложений такого рода, но хуже всего то, что при расчете длины строки данная версия MeasureString обычно не учитывает концевые пробелы. Чтобы выйти из ситуации, программа использует версию MeasureString с аргументом StringFormat и заносит в свойство FormatFlag аргумента StringFormat значение перечисления StringFormatFlags.MeasureTrattingSpaces. Без этих изменений каретка двигалась бы при наборе букв слова, но оставалась бы на месте при вводе пробела, разделяющего слова.

Но одних этих мер недостаточно, чтобы точно выровнять каретку и выводимый текст. В силу причин, обсуждаемых в главе 9, в методах MeasureString и DrawString имеются встроенные средства компенсации искажений, связанных с независимой от устройства растеризацией контурных шрифтов. Чтобы избежать применения этих средств, что происходит по умолчанию, программа использует объект StringFormat, получаемый из статического свойства StringFormat.GenericTypographic. Кроме того, в программе применяется сглаживание выводимого текста при помощи свойства TextRenderingHint объекта Graphics (полностью это решение освещается в главе 9).


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

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