C# для профессионалов. Том II - Симон Робинсон
Шрифт:
Интервал:
Закладка:
Мы вернемся к рассмотрению этого вопроса после изучения прокрутки экрана, когда речь пойдет об использовании трех различных координатных систем: мировых, страницы и устройства.
Size и SizeF
Подобно Point и PointF размеры выступают в двух вариантах. Структура Size предназначена для работы с целыми значениями, SizeF — для значений с плавающей точкой. В остальном Size и SizeF идентичны. Мы сосредоточимся здесь на структуре Size.
Во многом Size аналогична структуре Point. Она имеет два целых свойства, которые представляют горизонтальное и вертикальное расстояния, основное различие состоит в том, что вместо X и Y эти свойства называются Width и Height. Можно представить предыдущую диаграмму с помощью кода:
Size АВ = new Size(20, 10);
Console.WriteLine("Moved {0} across, {1} down", AB.Width, AB.Height);
Строго говоря структура Size математически представляет то же, что и Point, но концептуально она предназначена для использования немного другим образом. Point применяется, если говорится о местоположении объекта, a Size — когда речь идет о размере чего-то.
В качестве примера рассмотрим нарисованный ранее прямоугольник с координатой вверху слева (0, 0) и размером (50, 50):
Graphics dc. = е.Graphics;
Pen BluePen = new Pen(Color.Blue, 3);
dc.Rectangle(BluePen, 0, 0, 50, 50);
Размер этого прямоугольника равен (50, 50) и может быть представлен экземпляром Size. Нижний правый угол также находится в точке (50, 50), но будет представляться экземпляром Point. Чтобы увидеть различия, предположим, что мы рисуем прямоугольник в другом месте, так что его верхняя левая координата будет (10, 10).
dc.DrawRectangle(BluePen, 10, 10, 50, 50);
Теперь нижний правый угол имеет координаты (60, 60), но размер не изменился — по-прежнему (50, 50).
Дополнительный оператор был перезагружен для точек и размеров так, чтобы можно было добавлять размер к точке задавал другую точку:
static void Main(string [] args) {
Point TopLeft = new Point (10, 10);
Size RectangleSize = new Size(50, 50);
Point BottomRight = TopLeft + RectangleSize;
Console.WriteLine("TopLeft = " + TopLeft);
Console.WriteLine("BottomRight = " + BottomRight);
Console.WriteLine("Size = " + RectangleSize);
}
Этот код, выполняемый как простое консольное приложение, создает следующий вывод:
Отметим, что этот вывод показывает также, как метод ToString() объектов Point и Size был переопределен для вывода значения в формате {X, Y}.
Аналогично можно вычесть Size из Point, чтобы задать Point, или складывать два размера Size, задавая другой размер Size. Однако невозможно сложить точку Point с другой точкой Point. Компания Microsoft определила, что такое действие не имеет концептуального смысла, поэтому было решено не создавать никакою перезагружаемого оператора + который бы позволял это сделать.
Можно также явно преобразовать Point в Size и наоборот:
Point TopLeft = new Point(10, 10);
Size S1 = (Size)TopLeft;
Point P1 = (Point)S1;
При этом преобразовании значению S1.Width присваивается значение TopLeft.X, а S1.Height — TopLeft.Y. Следовательно, S1 содержит (10, 10). P1 будет содержать те же значения, что и TopLeft.
Rectangle и RectangleF
Эти структуры предcтавляют прямоугольную область (обычно на экране). Так же, как и в случае с Point и Size, мы рассмотрим только структуру Rectangle. RectangleF по сути идентична, за исключением того, что свойства, представляющие размеры, используют float, в то время как в Rectangle использует int.
Rectangle можно рассматривать как точку в верхнем левом углу прямоугольника и Size, которая представляет его размер. Один из его конструкторов действительно получает Point и Size в качестве параметров, Можно увидеть это переписывая предыдущий код рисования прямоугольника
Graphics dc = е Graphics;
Pen BluePen = new Pen(Color Blue, 3);
Point TopLeft = new Point(0, 0);
Size HowBig = new Size(50, 50);
Rectangle RectangleArea = new Rectangle(TopLeft, HowBig);
dc.DrawRectangle(BluePen, RectangleArea);
Этот код также использует альтернативное переопределение Graphics.DrawRectangle(), который получает Pen и структуру Rectangle в качестве своих параметров.
Можно также создать Rectangle, используя значения в таком порядке как отдельные числа: верхняя левая горизонтальная координата, верхняя левая вертикальная координата, отдельно ширина и высота:
Rectangle RectangleArea = new Rectangle(0, 0, 50, 50)
Rectangle имеет достаточно много свойств чтения-записи для задания или извлечения его размеров в различных комбинациях:
Свойство Описание int Left х-координата левого края int Right х-координата правого края int Top у-координата верхнего края int Bottom у-координата нижнего края int X То же самое что и Left int Y То же самое, что и Top int Width Ширина прямоугольника int Height Высота прямоугольника Point Location Верхний левый угол Size Size Размер прямоугольникаОтметим, что эти свойства не все независимы,— например задание Width будет влиять на значение Right.
Region
Мы упомянем здесь о существовании класса System.Drawing.Region, однако не будем рассматривать его подробно в этой книге. Region представляет область на экране, которая имеет некоторую сложную форму. Например, затененная область на рисунке может быть представлена Region:
Процесс инициализации экземпляра Region является сам по себе достаточно сложным. В целом это можно сделать, указывая, какие компоненты простой формы составляют область либо какой маршрут необходимо пройти, чтобы обойти область по границе. Если требуется работать с такими областями, то стоит изучить класс Region.
Замечание об отладке
Теперь мы готовы перейти к более сложным рисункам. Но сначала необходимо поговорить об отладке. Если попробовать задать точки прерывания для примеров в этой главе, то можно заметить, что отладка графических программ не является такой же простой задачей, как отладка других частей программы. Это связано с тем, что сам факт входа и выхода из отладчика часто приводит к отправке приложению сообщений Paint. В результате может оказаться, что задание точки прерывания в методе OnPaint заставляет приложение просто воспроизводить себя непрерывно, и поэтому оно не может делать ничего другого.
Типичный сценарий будет таков: необходимо определить, почему приложение что-то выводит неправильно, поэтому в OnPaint задается точка прерывания. Как ожидается, приложение доходит до точки прерывания и вызывает отладчик, в результате появляется окно среды разработки MDI.
В этом окне можно проверить значения некоторых переменных и даже найти что-нибудь полезное. Для продолжения работы нажмите клавишу F5, чтобы можно было увидеть, что происходит, когда приложение выводит что-то еще после выполнения некоторой обработки. К сожалению, в этот момент окно приложения выходит на передний план и Windows обнаруживает, что форма снова видна и немедленно посылает событие Paint. Это означает, конечно, что точка прерывания тут же сработает снова. Но обычно требуется, чтобы точка прерывания сработала позже, когда приложение нарисует что-то интересное, возможно, после выбора некоторых пунктов меню для чтения из файла или другого способа изменения изображения. Похоже на тупик. Либо не нужно вообще создавать точку прерывания в OnPaint, либо приложение никогда не сможет выйти за точку, где выводится его начальное окно.
Однако существуют способы обхода этой проблемы.
Если имеется достаточно большой экран, то простейшим способом является сохранение окна среды разработчика открытым, но так, чтобы оно не закрывало окно приложения. К сожалению, в большинстве случаев это не очень практичное решение, так как окно среды разработки будет слишком маленьким. Альтернативное решение, которое использует тот же самый принцип, состоит в том, что приложение должно объявить себя самым верхним приложением во время отладки. Это делается заданием свойства TopMost класса Form, что можно легко осуществить в методе InitializeComponent: