Перерисовка картинок, фигур и текста
Вы, наверное, давно уже обратили внимание на такой факт. Если форма с нарисованными фигурами, картинками или написанным текстом скрывается из глаз, закрытая другими окнами, то когда мы вновь ее добываем из-под других окон, то видим, что нарисованные картинки, фигуры и текст стерлись. Если мы часть формы затащили за край экрана, то стирается все, что было нарисовано и написано на этой скрывшейся за край экрана части. Если мы мышью уменьшили размеры формы, а потом снова увеличили, то на поверхности, обнажившейся после увеличения, все стерто. В общем, стирается все то, что пропадает с глаз.
Но если картинка находится на элементе управления в качестве его свойства Image, то она не стирается. Стирается лишь то, что нарисовано.
Чтобы стирания нарисованных вещей не происходило, в Visual Basic 6.0 мы просто в режиме проектирования устанавливали у формы свойство AutoRedraw, и могли больше об этом не думать. Однако VB слишком сложен и мощен для такого «детского» решения.
Посмотрим, что нужно делать в VB.
Событие Paint. В каком случае мы можем заметить, что на форме что-то стерто? Наверное, тогда и только тогда, когда в поле нашего зрения на экране появляется участок формы (пусть даже самый маленький), который был до этого по той или иной причине скрыт (и поэтому с него все было стерто).
Задача VB – создать у нас иллюзию, что ничего не стирается. Для этого путь один – как только такой участок со стертой информацией возникает в поле зрения, тут же его снова зарисовывать «как было», чтобы человек не успевал ничего заметить. Однако, сам VB зарисовывать ничего не собирается, он предоставляет позаботиться об этом программисту. Для чего в помощь ему VB отряжает событие Paint. Это событие наступает как раз тогда, когда в поле зрения на экране появляется участок формы (пусть самый ничтожный), который был до этого скрыт. Если вы, например, медленно вытаскиваете форму из-за края экрана или медленно увеличиваете ее высоту или ширину, то каждую ничтожную долю секунды в поле зрения появляются «стертые» участки, а значит, наступает событие Paint. Получается, что в данном случае событие Paint наступает много раз в секунду.
Раз есть событие, значит есть и процедура-обработчик этого события. Все, что должен сделать программист, это получить в окне кода обычным способом заготовку этой процедуры и записать в нее операторы рисования всех фигур и всего текста на форме, которые он хочет предохранить от стирания. По принципу: если забор в одном месте запачкался – перекрашивай весь забор.
Создайте проект с кнопкой. Введите в окно кода такой текст:
Dim Гр As Graphics = Me.CreateGraphics
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Гр.DrawEllipse(Pens.Black, 20, 20, 80, 100)
End Sub
Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) _
Handles MyBase.Paint
Гр.DrawRectangle(Pens.Black, 10, 10, 200, 100)
End Sub
Пояснения. При нажатии на кнопку рисуется кружок. Чтобы создать заготовку процедуры-обработчика события Paint, выберите, если вы работаете в Visual Studio .NET 2003, в левом поле окна кода Form1 Events, а если в Visual Studio .NET – Base Class Events., а в правом – Paint. Введите в эту процедуру оператор рисования квадрата.
Запустите проект. Вы видите, что квадрат уже на форме, несмотря на то, что никаких кнопок вы не нажимали. Произошло это потому, что событие Paint уже успело сработать при появлении формы на экране. Щелкните по кнопке Button1. Появился еще и кружок. Теперь задвиньте форму частично за левый край экрана и снова выдвиньте. Вы увидите, что часть кружка стерта, а квадрат как будто и не стирался (см. Рис. 12.16).
Рис. 12.16
На самом деле он прекрасно стерся, но при выдвижении формы из-за края экрана много раз в секунду наступало событие Paint и он каждый раз рисовался снова. Причем рисовался настолько быстро, что человеческий глаз не успевал этого заметить.
Итак, мы видим, что власть над тем, каким фигурам стираться, а каким нет, находится полностью в руках программиста. Поэтому, если вы хотите, чтобы фигуры не стирались, помещайте операторы для их рисования в обработчик события Paint.
Событие Paint имеется не только у формы, но и у элементов управления. Можете проверить его, например, на метке.
Мы можем искусственно вызвать событие Paint и, значит, перерисовку поверхности формы или элемента управления, применяя их метод Refresh. Например, так:
Me.Refresh()
Кроме обработки события Paint VB предлагает и другие способы перерисовки, но мы на них останавливаться не будем.
Imageне стирается. Очевиден такой способ борьбы со стиранием. Мы рисуем все, что нам нужно, в памяти на объекте Bitmap, а затем присваиваем получившийся Bitmap свойству Image элемента управления.
Текстурная кисть
Текстурной кистью называют такой способ заливки фигур, когда они вместо цвета или штрихового узора заполняются бесконечно повторяющейся картинкой, взятой вами из графического файла (см. Рис. 12.17).
Рис. 12.17
На этом рисунке эллипс заполнен квадратной картинкой, повторяющейся на нем полтора десятка раз. Создает такую заливку следующая программа:
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Dim Текстура As New Bitmap("Дверь.jpg")
Dim Кисть_текстурная As New TextureBrush(Текстура)
Dim Гр As Graphics = Me.CreateGraphics
Гр.FillEllipse(Кисть_текстурная, 10, 50, 1200, 900)
End Sub
Пояснения. Конструктор текстурной кисти (вторая строка) может сконструировать ее только из объекта типа Image, содержащего подходящую картинку. Поэтому первой строкой я создал такой объект с именем Текстура. Последние две строки рисуют на форме эллипс, заполненный этой текстурой.
Большинство вариантов конструктора позволяют использовать для кисти не всю картинку, а вырезанный из нее прямоугольник. Если вы знаете размер картинки в пикселях, то можете этим воспользоваться:
Dim Текстура As New Bitmap("Дверь.jpg")
Dim П As New Rectangle(200, 100, 40, 70)
Dim Кисть_текстурная As New TextureBrush(Текстура, П)
Здесь я заранее знаю размер картинки в объекте Bitmap – 256 на 256. Из нее я вырезал прямоугольник с левым верхним углом в точке (200, 100) относительно левого верхнего угла картинки и с размерами 40 на 70. Этот прямоугольный фрагмент и стал новой картинкой для кисти. Если вы «залезете» вырезаемым прямоугольником за край исходной картинки, VB выдаст ошибку.
Несколько вариантов конструктора позволяют управлять ориентацией картинки в текстурной заливке и создавать простейшие комбинации с использованием этой картинки в разных ориентациях (см. например, Рис. 12.18).
Рис. 12.18
Для этого используется параметр конструктора WrapMode. Это перечисление из пространства имен System.Drawing.Drawing2D. Оно имеет несколько значений, определяющих форму комбинации. Одно из них – TileFlipXY – использовано в программе, создающей этот рисунок
Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
Dim Картинка As New Bitmap("DIME.WMF")
Dim Текстура As New Bitmap(Картинка, 100, 100)
Dim Кисть_текстурн As New TextureBrush(Текстура, System.Drawing.Drawing2D.WrapMode.TileFlipXY)
Dim Гр As Graphics = Me.CreateGraphics
Гр.FillEllipse(Кисть_текстурн, 10, 10, 900, 600)
End Sub
Пояснения. Здесь я сначала, как положено, создал объект Bitmap для картинки, но поскольку картинка получилась слишком большая, я следующим оператором уменьшил ее до размеров 100 на 100. Следующий оператор из полученной картинки создает текстурную кисть с комбинацией TileFlipXY.
Работа с цветом
До сих пор мы с вами пользовались цветами, которые VB предоставлял нам в структуре Color и в классах Pens и Brushes. Это полторы сотни цветов со своими именами. Но есть и другие цвета. О них мы сейчас и поговорим.
Системные цвета
Задаем системные цвета вручную. Попробуйте в режиме проектирования настроить цвет кнопки. Для этого в окне свойств зайдите в ее свойство BackColor, а в нем – в закладку System (см. Рис. 12.19) и выберите какой-нибудь цвет.
Рис. 12.19
В этой закладке представлены так называемые системные цвета. Это те цвета, на которые настроены элементы вашей Windows: окна, их заголовки, цвет текста в заголовках, меню и т.д. Например, цвет кнопок и многих других рельефных элементов в Windows называется Control (у меня на компьютере он серый), а цвет рабочего стола, когда на нем нет обоев, называется Desktop (у меня он темно-голубой). Эти цвета мы и видим на рисунке. Вы, наверное, уже обратили внимание, что и VS вслед за Windows всегда предлагает вам цвет Control в качестве цвета формы, кнопок и других элементов управления.
Итак, системные цвета VS берет от Windows. Проверим. Выйдем из VS в Windows. Перенастроим цветовую гамму Windows. Сейчас она у вас, скорее всего, стандартная, серенькая или синеватая. Выберем что-нибудь поярче. Для этого щелкнем правой клавишей мыши по рабочему столу Windows и в контекстном меню выберем Свойства, а там – закладку Оформление. В возникшем окне перенастроим цветовую схему Windows. Обратите внимание, что весь цветовой облик Windows после этого изменился, цвета всех ее элементов стали другими.
Попробуйте теперь в Windows открыть какое-нибудь солидное приложение, например, Word. Вы увидите, что привычные цвета элементов Word тоже изменились в согласии с выбранной в Windows цветовой схемой. Вернитесь в VS, в ваш проект. Вы видите, что изменился цвет кнопки и цвет формы. Снова зайдите в окно свойств, в свойство BackColor, а в нем – в закладку System. Обратите внимание, что все цвета здесь изменились в согласии с выбранной в Windows цветовой схемой. Попробуйте открыть какой-нибудь старый проект – цвета формы и элементов управления изменились и в нем.
Таким образом, изменение цветовой схемы Windows влияет на все ее приложения. Хорошо это или плохо? Как посмотреть. В любом случае программист должен иметь возможность сам решать, должны ли цвета элементов в его приложении зависеть от настройки цветовой схемы Windows.
Как решать? Просто. Если речь идет о выборе цвета в режиме проектирования, то выбрав цвет из закладки System, вы тем самым разрешаете ему меняться в зависимости от настройки цветовой схемы Windows на компьютере пользователя. Если же вы выбираете цвет из закладок Custom или Web, то запрещаете.
Если вы не используете в своем проекте системных цветов, цвета вашего приложения на любом компьютере будут одинаковы независимо от настроек Windows.
Опасайтесь делать цвета одних элементов проекта системными, а других – несистемными. Например, сделав цвет кнопки системным, а именно Desktop, а цвет текста на этой кнопке несистемным, а именно белым, вы сильно рискуете, потому что если пользователю вздумается сделать рабочий стол своего Windows белым, он ничего не сможет прочесть на вашей кнопке.
И вообще – цветовые схемы Windows составляли профессиональные дизайнеры. Если ваше приложение пользуется только системными цветами, то есть хороший шанс, что его цветовое решение будет достаточно гармоничным. Если ваше приложение пользуется только несистемными цветами, то такой шанс тоже есть, если у вас была пятерка по рисованию. Если же приложение пользуется и системными и несистемными цветами, то есть вероятность, что на чужом компьютере оно будет напоминать помесь попугая с вороной.
Задаем системные цвета в коде. До сих пор в коде мы с вами пользовались несистемными цветами, которые VB предоставлял нам в структуре Color и в классах Pens и Brushes пространства имен System.Drawing. Системные же цвета лежат соответственно в классах SystemColors, SystemPensи SystemBrushesпространства имен System.Drawing. Там вы найдете пару десятков примерно тех же цветов, что и в закладке System свойства BackColor. Из класса SystemPens вы берете готовые перья, из класса SystemBrushes вы берете готовые кисти, из класса SystemColors вы берете цвета для всех прочих нужд. Вот примеры использования системных цветов:
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Button2.BackColor = SystemColors.ActiveCaption
Button2.ForeColor = SystemColors.ActiveCaptionText
Dim Гр As Graphics = Me.CreateGraphics
Гр.DrawEllipse(SystemPens.MenuText, 100, 0, 300, 200)
Гр.FillEllipse(SystemBrushes.ControlLightLight, 80, 80, 140, 180)
Dim Перо As New Pen(SystemColors.ControlDark)
Гр.DrawEllipse(Перо, 10, 50, 230, 150)
Dim Кисть As New SolidBrush(SystemColors.ControlDark)
Гр.FillEllipse(Кисть, 50, 50, 200, 100)
End Sub
Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click
Dim Гр As Graphics = Me.CreateGraphics
Гр.Clear(SystemColors.Control)
End Sub
Функция FromArgb
В VB существует 16 миллионов цветов с лишним (точнее - 16777216)! Их имен мы не знаем, да имен этих и не существует. Тем не менее, мы должны научиться управлять этими цветами. Чтобы навести порядок в этой массе цветов, VB предлагает функцию FromArgb. Она принадлежит структуре Color. Суть ее вот в чем.
Вспомним, что любую краску можно получить, смешав в определенной пропорции красную (Red), зеленую (Green) и синюю (Blue) краски. В VB каждой краски в смесь можно положить от 0 до 255 единиц. Функция FromArgb как раз и смешивает эти краски. Пусть мы хотим покрасить форму краской, в которую мы положили и смешали 200 единиц красной, 40 единиц зеленой и 250 единиц синей краски. Для этого пишем такой оператор:
Me.BackColor = Color.FromArgb(200, 40, 250)
Здесь мы использовали вариант функции FromArgb с тремя параметрами.
Примеры:
FromArgb (255, 0, 0) | Красный цвет |
FromArgb (0, 255, 0) | Зеленый цвет |
FromArgb (0, 0, 255) | Синий цвет |
FromArgb (255, 255, 0) | Желтый цвет |
Вы видите, что желтый цвет – это смесь красного с зеленым.
Чем меньше каждой краски мы положим, тем темнее будет цвет, чем больше – тем светлее:
FromArgb (70, 90, 88) | Темный цвет (потому что числа маленькие) |
FromArgb (210, 250, 202) | Светлый цвет (потому что числа большие) |
FromArgb (0, 0, 0) | Черный цвет |
FromArgb (255, 255, 255) | Белый цвет |
Если каждой краски положить поровну, получится серый цвет:
FromArgb (90, 90, 90) | Темно-серый цвет |
FromArgb (220, 220, 220) | Светло-серый цвет |
Вот программа, которая красит форму, рисует и заливает эллипс, как на Рис. 12.20:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim Цвет1, Цвет2 As Color
Цвет1 = Color.FromArgb(250, 230, 252)
Цвет2 = Color.FromArgb(220, 170, 240)
Me.BackColor = Цвет1 'Красим форму
Dim Перо As New Pen(Color.FromArgb(140, 120, 90), 40)
Dim Кисть As New SolidBrush(Цвет2)
Dim Гр As Graphics = Me.CreateGraphics
'Рисуем и заливаем эллипс:
Гр.DrawEllipse(Перо, 50, 30, 300, 200)
Гр.FillEllipse(Кисть, 50, 30, 300, 200)
End Sub
Здесь цвет формы, пера и кисти выбран при помощи функции FromArgb.
Рис. 12.20
Прозрачность
Функция FromArgbпозволяет управлять прозрачностью цвета. Для этого используется ее вариант не с тремя, а с четырьмя параметрами. Второй, третий и четвертый параметры имеют привычный нам смысл количества красной, зеленой и синей краски. А вот первый параметр определяет прозрачность цвета. Если он равен 255, то цвет полностью непрозрачен, а если 0 – то полностью прозрачен (невидим).
Вот программа:
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Dim Гр As Graphics = Me.CreateGraphics
Dim Цвет1, Цвет2, Цвет3, Цвет4 As Color
Цвет1 = Color.FromArgb(255, 50, 130, 152) 'Совершенно непрозрачный цвет
Цвет2 = Color.FromArgb(200, 50, 130, 152) 'Немного прозрачный цвет
Цвет3 = Color.FromArgb(150, 50, 130, 152) 'Более прозрачный цвет
Цвет4 = Color.FromArgb(100, 50, 130, 152) 'Еще более прозрачный цвет
Dim Перо As New Pen(Color.Black, 40) 'Черное перо для горизонтальной линии
Dim Перо1 As New Pen(Цвет1, 40)
Dim Перо2 As New Pen(Цвет2, 40)
Dim Перо3 As New Pen(Цвет3, 40)
Dim Перо4 As New Pen(Цвет4, 40)
Гр.DrawLine(Перо, 30, 100, 300, 100) 'Чертим горизонтальную линию
Гр.DrawLine(Перо1, 80, 60, 80, 200) 'Чертим левую вертикальную линию
Гр.DrawLine(Перо2, 130, 60, 130, 200) 'Чертим вторую слева вертикальную линию
Гр.DrawLine(Перо3, 180, 60, 180, 200) 'Чертим третью слева вертикальную линию
Гр.DrawLine(Перо4, 230, 60, 230, 200) 'Чертим правую вертикальную линию
End Sub
Результат ее работы вы видите на Рис. 12.21. Четыре вертикальные линии одного и того же цвета (50, 130, 152), но разной прозрачности нарисованы на фоне черной горизонтальной линии.
Рис. 12.21
Попробуйте несколько раз нажать на кнопку. Сможете объяснить результат?
Задание 71.
«Атака абстракциониста». На экране рисуются один за другим в быстром темпе залитые случайными цветами эллипсы или прямоугольники случайных размеров и местоположения. Получается очень ярко и живописно.
Задание 72.
Попробуйте из картинки в левой части Рис. 12.22 сделать картинку в правой.
Рис. 12.22
Указание: Для этого заполните пространство фотографии белыми концентрическими эллипсами с разной прозрачностью.
Задание 73.
Представьте себе куб, собранный из множества кубиков. Его высота – 256 кубиков, ширина и толщина – тоже по 256 кубиков. Получается ровно 16777216 кубиков – по числу цветов в VB. Каждый кубик покрашен в свой цвет. Цвета не повторяются. Система раскраски такая. Слева направо растет от 0 до 255 красная составляющая в цвете кубиков, сверху вниз – зеленая, от нас вдаль – синяя. Так что самый левый верхний ближний кубик получается абсолютно черным, а самый правый нижний дальний кубик – абсолютно белым. Сразу все кубики видеть мы, конечно, не можем, но мы можем делать срез куба в любом месте параллельно любой из его граней, в результате чего на срезе будем видеть квадрат, состоящий из 256*256 разноцветных квадратиков. Вот эту задачу среза я бы и хотел вам предложить. Программа просит пользователя выбрать один из трех основных цветов (это удобно сделать через меню) и его насыщенность (число от 0 до 255). Этим определяется место среза. Затем программа чертит на форме этот разноцветный срез. Конечно, квадратики получатся очень маленькими, но это ничего.
Указание: Используйте процедуру с двумя параметрами: выбранный пользователем цвет (один из трех) и его насыщенность.
Кстати, догадайтесь, из каких цветов составлена главная диагональ куба, проведенная между двумя упомянутыми мной кубиками.