Программирование на Visual Basic

Строка состояния на visual basic

  Эта статья - первая в цикле статей "Common Controls" о возможностях библиотеки comctl32.dll и их прямом использовании. Любой программист на VB знаком с элементами управления Microsoft Windows Common Controls. Они реализуют функции указанной выше библиотеки. Их удобно использовать, но приходиться таскать с собой лишнюю OCX-библиотеку, солидного размера.
Теперь появилась возможность вызывать необходимые функции напрямую. То есть она была, но функции были неизвестны (почему-то их не поместили в Win32Api.txt). Покопавшись в заголовочных файлах C++, я обнаружил много полезной информации на эту тему. Перед тем, как перейти к подробностям, скажу, что используемый в этих статьях код работает под Windows98 и windows2000(Me и Xp не проверял, оставляю Вам).

Строку состояния я реализовал как класс CStatusBar, так, что увидеть её можно будет только в период выполнения.
Для начала объявим необходимые функции.
Первая - для создания строки состояния: CreateStatusWindow.Объявляется она так:

Declare Function CreateStatusWindow Lib "comctl32.dll" Alias "CreateStatusWindowA" ( _
ByVal Style As Long, _
ByVal pszText As String, _
ByVal hWndParent As Long, _
ByVal wID As Long) As Long


Разберём параметры. Первый Style отвечает за стиль строки состояния. В этом параметре нужно передать WS_CHILD и WS_VISIBLE. В pszText передаётся текст для строки. hWndParent - манипулятор окна, для которого создаётся строка состояния. wID - идентификатор строки состояния(передаёте 0 и забудьте о нём).
Следующая процедура используется для инициализации библиотеки.

Declare Sub InitCommonControls Lib "comctl32.dll" ()

Перед использованием ЛЮБОГО "общего элемента" нужно взывать эту процедуру.
Чтобы управлять строкой состояния потребуется функция SendMessage, а по окончании работы строка уничтожается функцией DestroyWindow.
Нам потребуются константы WM_SZIE - для подгонки строки состояния под размер окна и WM_USER, как основа для сообщений строки: SB_SETPARTS = WM_USER + 4 - для разбиения на панели, и SB_SETTEXTA = WM_USER + 1 ( SB_SETTEXTW = WM_USER + 11) - для установки текста строки.
Также нам понадобятся два специальных типа.
Тип для хранения свойств панели.

Private Type SB_PANEL_TYPE
Width As Long "Ширина панели в пикселях (отсчёт от левого края)
"или -1 для прижатия панели к правому краю
Text As String "Текст панели
Style As Long "Стиль панели
End Type


И перечисляемый тип для определения стиля панели.

Public Enum SBStyleConstants
Normal=0
NoBorders=&H100 "Без рамки
PopOut=&H200 "Выдавленная
RTLReading=&H400 "Вывод текста слева направо.
End Enum

Добавим несколько переменных.

Private SPCount As Long "Число панелей
Private SP() As SB_PANEL_TYPE "Набор панелей
Private hStatusBar As Long "Манипулятор строки состояния


Итак, объявления проведены, теперь разберёмся с событиями. Главное событие класса - инициализация.

Private Sub Class_Initialize
"Выполним единственную требуемую операцию инициализации
"подготовим элементы управления.
InitCommonControls
End Sub


Всё по правилам, инициализация проведена. В соответствие ей проведём заврещение.

Private Sub Class_Terminate
"Если окно создавалось средствами API
"его нужно уничтожить теми же средствами.
If hStatusBar <> 0 Then DestroyWindow hStatusBar
End Sub


События рассмотрены, переходим к методам.
Основной минус VB, в том, что переменную нельзя объявить и инициализировать сразу, а значит нужен метод Create.

Public Sub Create (ByVal STBText As String, _
ByVal hWndParent As Long)
"Создаётся строка состояния, подобная строке из OCX, типа - Simple
hStatusBar = CreateStatusWindow(WS_CHILD Or WS_VISIBLE, STBText,hWndParent,0)
End Sub


Этот метод получает текст для строки и манипулятор окна, для создания строки. Во время выполненияполучим окна "украшенное" стандартной строкой состояния. Но стоит "поиграть" с окном, как вся красота исчезнет. Увы, наша строка не реагирует на изменение размеров окна. Что-ж в наших силах это исправить.

Public Sub Resize()
"Если создавалось окно...
If hStatusBar <> 0 Then SendMessage hStatusBar, WM_SIZE, 0, ByVal 0&
"Отправим стандартное сообщение Windows
End Sub

Всё просто, поместим вызов метода Resize в событие Form_Resize и получается просто загляденье.
Но этого мало, строка состояния получается слишком уж Simple. Надо позаботиться о наборе панелей.

