На главную

Библиотека Интернет Индустрии I2R.ru

Rambler's Top100

Малобюджетные сайты...

Продвижение веб-сайта...

Контент и авторское право...

Забобрить эту страницу! Забобрить! Блог Библиотека Сайтостроительства на toodoo
  Поиск:   
Рассылки для занятых...»
I2R » И2Р Программы » Программирование » VBasic, VBS

Техника программирования сложных окон в Visual Basic

Введение
Многие из Вас наверняка видели в Windows программах окна нестандартной формы (круглые, треугольные и т.д.) и задавали себе вопрос: как мне сделать такое окно? Если прочитать документацию по Visual Basic, то можно сделать вывод, что стандартные средства языка не предоставляют такой возможности. А что же делать, если очень хочется? Тогда следует вспомнить, что в распоряжении программиста на VB есть еще и Windows API, который должен нам в этом помочь. Все примеры кода, которые описаны в этой статье, Вы можете взять здесь.

Теоретические основы
Для начала давайте разберемся, как это можно сделать теоретически. Из документации Windows видно, что каждое окно в системе описывается множеством параметров, из которых нас с Вами интересует "видимая область окна". Видимая область окна в системе, создаваемое Visual Basic "по умолчанию" имеет вид прямоугольника, но, в принципе, ничто не мешает изменить форму этой области. Данная область окна описывается с помощью специального объекта, который называется Region. Регион можно представить в виде поверхности, ограниченной координатами, описываемыми угловые точки этой области. Проще говоря, можно описать область любой формы, затем создать из неё, с помощью специальных функций, регион и "прикрепить" его к нужному нам окну.
Существует несколько функций Windows API для создания регионов, основными из которых являются следующие:

CombineRgn - Комбинирует два региона между собой
CreateEllipticRgn - Создает регион в виде эллипса или окружности
CreatePolygonRgn - Создает регион в виде многоугольника
CreateRectRgn - Создает прямоугольный регион
CreateRoundRectRgn - Создает регион со скругленными краями из прямоугольной области
SetWindowRgn - Прикрепляет регион к указанному окну

Я не буду приводить подробное описание этих функций, так как его можно найти в описании Win32 API. Кроме этих функций существуют ещё несколько функций для работы с регионами, но нам они не потребуются.

Создание простых нестандартных окон
Теперь, когда нам известны основные функции, для создания регионов, мы можем применить полученные знания на практике. Загрузите проект pTestRgn и внимательно изучите его код. В этом проете, для изменения формы окна на овальную, используется всего три строки кода и три функции Win32 API. Вначале с помощью CreateEllipticRgn создается регион, затем он прикрепляется к окну и, наконец, завершающая фаза удаление, ставшего ненужным, созданного нами региона. Если же Вы не удалите ненужный Вам больше объект, то Windows, создав регион для Вас будет хранить его в своих "недрах" и ждать дальнейших указаний по его использованию. В общем, нехорошо "захламлять" выделенную память, и настигнет Вас кара небесная, и затянется небо тучами синими, и будет страшный суд над всеми неверующими... Короче код выглядит так:

Private Sub cmbCreateOval_Click()
 Dim lRgn As Long
 lRgn = CreateEllipticRgn(0, 0, Me.ScaleWidth / Screen.TwipsPerPixelX, Me.ScaleHeight / Screen.TwipsPerPixelY)
 SetWindowRgn Me.hwnd, lRgn, True
 DeleteObject lRgn
End Sub

Как же всё просто, скажете Вы? Да, на первый взгляд всё очень просто, но это только кажется. Тот пример, который Вы только что видели, почти не имеет практического применения в настоящих приложениях Windows. Кому же нужно просто овальное окно, которое к тому же жестко задается на этапе программирования? А вот окно, которое свободно могло бы менять свою форму вполне может потребоваться. Примеры? Пожалуйста, WinAmp, Помощник в Microsoft Office и другие программы. Как же там всё это реализовано? Давайте разберемся с таким применением регионов.

Создание сложных нестандартных окон
Допустим, что у нас есть рисунок в BMP формате, из которого нужно сделать форму, а белый цвет (например) на нём означает "пустоту". Как же сделать форму? Очень просто, нужно взять все "не белые" пиксели на рисунке, создать из их координат регион и прикрепить его к нужному нам окну. Анализировать пиксели можно GetPixel, эта функция по координатам возвращает его цвет. Давайте теперь напишем такой алгоритм для анализа BMP матрицы. Я думаю, что такой алгоритм Вам известен, и мы не будем его подробно разбирать, отмечу только, что анализ производится построчно и Pixel-и добавляются в регион не по одному, а группами построчно. Такой подход сильно экономит ресурсы процессора, выигрыш в производительности достигает 100%.

Public Function lGetRegion(pic As PictureBox, lBackColor As Long) As Long
 Dim lRgn As Long
 Dim lSkinRgn As Long
 Dim lStart As Long
 Dim lX As Long
 Dim lY As Long
 Dim lHeight As Long
 Dim lWidth As Long

