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

Произвольные координаты

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

Иногда требуется жестко запрограммировать набор координат и избежать при этом их масштабирования в явном виде. Допустим, нужно запрограммировать вывод графики в некоторой системе координат, скажем, размером 1 000 × 1 000 единиц по горизонтали и вертикали. Это пространство координат нужно сделать как можно большим, но не больше клиентской области или страницы принтера.

Вот как это делается:

ArbitraryCoordinates.cs

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

  class ArbitraryCoordinates: PrintableForm
  {
      public new static void Main()
      {
          Application.Run(new ArbitraryCoordinates());
      }
      public ArbitraryCoordinates()
      {
          Text = "Arbitrary Coordinates";
      }
      protected override void DoPage(Graphics grfx, Color clr, int cx, int cy)
      {
          grfx.PageUnit = GraphicsUnit.Pixel;
          SizeF sizef = grfx.VisibleClipBounds.Size;

          grfx.PageUnit  = GraphicsUnit.Inch;
          grfx.PageScale = Math.Min(sizef.Width  / grfx.DpiX / 1000, 
                                    sizef.Height / grfx.DpiY / 1000);

          grfx.DrawEllipse(new Pen(clr), 0, 0, 990, 990);
      }
  }

Сначала метод DoPage устанавливает свойство PageUnit как GraphicsUnit.Pixel с единственной целью — получить значение свойства VisibleClipBounds, указывающее размер клиентской области или страницы принтера в пикселах.

Далее DoPage устанавливает PageUnit для измерений в дюймах:

  grfx.PageUnit = GraphicsUnit.Inch;

Выше я показал, что при этом применяются следующие формулы преобразования:



xустр. = xстр. × PageScale × DpiX

yустр. = yстр. × PageScale × DpiY


Значения xстр. и yстр. должны варьироваться в пределах 0 до 1000, а xустр. и yустр. — от 0 до значений Width и Height свойства VisibleClipBounds соответственно. Иными словами:


Width = 1000 × PageScale × DpiX

Height = 1000 × PageScale × DpiY


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

  grfx.PageScale = Math.Min(sizef.Width  / grfx.DpiX / 1000,
                            sizef.Height / grfx.DpiY / 1000);

Далее программа рисует эллипс шириной и высотой в 990 единиц (использование значений 1 000 или 999 для ширины и высоты иногда приводит к обрезанию фигуры при больших размерах окна). Результирующая фигура — окружность, которая рисуется слева, если ширина клиентской области больше ее высоты, или сверху, если наоборот.


Рис. 7.5.

Окружность можно напечатать, при этом она будет вписана по ширине в область печати страницы.

Но с этой программой есть маленькая проблема. Попробуйте как можно сильнее уменьшить размер окна. Вы увидите, что уменьшать ширину окна можно лишь до некоторого предела, в то время как высоту окна можно уменьшать хоть до 0. В этот момент возникает исключение, поскольку метод DoPage присваивает свойству PageScale недействительное значение 0.

Есть два пути решения этой проблемы. Самый очевидный, вероятно, — просто прервать исполнение метода DoPage при достижении нулевой высоты клиентской области:

  if (су == 0)
      return;

Это вполне нормально, поскольку рисовать что-то в таком окне нет смысла.

Не находите ли вы, что вызывать метод OnPaint при нулевом размере клиентской области, несколько странно? Поэтому не повредит добавить в начало метода OnPaint инструкцию:

  if (pea.ClipRectangle.IsEmpty)
      return;

эквивалентную инструкции:

  if (grfx.IsVisibleClipEmpty)
      return;

Очень специализированное решение — вызвать метод Math.Max для расчета свойства PageScale, чтобы исключить появление значения 0:

  grfx.PageScale = Math.Min(sizef.Width               / grfx.DpiX / 1000,
                            Math.Max(sizef.Height, 1) / grfx.DpiY / 1000);

Или, чтобы показать всем, что вам кое-что известно об обработке исключении в С#, можно поместить проблемный оператор в блок try:

  try
  {
      grfx.PageScale = Math.Min(sizef.Width  / grfx.DpiX / 1000,
                                sizef.Height / grfx.DpiY / 1000);
  {
  catch
  {
      return;
  }

Но есть и не столь очевидный метод, суть которого — в предотвращении уменьшения размера клиентской области до 0. Статическое свойство SystemInformation.MmimumWindowSize возвращает размер, равный сумме высоты заголовка и удвоенной толщины границы окна. Чтобы при минимальной ширине окна была видна часть его заголовка, ширина окна должна быть гораздо больше минимальной.

Можно использовать свойство MinimumSize, чтобы поддерживать размер окна не меньше заданного. Попробуйте поместить в конструктор ArbitraryCoordinates следующее:

  MinimumSize = SystemInformation.MinimumWindowSize + new Size(0, 1);

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

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