ЯЗЫК ПРОГРАММИРОВАНИЯ С# 2005 И ПЛАТФОРМА .NET 2.0. 3-е издание - Эндрю Троелсен
Шрифт:
Интервал:
Закладка:
Однако, заметьте, Dispose() не вызывается явно для объекта, который был получен из поступающего на вход PaintEventArgs. Причина в том, что вы не создавали этот объект непосредственно и поэтому не можете гарантировать, что другие части программы не используют его. Очевидно, что при освобождении объекта, используемого в другом месте программы, могут возникать проблемы.
В связи с этим заметим, что если вы забудете вызвать метод Dispose() для объекта, реализующего IDisposable, внутренние ресурсы будут освобождены позже, при обработке объекта сборщиком мусора (см. главу 5). В этом смысле освобождение объекта imgGraphics, строго говоря, не является необходимым. Так что, хотя явное освобождение объектов GDI+, созданных вами непосредственно, делает программный код совершеннее, мы с вами, чтобы сделать примеры программного кода в этой главе более краткими, не будем освобождать каждый тип GDI+ вручную.
Системы координат GDI+
Нашей следующей задачей будет рассмотрение координатных систем GDI+. В GDI+ определяются три разные системы координат, которые используются средой выполнения, при определении места размещения и размеров содержимого визуализации. Во-первых, есть так называемые мировые координаты (или внешние координаты). Мировые координаты представляют абстракцию размеров данного типа GDI+, независимую от единиц измерения. Например, при прорисовке прямоугольника с указанием для размерности (0, 0, 100, 100), вы на самом деле указываете прямоугольник размером 100×100 "единиц". Как вы можете догадаться, по умолчанию "единица" – это пиксель, однако ей можно назначить и другую единицу измерения (дюйм, сантиметр и т.п.),
Далее, есть страничные координаты (координаты страницы). Страничные координаты представляют смещение в применении к оригинальным мировым координатам. Это удобно тогда, когда вы не хотите вычислять смещение в своем программном коде вручную (вы не обязаны это делать). Например, если у вас есть форма, которая должна оставаться в границах 100×100 пикселей, вы можете указать страничную координату (100*100), чтобы визуализация выполнялась относительно точки (100*100). Тогда в своем базовом коде вы сможете просто указать мировые координаты (избежав, тем самым, необходимости вручную учитывать смещение),
Наконец, есть приборные координаты (координаты устройства). Приборные координаты представляют результат применения страничных координат к оригинальным мировым координатам. Эта координатная система используется для определения того, где именно будет показан соответствующий тип GDI+. При программировании с помощью средств GDI+ программист обычно мыслит в терминах мировых координат, которые являются базой для определения размеров и места размещения типа GDI+. Для визуализации в мировых координатах не требуется никаких специальных программных ухищрений – нужно просто передать значения измерений текущей операции визуализации,
void MainForm_Paint(object sender, PaintEventArgs s) {
// Визуализация прямоугольника в мировых координатах.
i.Graphics g = е.Graphics;
g.DrawRectangle(Pens.Black, 10, 10, 100, 100);
}
"За кулисами" ваши мировые координаты автоматически отображаются в координаты страницы, которые затем отображаются в приборные координаты. Во многих случаях вы вообще не будете использовать координаты страницы и приборные координаты непосредственно, если только не захотите применить определенные графические трансформации. Поскольку в предыдущем программном коде не используется никакой программной логики трансформаций, мировые, страничные и приборные координаты оказываются идентичными.
Если перед визуализацией своей программной логики GDI+ вы хотите применить какие-то преобразования, вы должны использовать подходящие члены типа Graphics (например, метод TranslateTransform()), чтобы перед тем, как выполнить визуализацию, указать "страничные координаты" в существующей системе мировых координат. В результате устанавливаются приборные координаты, которые будут использоваться при выводе типа GDI+ на соответствующее устройство.
private void MainForm_Paint(object sender, PaintEventArgs e) {
// Указание смещения (10 * 10) для страничных координат.
Graphics g = е.Graphics;
g.TranslateTransform(10, 10);
g.DrawRectangle(10, 10, 100, 100);
}
В данном случае при отображении прямоугольника его левый верхний угол фактически будет помещен в точку (20, 20), поскольку к мировой системе координат будет добавлено смещение в результате вызова TranslateTransform().
Единица измерения, предлагаемая по умолчанию
В GDI+ единицей измерения по умолчанию является пиксель. Начало координат размещается в левом верхнем углу с увеличением оси абсцисс вправо, а оси ординат – вниз (рис. 20.2).
Рис. 20.2. Система координат GDI+, предлагаемая по умолчанию
Поэтому, если вы отобразите Rectangle с использованием пера толщиной в 5 пикселей и красного цвета, как показано ниже.
void MainForm_Paint (object sender, PaintEventArgs e) {
// Установка мировых координат с использованием единиц измерения,
// предлагаемых по умолчанию.
Graphics g = е.Graphics;
g.DrawRectangle(newPen(Color.Red, 5), 10, 10, 100, 100);
}
вы должны увидеть квадрат, смещенный на 10 пикселей вниз и вправо относительно верхнего и левого края клиентской области формы, как показано на рис. 20.3.
Рис. 20.3. Визуализация в пиксельных единицах
Выбор альтернативной единицы измерения
Если вы не хотите выполнять визуализацию изображений с использованием пиксельных единиц измерения, вы имеете возможность изменить эту принятую по умолчанию установку с помощью свойства PageUnit объекта Graphics. Свойству PageUnit можно присвоить любое значение из перечня GraphicsUnit.
public enum GraphicsUnit {
// Мировые координаты.
World,
// Пиксель для видеодисплея и 1/100 дюйма для принтера.
Display,
// Пиксель.
Pixel,
// Стандартная точка принтера (1/72 дюйма).
Point,
// Дюйм.
Inch,
// Стандартная единица документа (1/300 дюйма).
Document,
// Миллиметр.
Millimeter
}
Чтобы проверить, как изменяется базовая единица измерения, модифицируйте имеющийся программный код так, как предлагается ниже.
private void MainForm_Paint(object sender, PaintEventArgs e) {
// Отображение прямоугольника а дюймах, а не в пикселях…
Graphics g = e.Graphics;
g.PageUnit = GraphicsUnit.Inch;
g.DrawRectangle(new Pen(Color.Red, 5), 0, 0, 100, 100);
}
Вы должны увидеть совершенно другой прямоугольник, как показано на рис. 20.4.
Рис. 20.4. Визуализация в дюймах
Причина того, что здесь более 90% области клиента формы занято темным (красным) цветом, заключается в указании пера "шириной" в 5 дюймов! Сам прямоугольник теперь имеет размеры 100×100 дюймов, и тот маленький светлый прямоугольник, который вы видите на рисунке в правом нижнем углу, является левым верхним углом большого внутреннего прямоугольника.
Изменение начала координат
Напомним, что при использовании координат и единиц измерения, предлагаемых по умолчанию, точка (0, 0) находится в левом верхнем углу соответствующей области. Часто это и является именно тем, что требуется, но что делать, если вам нужно поменять точку, относительно которой выполняется визуализация? Предположим, например, что ваше приложение всегда должно (по какой-то причине) оставлять пустой полосу шириной в 100 пикселей вдоль границы области клиента формы. Тогда вы должны гарантировать, чтобы все операции GDI+ выполнялись в соответствующих пределах внутренней области,
Один из подходов, который можно при этом использовать, заключается в добавлении смещения вручную. Конечно, утомительно добавлять значения смещениях каждой операции визуализации. Значительно удобнее (и проще) было бы использовать свойство, которое, по сути, говорило бы следующее: "Хотя я даю указание отобразить прямоугольник с началом координат в точке (0, 0), вы должны использовать для начала координат точку (100, 100)". Это должно сильно "упростить вам жизнь", поскольку вы сможете указать параметры размещения без модификаций,
В рамках GDI+ вы можете указать точку начала координат, установив значение трансформации с помощью метода TranslateTransform() (класса Graphics), позволяющего указать страничные координаты, которые будут применяться к вашим оригинальным мировым координатам, например:
void MainForm_Paint(object sender, PaintEventArgs e) {