Техніка програмування складних вікон в Visual Basic h2>
Вступ h2>
Mногіе
з Вас напевно бачили в Windows програмах вікна нестандартної форми (круглі,
трикутні і т.д.) і ставили собі питання: як мені зробити таке вікно? Якщо
прочитати документацію по Visual Basic, то можна зробити висновок, що стандартні
засоби мови не надають такої можливості. А що ж робити, якщо дуже
хочеться? Тоді слід згадати, що в розпорядженні програміста на VB є
ще й Windows API, який повинен нам у цьому допомогти. p>
Теоретичні основи h2>
Для
Спочатку давайте розберемося, як це можна зробити теоретично. З документації
Windows видно, що кожне вікно в системі описується безліччю параметрів, з
яких нас з Вами цікавить. Видима область
вікна в системі, що створюється Visual Basic має вигляд
прямокутника, але, в принципі, ніщо не заважає змінити форму цієї області.
Дана область вікна описується за допомогою спеціального об'єкта, що
називається Region. Регіон можна представити у вигляді поверхні, обмеженої
координатами, описуваними кутові точки цієї області. Простіше кажучи, можна
описати область будь-якої форми, а потім створити з неї, за допомогою спеціальних
функцій, регіон та його до нужому нам вікна. p>
Існує
декілька функцій Windows API для створення регіонів, основними з яких
є наступні: p>
CombineRgn
- Комбінує два регіони між собою p>
CreateEllipticRgn
- Створює регіон у вигляді еліпса або кола p>
CreatePolygonRgn
- Створює регіон у вигляді багатокутника p>
CreateRectRgn
- Створює прямокутний регіон p>
CreateRoundRectRgn
- Створює регіон із закругленими краями з прямокутної області p>
SetWindowRgn
- Прикріплює регіон до зазначеного вікна p>
Я
не буду приводити докладний опис цих функцій, так як його можна знайти в
описі Win32 API. Крім цих функцій існують ще кілька функцій для
роботи з регіонами, але нам вони не потрібні. p>
Створення простих нестандартних вікон h2>
Тепер,
коли нам відомі основні функції, для створення регіонів, ми можемо застосувати
отримані знання з практики. Завантажте проект pTestRgn і уважно вивчіть
його код. У цьому Проет, для зміни форми вікна на овальну, використовується
всього три рядки коду та три функції Win32 API. Спочатку за допомогою
CreateEllipticRgn створюється регіон, потім він прикріплюється до вікна і, нарешті,
завершальна фаза видалення, яка стала непотрібною, створеного нами регіону. Якщо ж
Ви не видалите непотрібний Вам більше об'єкт, то Windows, створивши регіон для Вас
буде зберігати його у своїх і чекати подальших вказівок за його
використання. Загалом, недобре виділену пам'ять, і
наздожене Вас кара небесна, і затягнеться небо хмарами синіми, і буде страшний
суд над усіма невіруючими: Короче код виглядає так: p>
Private
Sub cmbCreateOval_Click () p>
Dim lRgn As Long p>
lRgn = CreateEllipticRgn (0, 0, Me.ScaleWidth
/ Screen.TwipsPerPixelX, _ p>
Me.ScaleHeight/Screen.TwipsPerPixelY) p>
SetWindowRgn Me.hwnd, lRgn, True p>
DeleteObject lRgn p>
End
Sub p>
Так
ж все просто, скажете Ви? Так, на перший погляд все дуже просто, але це тільки
здається. Той приклад, який Ви тільки що бачили, майже не має практичного
застосування в цих додатках Windows. Кому ж потрібно просто овальне вікно,
яке до того ж жорстко задається на етапі програмування? А ось вікно,
яке вільно могло б міняти свою форму цілком може знадобитися.
Приклади? Будь ласка, WinAmp, Помічник в Microsoft Office та інші програми.
Як же там все це реалізовано? Давайте розберемося з таким застосуванням
регіонів. p>
Створення складних нестандартних вікон h2>
Припустимо,
що у нас є малюнок у форматі BMP, з якого потрібно зробити форму, а білий
колір (наприклад) на ньому означає. Як же зробити форму? Дуже
просто, потрібно взяти все пікселі на малюнку, створити з їх
координат регіон і прикріпити його до потрібного нам вікна. Аналізувати пікселі
можна GetPixel, ця функція за координатами повертає його колір. Давайте тепер
напишемо такий алгоритм для аналізу BMP матриці. Я думаю, що такий алгоритм Вам
відомий, і ми не будемо його докладно розбирати, зазначу лише, що аналіз
проводиться порядково і Pixel-і додаються в регіон не поодинці, а групами
порядково. Такий підхід сильно заощаджує ресурси процесора, виграш у
продуктивності досягає 100%. p>
Public Function lGetRegion (pic As
PictureBox, lBackColor As Long) As Long p>
Dim lRgn As Long p>
Dim lSkinRgn As Long p>
Dim lStart As Long p>
Dim lX As Long p>
Dim lY As Long p>
Dim lHeight As Long p>
Dim lWidth As Long p>
'створюємо порожній регіон, з якого почнемо
роботу p>
lSkinRgn = CreateRectRgn (0, 0, 0, 0) p>
With pic p>
'підрахуємо розміри малюнка в Pixel p>
lHeight =. Height/Screen.TwipsPerPixelY p>
lWidth =. Width/Screen.TwipsPerPixelX p>
For lX = 0 To lHeight - 1 p>
lY = 0 p>
Do While lY
'шукаємо потрібний Pixel p>
Do While lY
lY = lY + 1 p>
Loop p>
If lY
lStart = lY p>
Do While lY
lY = lY + 1 p>
Loop p>
If lY> lWidth Then lY = lWidth p>
'потрібний Pixel знайдений, додамо
його в регіон p>
lRgn = CreateRectRgn (lStart, lX,
lY, lX + 1) p>
CombineRgn lSkinRgn, lSkinRgn,
lRgn, RGN_OR p>
DeleteObject lRgn p>
End If p>
Loop p>
Next p>
End With p>
lGetRegion = lSkinRgn p>
End
Function p>
Отже,
для перевірки на практиці цього алгоритму завантажте приклад pTestRgnSkin і
уважно вивчіть його код. У цьому проекті потрібний нам малюнок, для зручності,
у файлі ресурсів, крім того проект запускається процедурою Main,
в якій і відбуваються всі перетворення. Спочатку завантажується форма, потім в
PictureBox з ресурсів завантажується потрібний нам малюнок, далі викликається
функція, яка створює регіон і, нарешті, завершальний етап - прикріплення
регіону до потрібного нам вікна. Для зручності тут же викликається функція,
поміщаються вікно, щоб воно у Вас на
робочому столі Windows. Крім того, для нормальної роботи програми необхідно,
щоб для PictureBox властивість AutoRedraw було встановлення в True, інакше нічого
не вийде. p>
Sub
Main () p>
Dim lRgn As Long p>
Load frmTestRgnSkin p>
frmTestRgnSkin.pic.Picture =
LoadResPicture (101, vbResBitmap) p>
lRgn = lGetRegion (frmTestRgnSkin.pic, vbWhite) p>
SetWindowRgn frmTestRgnSkin.hWnd, lRgn, True p>
DeleteObject lRgn p>
frmTestRgnSkin.Show p>
SetFormPosition frmTestRgnSkin.hWnd, True p>
End
Sub p>
Тепер
можна запускати проект ... О, знайоме обличчя, скажіть Ви, це ж
з Microsoft Office. Так, схожий, але не зовсім, рухається, а цей
немає. Що ж потрібно зробити, щоб це вікно динамічно змінювало свою форму за
малюнку, що в цей момент часу в PictureBox? p>
Динамічне
зміна форми вікна p>
Існують
програми в яких необхідно динамічно під час роботи змінювати форму вікна
(наприклад анімований персонаж з Microsoft Office). Все це не дуже складно
реалізувати, потрібно в подія PictureBox.Change додати наступний код: p>
lRgn
= LGetRegion (frmTestRgnSkin.pic, vbWhite) p>
SetWindowRgn
frmTestRgnSkin.hWnd, lRgn, True p>
DeleteObject
lRgn p>
SetFormPosition
frmTestRgnSkin.hWnd, True p>
В
принципі все готово, залишилося тільки додати код для зміни картинки на
формі, і буде жити. У нашому прикладі змінювати малюнок будемо в Timer
циклічно, тобто анімація буде безперервна, так простіше. Отже, додамо на форму
Timer і помістимо невеликий код, який відповідає за зміни малюнка
в PictureBox. Малюнків у файлі ресурсів десять штук, тому I має
змінюватися від 101 до 110. Код зміни виглядає так: p>
Static
i As Long p>
If
i <101 Then i = 101 p>
If
i> 110 Then i = 101 p>
frmAnimateForm.pic.Picture
= LoadResPicture (i, vbResBitmap) p>
i
= I + 1 p>
Готово,
можна запускати проект, і якщо Ви щасливий володар Pentium III або Athlon,
то Вам посміхнеться удача, тому що буде рухатися. Але якщо Ваш
процесор Pentium II і нижче, то комп'ютер не зможе виконувати необхідні
розрахунки за потрібне нам час, тому що для плавної анімації необхідно (для
нашого випадку) показувати близько 15 кадрів в секунду, а точніше кожні 80
мілісекунди по кадру і ще залишати час для інших завдань комп'ютера. Як ми
бачимо наші алгоритми явно не тягнуть для таких завдань і призначені для
не потребують таких швидких змін форми вікна, так як,
наприклад на Celeron 333 один кадр формується близько 100 мілісекунди. Що ж
робити? p>
Оптимізація алгоритму для швидкої анімації h2>
Аналіз
роботи алгоритму показує, що найбільші витрати часу припадають на
функцію GetPixel. Це відбувається тому, що аналіз картинки йде безпосередньо
на екрані. Єдиний шлях збільшення швидкодії алгоритму, це перенесення
аналізу в пам'ять комп'ютера і використання при цьому Win 32 API. Такі
алгоритми існують, але це тема окремої розмови, скажу тільки, що для
оптимізації роботи алгоритм пишеться окремо для кожної глибини кольору і при
застосуванні такої схеми швидкодія збільшується майже в чотири рази і
дозволяє робити практично будь-яку анімацію. p>
Список літератури h2>
Для
підготовки даної роботи були використані матеріали з сайту http://visualprogs.narod.ru/
p>