'создаем пустой регион, с которого начнем работу
 lSkinRgn = CreateRectRgn(0, 0, 0, 0)

 With pic
 'подсчитаем размеры рисунка в Pixel
 lHeight = .Height / Screen.TwipsPerPixelY
 lWidth = .Width / Screen.TwipsPerPixelX
 For lX = 0 To lHeight - 1
 lY = 0
 Do While lY < lWidth
 'ищем нужный Pixel
 Do While lY < lWidth And GetPixel(.hDC, lY, lX) = lBackColor
 lY = lY + 1
 Loop

 If lY < lWidth Then
 lStart = lY
 Do While lY < lWidth And GetPixel(.hDC, lY, lX) <> lBackColor
 lY = lY + 1
 Loop
 If lY > lWidth Then lY = lWidth
 'нужный Pixel найден, добавим его в регион
 lRgn = CreateRectRgn(lStart, lX, lY, lX + 1)
 CombineRgn lSkinRgn, lSkinRgn, lRgn, RGN_OR
 'удалим ненужный объект
 DeleteObject lRgn
 End If
 Loop
 Next
 End With
 lGetRegion = lSkinRgn
End Function

Итак, для проверки на практике этого алгоритма загрузите пример pTestRgnSkin и внимательно изучите его код. В этом проекте нужный нам рисунок, для удобства, "зашит" в файле ресурсов, кроме того проект запускается процедурой Main, в которой и происходят все преобразования. Вначале загружается форма, затем в PictureBox из ресурсов загружается нужный нам рисунок, далее вызывается функция, которая создает регион и, наконец, завершающий этап - прикрепление региона к нужному нам окну. Для удобства здесь же вызывается функция, помещающая окно "поверх всех", чтобы оно "не потерялось" у Вас на рабочем столе Windows. Кроме того, для нормальной работы программы необходимо, чтобы для PictureBox свойство AutoRedraw было установлено в True, иначе ничего не получится.

Sub Main()
 Dim lRgn As Long
 Load frmTestRgnSkin
 frmTestRgnSkin.pic.Picture = LoadResPicture(101, vbResBitmap)
 lRgn = lGetRegion(frmTestRgnSkin.pic, vbWhite)
 SetWindowRgn frmTestRgnSkin.hWnd, lRgn, True
 DeleteObject lRgn
 frmTestRgnSkin.Show
 SetFormPosition frmTestRgnSkin.hWnd, True
End Sub

Теперь можно запускать проект... О, знакомое лицо, скажите Вы, это же "Скрепыш" из Microsoft Office. Да, похож, но не совсем, "Скрепыш" двигается, а этот нет. Что же нужно сделать, чтобы это окно динамически изменяло свою форму по рисунку, отображаемому в данный момент времени в PictureBox?

Динамическое изменение формы окна
Существуют программы в которых необходимо динамически во время работы изменять форму окна (например анимированный персонаж из Microsoft Office). Все это не очень сложно реализовать, нужно в событие PictureBox.Change добавить следующий код:

lRgn = lGetRegion(frmTestRgnSkin.pic, vbWhite)
SetWindowRgn frmTestRgnSkin.hWnd, lRgn, True
DeleteObject lRgn
SetFormPosition frmTestRgnSkin.hWnd, True

В принципе всё готово, осталось только добавить код для изменения картинки на форме, и "Скрепыш" оживёт. В нашем примере изменять рисунок будем в Timer циклически, т.е. анимация будет непрерывна, так проще. Итак, добавим на форму Timer и поместим "в него" небольшой код, отвечающий за изменения рисунка в PictureBox. Рисунков в файле ресурсов десять штук, поэтому I должно изменяться от 101 до 110. Код изменения выглядит так:

Static i As Long
If i < 101 Then i = 101
If i > 110 Then i = 101
frmAnimateForm.pic.Picture = LoadResPicture(i, vbResBitmap)
i = i + 1

Готово, можно запускать проект, и если Вы счастливый обладатель Pentium III или Athlon, то Вам улыбнется удача, так как "Скрепыш" будет двигаться. Но если Ваш процессор Pentium II и ниже, то компьютер не сможет выполнять необходимые расчеты за нужное нам время, так как для плавной анимации необходимо (для нашего случая) показывать порядка 15 кадров в секунду, а точнее каждые 80 миллисекунд по кадру и ещё оставлять время для других задач компьютера. Как мы видим наши алгоритмы явно не тянут для таких задач и предназначены для "работ" не требующих таких быстрых изменений формы окна, так как, например на Celeron 333 один кадр формируется около 100 миллисекунд. Что же делать?

Оптимизация алгоритма для быстрой анимации
Анализ работы алгоритма показывает, что наибольшие затраты времени приходятся на функцию GetPixel. Это происходит потому, что анализ картинки идет непосредственно на экране. Единственный путь увеличения быстродействия алгоритма, это перенос анализа в память компьютера и использование при этом Win 32 API. Такие алгоритмы существуют, но это тема отдельного разговора, скажу только, что для оптимизации работы алгоритм пишется отдельно для каждой глубины цвета и при применении такой схемы быстродействие увеличивается почти в четыре раза и позволяет делать практически любую анимацию.

Шатрыкин Иван
www.vbline.narod.ru

Другие разделы
C, C++
Java
PHP
VBasic, VBS
Delphi и Pascal
Новое в разделе
Базы данных
Общие вопросы
Теория программирования и алгоритмы
JavaScript и DHTML
Perl
Python
Active Server Pages
Программирование под Windows
I2R-Журналы
I2R Business
I2R Web Creation
I2R Computer
рассылки библиотеки +
И2Р Программы
Всё о Windows
Программирование
Софт
Мир Linux
Галерея Попова
Каталог I2R
Партнеры
Amicus Studio
NunDesign
Горящие путевки, идеи путешествийMegaTIS.Ru

2000-2008 г.   
Все авторские права соблюдены.
Rambler's Top100