netlib.narod.ru | < Назад | Оглавление | Далее > |
Чтобы избежать написания таких методов, как MMConv, в GDI+ включен механизм автоматического масштабирования до заданного размера. В сущности этот механизм масштабирует координаты, передаваемые функциям рисования объекта Graphics, при помощи констант, как метод MMConv. Но при этом коэффициенты масштабирования задают не напрямую, а через свойства PageUnit и PageScale:
Свойства Graphics (выборочно)
Тип | Свойство | Доступ |
GraphicsUnit | PageUnit | Чтение/запись |
float | PageScale | Чтение/запись |
В свойство PageUnit заносится одно из значений перечисления GraphicsUnit:
Перечисление GraphicsUnit
Член | Значение | Описание |
World | 0 | Не используется со свойством PageUnit |
Display | 1 | Для дисплея то же, что Pixel; для принтера равно 1/100 дюйма (задано по умолчанию для принтера). |
Pixel | 2 | Единицы измерения — пикселы (задано по умолчанию для дисплея) |
Point | 3 | Единицы измерения — пункты (1/72 дюйма) |
Inch | 4 | Единицы измерения — дюймы |
Document | 5 | Единицы измерения — 1/300 дюйма |
Millimeter | 6 | Единицы измерения — миллиметры |
Например, если вы хотите рисовать в координатах, исчисляемых сотыми долями дюйма, этой паре свойств надо задать такие значения:
grfx.PageUnit = GraphicsUnit.Inch; grfx.PageScale = 0.01f;
что эквивалентно указанию считать одно деление координатной оси равным 0,01 дюйма. После этого следующий вызов метода DrawLine нарисует линию длиной в 1 дюйм:
grfx,DrawLine(pen, 0, 0, 100, 0);
Если отпечатать такую линию на принтере, вполне можно ее измерить и убедиться, что ее длина действительно равна 1 дюйму; на дисплее этот дюйм равен grfx.DpiX пикселов. Аналогичный результат можно получить так:
grfx.PageUnit = GrapnicsUnit.Document; grfx.PageScale = 3;
или:
grfx.PageUnit = GraphicsUnit.Millimeter; grfx.PageScale = 0.254f;
или:
grfx.PageUnit = GraphicsUnit.Point; grfx.PageScale = 0.72f;
По умолчанию для дисплея задано значение GraphicsUnit.Pixel, а для принтера — GraphicsUnit.Display, для обоих устройств значение PageScale равно 1. Кстати, смысл значения GraphicsUnit.Display для дисплея и для принтера разный. Для дисплея это то же самое, что GraphicsUnit.Pixel, но для принтера оно задает единицы измерения, равные 1/100 дюйма.
Итак, чтобы заставить программу TenCentimeterRuler работать с принтером, достаточно присвоить свойству PageUnit значение GraphicsUnit.Pixel, и все будет хорошо. Новая версия метода OnPage устанавливает свойство PageUnit и вызывает метод DoPage из базового класса.
PrintableTenCentimeterRuler.cs
//------------------------------------------------------------ // PrintableTenCentimeterRuler.cs (C) 2001 by Charles Petzold //------------------------------------------------------------ using System; using System.Drawing; using System.Windows.Forms; class PrintableTenCentimeterRuler: TenCentimeterRuler { public new static void Main() { Application.Run(new PrintableTenCentimeterRuler()); } public PrintableTenCentimeterRuler() { Text = "Printable " + Text; } protected override void DoPage(Graphics grfx, Color clr, int cx, int cy) { grfx.PageUnit = GraphicsUnit.Pixel; base.DoPage(grfx, clr, cx, cy); } }
В этой программе метод DoPage не использует аргументы сх и су. Размеры, определяемые этими аргументами (размеры клиентской области формы и области печати страницы принтера), исчисляются в единицах, определяемых значением PageUnit по умолчанию. В общем случае при изменении значения PageUnit размер страницы устройства вывода тоже должен быть пересчитан в идентичные единицы. Чуть ниже мы обсудим этот вопрос.
Теперь, даже если рисовать на принтере, задавая координаты в пикселах, вид шрифтов не исказится, а у шрифта, доступного через свойство формы Font, будет кегль 8 как на дисплее, так и на принтере. В главе 9 мы увидим, как это работает.
Однако в этой программе остается проблема с пером, определяемым версией метода DoPage, которая используется в программе TenCentimeterRuler:
Pen pen = new Pen(clr);
Толщина этого пера по умолчанию равна 1. Для дисплея это означает 1 пиксел, а для принтера это обычно 1/100 дюйма. Но если изменить значение PageUnit на GraphicsUnit.Pixel, то толщина 1 будет интерпретирована как 1 пиксел. На некоторых принтерах с высоким разрешением такая линия будет почти невидимой.
Чтобы не возиться с исходной версией программы, рисующей 10-сантиметровую линейку, задействуем преимущество свойств PageUnit и PageScale и избавимся от ручного преобразования координат.
TenCentimeterRulerAuto.cs
//------------------------------------------------------- // TenCentimeterRulerAuto.cs (C) 2001 by Charles Petzold //------------------------------------------------------- using System; using System.Drawing; using System.Windows.Forms; class TenCentimeterRulerAuto: PrintableForm { public new static void Main() { Application.Run(new TenCentimeterRulerAuto()); } public TenCentimeterRulerAuto() { Text = "Ten-Centimeter Ruler (Auto)"; } protected override void DoPage(Graphics grfx, Color clr, int cx, int cy) { Pen pen = new Pen(clr, 0.25f); Brush brush = new SolidBrush(clr); const int xOffset = 10; const int yOffset = 10; grfx.PageUnit = GraphicsUnit.Millimeter; grfx.PageScale = 1; grfx.DrawRectangle(pen, xOffset, yOffset, 100, 10); StringFormat strfmt = new StringFormat(); strfmt.Alignment = StringAlignment.Center; for (int i = 1; i < 100; i++) { if (i % 10 == 0) // Сантиметровые метки { grfx.DrawLine(pen, new PointF(xOffset + i, yOffset), new PointF(xOffset + i, yOffset + 5)); grfx.DrawString((i/10).ToString(), Font, brush, new PointF(xOffset + i, yOffset + 5), strfmt); } else if (i % 5 == 0) // Полусантиметровые метки { grfx.DrawLine(pen, new PointF(xOffset + i, yOffset), new PointF(xOffset + i, yOffset + 3)); } else // Миллиметровые метки { grfx.DrawLine(pen, new PointF(xOffset + i, yOffset), new PointF(xOffset + i, yOffset + 2.5f)); } } } }
Кроме удаления метода MMConv, почти ничего не изменилось. Мой метод MMConv работал только со структурами PointF, поэтому в ранних версиях программы для рисования контуров линейки использовался метод DrawPolygon, а не DrawRectangle. Поскольку GDI+ одинаково масштабирует координаты и размеры, здесь можно применить метод DrawRectangle. Другое изменение коснулось метода DoPage. Теперь в начале DoPage программа создает перо толщиной 0,25 единицы:
Pen pen = new Pen(clr, 0.25f);
Программа также настраивает объект Graphics для рисования в миллиметровых координатах:
grfx.PageUnit = Graphics.Unit. Millimeter; grfx.PageScale = 1;
Возможно, вам любопытно знать, есть ли разница между установкой свойств PageUnit и PageScale перед созданием пера и созданием пера с заданной толщиной перед установкой этих свойств. Когда создано перо: до установки этих свойств или после нее — не имеет никакого значения, так как объекты Pen независимы от устройства! Пока не вызван ни один из методов рисования линий, объект Pen не связан ни с одним конкретным объектом Graphics. Лишь после привязки толщина пера интерпретируется в зависимости от текущих значений свойств PageUnit и PageScale. В данном случае толщина пера интерпретируется как 0,25 мм или около 1/100 дюйма. Чтобы увидеть различия на печати, можно попробовать меньшее значение (например, 0,10 мм).
Если толщина пера не включена в его конструктор, создается перо толщиной в 1 единицу. В данном случае это значит, что будет создано перо толщиной ровно 1 мм и все деления сольются в одно большое пятно (убедитесь сами!).
netlib.narod.ru | < Назад | Оглавление | Далее > |