Бедный-бедный ProgressBar! В свое время, разработчики VB, создав мощную библиотеку элементов управления (но очень "тяжелую"), подвигли программистов на создание собственных контролов. И первым стоял ProgressBar. Этот элемент используется в 90% программ и далеко не всегда нужны остальные элементы. В своих разработках я неоднократно обращался к созданию ProgressBar'ов, например "вырезанный" в виде песочных часов. Наверно потому, что это достаточно простой элемент и на его примере можно показать работу других различных функций, процедур и т.п.
Сегодняшний пример - это создание овального ProgressBar'а. Я не буду подробно останавливаться на некоторых мелочах, для этого существует свой цикл статей.
Ну что ж, начнем. Создадим 2 проекта: основной (Name=ProgressCircle) и тестировочный (Name=TestPrCircle).С помощью ActiveX Control Interface Wizard добавим на наш UserControl (Name=PrCircle, AutoRedraw=True, ScaleMode=3 (Pixel)) следующие свойства:
Имя | Описание | Привязка к свойствам UserControl'а | Тип | Значение по умолчанию |
BackColor | Цвет фона | UserControl.BackColor | - | - |
ForeColorPercent | Цвет выводимой надписи % | UserControl.ForeColor | - | - |
FontPercent | Шрифт выводимой надписи % | UserControl.Font | - | - |
ProgressColor | Цвет закрашиваемого сегмента | - | OLE_COLOR | 0 |
PercentVisible | Отображение надписи % | - | Boolean | True |
Min | Минимальное значение | - | Long | 0 |
Max | Максимальное значение | - | Long | 100 |
Value | Текущее значение | - | Long | 0 |
В Property Let каждого свойства добавим последней строкой выполнение внутренней процедуры Draw (ей мы займемся чуть позже). Так же эту процедуру добавим в события UserControl_Resize и UserControl_Show.
Теперь нам необходимо создать еще одно свойство (только для чтения) - Percent. Расчет выполняемого процента несложен и укладывается в простую формулу.
Public Property Get Percent()
As Integer
On Error Resume Next
Percent = Format((m_Value - m_Min) * 100 / _
(m_Max - m_Min), "0")
End Property
Заготовка сделана. Все остальное будем добавлять по мере написания процедуры Draw, которой сейчас и займемся:
В раздел деклараций добавим
функцию, рисующую эллипс:
Private Declare Function Ellipse
Lib "gdi32" _
(ByVal hdc As Long,
_
ByVal X1 As Long,
_
ByVal Y1 As Long,
_
ByVal X2 As Long,
_
ByVal Y2 As Long)
_
As Long
Private Sub
Draw()
'переменные, используемые в
процедуре
Dim i%, C As Single, S As Single, numPt&, hRgn&, hBrush&, LB
As LOGBRUSH
Cls
Ellipse hdc, 0, 0, ScaleWidth, ScaleHeight
Line (lWidth, lHeight)-(lWidth, 0), m_ProgressColor
...
Сначала очищаем контрол от предыдущей
графики, затем рисуем эллипс, заполняющим
весь контрол. И наконец, рисуем стартовую
вертикальную линию от центра строго вверх.
Чтобы многократно не вызывать одно и то же
свойство контрола, объявим две переменные в
разделе деклараций: lWidth As Long и lHeight As Long, а
инициализацию их будем проводить в событии
UserControl_Resize. В окончательном виде это событие
выглядит так:
Private Sub UserControl_Resize()
lWidth = ScaleWidth / 2
lHeight = ScaleHeight / 2
Draw
End Sub
Для
просчета точек дуги, нам необходимо создать
регион, причем каждая точка имеет
координаты по вертикали и по горизонтали.
Опять же в разделе деклараций объявим тип и
массив:
Private Type COORD
x As Long
y As Long
End Type
Private arrFillRgn() As COORD
В
процедуре Draw высчитываем количество точек
и переопределяем массив:
...
numPt = Round(Percent * 3.6, 0)
ReDim arrFillRgn(numPt + 1)
...
Далее вносим координаты
каждой точки в массив, используя константу
rd перевода в радианы (для тех, кто забыл
математику, подсказываю Pi/180). Последняя
точка получает координаты центра эллипса,
тем самым замыкая регион (выделяемую
область). И создаем сам регион. Для этого
объявляем в разделе деклараций еще одну API-функцию:
Private Declare Function CreatePolygonRgn
Lib "gdi32" _
(lpPoint As Any, _
ByVal nCount As Long,
_
ByVal nPolyFillMode As Long)
_
As Long
...
For i = 0 To numPt
C = Cos(i * rd)
S = Sin(i * rd)
arrFillRgn(i).x = S * lWidth + lWidth
arrFillRgn(i).y = -C * lHeight + lHeight
Next i
arrFillRgn(numPt + 1).x = lWidth
arrFillRgn(numPt + 1).y = lHeight
hRgn = CreatePolygonRgn(arrFillRgn(1), numPt + 1, ALTERNATE)
...
Для того, чтобы залить
полученный регион цветом, необходимо
создать кисть. Поэтому в разделе деклараций
появляется еще один тип и 2 API-функции:
Private Type LOGBRUSH
lbStyle As Long
lbColor As Long
lbHatch As Long
End Type
Private Const ALTERNATE = 1
Private Const HS_SOLID = 8
Private Const BS_SOLID = 0
Private Declare Function CreateBrushIndirect
Lib "gdi32" _
(lpLogBrush As LOGBRUSH) _
As Long
Private Declare Function FillRgn
Lib "gdi32" _
(ByVal hdc As Long,
_
ByVal hRgn As Long,
_
ByVal hBrush As Long)
_
As Long
Вернемся
в Draw и продолжим:
...
LB.lbColor = m_ProgressColor
LB.lbHatch = HS_SOLID
LB.lbStyle = BS_SOLID
hBrush = CreateBrushIndirect(LB)
FillRgn hdc, hRgn, hBrush
...
Выполнив все, что нам было
необходимо, мы обязаны освободить память с
помощью функции
Private Declare Function DeleteObject
Lib "gdi32" _
(ByVal hObject As Long)
_
As Long
...
DeleteObject hRgn
DeleteObject hBrush
...
Ну и напоследок, отображаем
(или нет) надпись процентов на нашем
контроле:
If m_PercentVisible
Then
Dim strPercent$
strPercent = Str(Percent) & " %"
CurrentX = (ScaleWidth - TextWidth(strPercent)) / 2
CurrentY = (ScaleHeight - TextHeight(strPercent)) / 2
Print strPercent
End If
Refresh
В
тестировочной форме разместим наш контрол,
кнопку и добавим код:
Private Sub cmdTest_Click()
Dim i&
With PrCircle1
For i = .Min To .Max
.Value = i
Next
End With
End Sub
В основе -
это все. Мы получили полнофункциональный
элемент управления. При желании Вы можете
добавить свойства, подстраивая контрол "под
себя".
Удачи!
2002 г.