Public Sub AddPanel(ByVal NewText As String, _
Optional NewWidth As Long, _
Optional NewStyle As SBStyleConstants)
If NewWidth = 0 Then NewWidth = -1
"Сделав панель шириной в -1 пиксель, мы растянем её до правого краяя окна.
SPCount = SPCount + 1 "Увеличим счётчик панелей
"Увеличиваем набор панелей на одну
ReDim Preserve SP(SPCount)
"Настроим свойства
With SP(SPCount)
.Style = NewStyle
.Text = NewText
If NewWidth <> -1 Then .Width = NewWidth + SP(SPCount - 1).Width _
Else .Width = -1
End With
"Вызываем внутренние методы
SetParts
SetText
End Sub


Теперь сами внутренние методы. Первый SetParts разбивает строку состояния на панели. Второй SetText устанавливает текст панелей.

Private Sub SetParts()
"Проверка создания строки
If hStatusBar = 0 Then Exit Sub
"Для разбиения на панели SendMessage в lParam передаётся первый элемент
"массива содержащего размеры панелей.
"В wParam передаётся число элементов массива.
"Объявим динамический массив
Dim PSize() As Long
" и подгоним его размер под число панелей
ReDim PSize(SPCount)
Dim I As Long
"Цикл для переноса значений
For I = 0 To SPCount
PSize(i) = SP(i).Width
Next I
"Отсылаем сообщение SB_SETPARTS
SendMessage hStatusBar, SB_SETPARTS, SPCount + 1, PSize(0)
End Sub

Private Sub SetText()
"Проверка создания строки
If hStatusBar = 0 Then Exit Sub
"Для установки текста SendMessage в lParam передаётся текст панели(точнее его адрес)
"В wParam передаётся номер панели и стиль.
"Объявляем байтовый массив
Dim PStr() As Byte
"Подгоняем размер
ReDim PStr(300)
Dim I as Long
Dim strTemp As String
"Перебираем панели
For I = 1 To SPCount
If Len(SP(I).Text) <> 0 Then
"Подгоняем длину
strTemp = SP(i).Text & String$(300-Len(SP(I).Text),32)
"Преобразуем строку в байтовый массив
PStr = StrConv(strTemp,vbFromUnicode)
"Отправляем сообщение SB_SETTEXTA
SendMessage hStatusBar, SB_SETTEXTA,I Or SP(I).Style, PStr(0)
End If
Next I
End Sub


Остановимся подробнее на преобразовании строки. Массив из 300 байт нужен по двум причинам. Во-первых: строка состояния ожидает текст в ANSI-кодировке, а VB использует Unicode.Во-вторых: если использовать массив произвольного размера или строку переменной длины, могут появиться ненужные символы в конце.
Методы установлены, разберём свойства.
Panel Text - Текст выводимый в панели.

Public Property Get PanelText(ByVal Index as Long) As String
"Застрахуемся от невнимательных, безалаберных, злобных и т.д. пользователей нашего класса
If (Index < 1) Or (Index > SPCount) Then Exit Property
PanelText = SP(Index).Text
End Property

Public Property Let PanelText(ByVal Index as Long, ByVal vNewValue As String)
If (Index < 1) Or (Index > SPCount) Then Exit Property
SP(Index).Text = vNewValue
SetText
End Property


PanelStyle - стиль панели
Public Property Get PanelStyle(ByVal Index As Long) As SBStyleConstants
If (Index < 1) Or (Index > SPCount) Then Exit Property
PanelStyle = SP(Index).Style
End Property

Public Property Let PanelStyle(ByVal Index As Long, ByVal vNewValue As SBStyleConstants)
If (Index < 1) Or (Index > SPCount) Then Exit Property
SP(Index).Style = vNewValue
SetText
End Property


PanelWidth - ширина панели
Public Property Get PanelWidth(ByVal Index As Long) As Long
If (Index < 1) Or (Index > SPCount) Then Exit Property
"Т.к. на самом деле в массиве храниться не ширина, а координата правой границы
If SP(Index).Width <> -1 Then PanelWidth = SP(Index).Width - SP(Index - 1).Width _
Else PanelWidth = -1
End Property

Public Property Let PanelWidth(ByVal Index As Long, ByVal vNewValue As Long)
If (Index < 1) Or (Index > SPCount) Then Exit Property
If vNewValue <> -1 Then SP(Index).Width = vNewValue + SP(Index - 1).Width _
Else SP(Index).Width = -1
SetText
SetParts
End Property


И ещё один полезный метод, который размещает на панели любой элемент управления обладающий свойством hWnd.

Public Sub AddControl(ByVal Index As Long, ByVal CtlHWND As Long)
If (Index < 1) Or (Index > SPCount) Then Exit Sub
SetParent CtlHWND, hStatusBar
SetWindowPos CtlHWND, 0, SP(Index - 1).Width + 3, 0 + 3, 0, 0, SWP_NOZORDER Or SWP_NOSIZE
End Sub


Итак, мы имеем неплохую альтернативу библиотечной строки состояния, и при этом избавлены от этой библиотеки. По-моему неплохо.

Hosted by uCoz