к оглавлению   к 4GL - визуальному программированию

Советы тем, кто программирует на Visual Basic

Андрей Колесов
Ольга Павлова

Совет 145. Определение положения указателя линейки прокрутки

Как известно, положение указателя (движка) линейки прокрутки характеризуется значением свойства Value, которое изменяется от значения Min до Max (свойства элемента управления, которые можно задать в окне Properties).

Сделайте такой небольшой опыт. Поместите на форме поле текста, а справа от него — вертикальную линейку прокрутки. Установите Min и Max равными 1 и 10 соответственно (по умолчанию они были 0 и 32767). В событие VScroll1_Change введите код:

Text1.Text = VScroll1.Value

Теперь нажмите F5 для запуска проекта. Прокручивая линейку, вы будете видеть в поле текста цифровое значение положения указателя. Обратите внимание, что свойства LargeChange и SmallChange элемента VScroll задают соответственно величину, на которую будет изменяться значение свойства Value данного элемента управления при щелчке пользователем области между движком и стрелкой прокрутки или при щелчке самой стрелки прокрутки.

Проблема же порой заключается в том, что минимальное значение Value соответствует верхнему положению указателя. В то же время в некоторых случаях бывает нужно, чтобы Value при перемещении указателя вниз не увеличивалось, а наоборот — уменьшалось. Сделать это можно просто: заменив программный код:

Text1.Text = VScroll1.Max — VScroll1.Value + VScroll1.Max

Однако еще проще сделать таким образом: свойства Max и Min установите равными отрицательным значениям, например -1 и -10, а в событие Change запишите строку:

Text1.Text = Abs(VScroll1.Value)

Теперь в обоих случаях верхнее положение указателя будет соответствовать максимальному значению заданного диапазона данных.

Совет 146. Создание числового счетчика

Предыдущий совет поможет нам в создании элемента управления, представляющего собой числовой счетчик, внешний вид которого приведен на рис. 1. Как легко догадаться, на самом деле это два стоящих рядом элемента управления Text и VScroll с одинаковыми значениями свойств Top и Height, причем последний подобран таким образом, чтобы указатель линейки прокрутки стал невидимым. Теперь, щелкая стрелки линейки, мы увидим автоматическое изменение числового значения с нужным нам шагом (SmallChange). Для практического использования такого счетчика следует также установить его начальное значение, например, при загрузке формы:

Sub Form_Load()

VScroll1.Value = VScroll1.Max

Call VScroll1_Change()

End Sub

Совет 147. Как измерить протяженность текста

Порой бывает полезным узнать физическую длину текстовой строки (размер в единицах длины с учетом, в частности, размера и типа шрифта) при выводе ее на экран. В принципе для этого можно воспользоваться функциями Win API, но есть еще более простой способ – использовать свойство AutoSize элемента управления Label. Для начала поместите метку (labMeasure) на форму и установите ее свойство AutoSize как True, а свойство Visible — как False. То есть ширина метки будет автоматически устанавливаться равной длине строки, но при этом сам текст метки будет невидимым. После этого напишите такую функцию:

Private Function TextExtent(txt$ as String) as Integer

labMeasure.Caption = txt$

TextExtent = labMeasure.Width

End Function

Обратите внимание, что возвращаемая величина (TextExtent) измеряется в твипах. Твип (twip) — это независимая от экрана единица измерения, используемая для обеспечения того, чтобы местонахождение и пропорции элементов экрана в приложении всегда были одинаковы на всех типах дисплеев. Твип — единица измерения экрана, равная 1/20 точки принтера. В одном логическом сантиметре содержится приблизительно 567 твипов, а в одном логическом дюйме — 1440 твипов. Логический сантиметр (дюйм) — это длина элемента экрана, равная сантиметру (дюйму) при печати.

Теперь, если вы захотите выяснить протяженность какого-либо текста, просто вызовите данную функцию с текстовой строкой в качестве параметра. Проиллюстрируем это на следующем примере. Поместим на форму еще один элемент управления — поле текста. Мы хотим выяснить, какую он должен иметь ширину, чтобы целиком вмещать строковую переменную a$ (у текстового поля нет свойства AutoSize). Обратите внимание, что элементы управления Label и TextBox содержат разное количество символов и при равной ширине позволяют записывать различное число видимых символов. Поэтому мы добавляем два пробела к строке A$:

Private Sub Form_Load()

txt1$ = “Исходный текст”

Text1.Width = TextExtent(“ “ & txt1$)

Text1.Text = txt1$

End Sub

Совет 148. Используйте пользовательские диалоговые окна при редактировании ячеек элемента управления DBGrid

Существует простой способ добавления различных пользовательских диалоговых окон ко всем ячейкам элемента управления DBGrid. Для этого вначале поместите на форму компоненты DBGrid и Data. Установите свойства DatabaseName и RecordSource элемента управления Data как, например, biblio.mdb и Publishers. Затем установите свойство DataSource элемента управления DBGrid как Data1 (компонент Data).

Теперь напишите следующий код:

Dim strDBGridCell As String

Private Sub DBGrid1_AfterColEdit(ByVal ColIndex As Integer)

DBGrid1.Columns(ColIndex) = strDBGridCell

End Sub

Private Sub DBGrid1_BeforeColEdit(ByVal ColIndex As Integer, _

ByVal KeyAscii As Integer, Cancel As Integer)

strDBGridCell = InputBox(“Edit DBGrid Cell:”, , _

DBGrid1.Columns(ColIndex))

End Sub

Теперь, когда пользователь попытается отредактировать какую-либо ячейку элемента управления DBGRid, на экран будет выводиться запрос InputBox для осуществления ввода. Если хотите, то можете заменить этот запрос на любое другое пользовательское диалоговое окно.

Совет 149. При формировании констант типа Long используйте суффикс &

Уже довольно давно, в совете 80 (КомпьютерПресс № 2’97, стр. 74) мы обращали ваше внимание на специфику работы с переменными типа Long (&) при их использовании в качестве масок в логических операциях.

Например, если выполнить такой фрагмент программы:

Code& = &H80040113

Result& = Code& And &HFFFF,

то содержимое Result& будет равно не &H0113, как хотелось бы, а

&H80040113.

Суть проблемы заключается в том, что переменные Integer и Long являются переменными со ЗНАКОМ, и нужно быть очень внимательным при переходе от беззнакового представления числа (&H...) к знаковому (цифровому десятичному). Рассмотрим такую конструкцию:

Const Mask& = &HFFFF

Print HEX$(Mask&) ‘ будет напечатано &HFFFFFFFF !!!!

Дело в том, что константа &HFFFF сначала автоматически представляется в виде переменной Integer и в числовом выражении равна -1. А уже затем при присвоении Mask& = &HFFFF происходит преобразование из Integer в Long, и переменная Mask& = -1 (&HFFFFFFFF)! Аналогичные преобразования происходят и в приведенной выше операции Result& = Code& And &HFFFF.

Тогда мы посоветовали читателям для определения констант типа Long, в том числе в арифметических или логических операциях, в диапазоне значений &H8000-&HFFFF (32768-65535) не использовать беззнаковое шестнадцатеричное представление, применяя только десятичное.

Так вот, мы были в общем-то не правы. На самом деле имеется возможность задания “чистых” констант типа Long независимо от диапазона их значений. Действительно, константа &HFFFF преобразуется в целочисленную переменную типа Integer. Но если справа к ней добавить еще один символ &, то сразу получится переменная Long. Попробуйте сами:

Const Mask& = &HFFFF&

Print HEX$(Mask&)

будет напечатано &HFFFF — то, что нам и нужно было получить. Причем машинный код этого фрагмента стал компактнее из-за отсутствия дополнительных внутренних преобразований данных.

Совет 150. Использование параметра Alias при работе с функциями API

Ряд функций Windows API содержит параметры, которые могут определяться различным образом. Например, при вызове функции WinHelp последний параметр может передавать данные типа Long или String в зависимости от заданного действия.

Visual Basic позволяет объявлять такой тип данных, как Any, при вызовах функций API, но это может привести к ошибкам несовпадения типов или даже к аварийному отказу системы, если значение имеет неправильный тип.

Поэтому во избежание подобных ошибок или для улучшения проверки типов данных во время выполнения приложения бывает полезно произвести объявление нескольких версий одной и той же функции API. Написав для каждого возможного типа параметра свое объявление функции, вы осуществляете более жесткую проверку типов данных.

Для иллюстрации данной методики добавьте к модулю формы следующие функции API и константы. Обратите внимание, что два описания функции API отличаются друг от друга только своим названием (WinHelp и WinHelpSearch) и объявлением типа последнего параметра (dwData as Long и dwData as String).

‘ Объявления API-функции WinHelp

Private Declare Function WinHelp Lib “user32” Alias _

“WinHelpA” (ByVal hwnd As Long, ByVal lpHelpFile _

As String, ByVal wCommand As Long, ByVal dwData _

As Long) As Long

Private Declare Function WinHelpSearch Lib “user32” _

Alias “WinHelpA” (ByVal hwnd As Long, ByVal _

lpHelpFile As String, ByVal wCommand As Long, ByVal _

dwData As String) As Long

Private Const HELP_PARTIALKEY = &H105&

Private Const HELP_HELPONHELP = &H4&

Private Const HelpFile = _

“c:\program files\devstudio\vb5\help\vb5.hlp”

Примечание. Мы учли наш предыдущий совет, указав суффикс & при определении констант. В данном случае (для данных конкретных значений) это было необязательно. Но, во-первых, мы подчеркнули, что формируются константы именно Long. А во-вторых, машинный код стал короче из-за отсутствия дополнительных преобразований.

Теперь поместите на форму две командные кнопки (cmdHelpAbout и cmdHelpSearch) и напишите для них следующий код. Измените путь к файлу Справки в соответствии со своими установками для Visual Basic.

Private Sub cmdHelpAbout_Click()

WinHelp Me.hwnd, HelpFile, HELP_HELPONHELP, &H0

End Sub

Private Sub cmdHelpSearch_Click()

WinHelpSearch Me.hwnd, HelpFile, HELP_PARTIALKEY, “option”

End Sub

Запустите полученный проект на выполнение (F5). Если вы щелкните кнопку cmdHelpAbout, то увидите раздел Справки об использовании справочной системы. Если же вы щелкните кнопку cmdHelpSearch, то на экране появится список элементов Справки по теме option.

Совет 151. Как дать возможность пользователям отменить выгрузку форм.

Существует множество различных способов выгрузить форму: щелкнуть кнопку Exit или соответствующую команду меню, щелкнуть кнопку со знаком X в верхнем правом углу формы, выбрать команду Close из всплывающего меню окна формы в верхнем левом углу. Можно даже произвести отмену выполнения программы из менеджера задач или перезагрузить компьютер.

Так или иначе, было бы полезно показать пользователям возможность отмены операции выгрузки формы, проводимой одним из вышеперечисленных способов. Для этого следует поместить код, осуществляющий проверку выгрузки формы, в событие QueryUnload для этой формы. Данное событие инициируется независимо от метода, используемого для выгрузки формы:

Private Sub Form_QueryUnload(Cancel As Integer, _

UnloadMode As Integer)

‘ универсальная проверка выгрузки формы

Dim strQuestion As String

Dim intAnswer As Integer

Dim aryMode As Variant

aryMode = Array(“vbFormControlMenu”, “vbFormCode”, _

“vbAppWindows”, “vbAppTaskManager”, “vbFormMDIForm”)

strQuestion = “Вы готовы выгрузить эту форму?”

intAnswer = MsgBox(strQuestion, vbQuestion + vbYesNo, _

aryMode(UnloadMode))

If intAnswer = vbNo Then Cancel = -1

End Sub

Совет 152. Создание “автоматического” поля текста

С помощью приведенной здесь программы вы можете создать поле текста (TextBox), аналогичное тем, которые реализованы в последних версиях Microsoft Excel или Internet Explorer. Иными словами, каждый раз, когда вы будете вводить какой-либо текст в это поле, первые буквы строки будут сравниваться с элементами невидимого списка, а затем программа сама определит, как следует дописать данную строку, и сделает это за вас.

Для реализации такого поля текста вначале добавьте окно списка к своей форме и установите его свойство Visible как False. В событии Form_Load заполним это окно списка некоторыми элементами. В реальном приложении добавление новых элементов списка следует осуществлять каждый раз, когда пользователь закончит ввод строки. Поместите на форму элемент управления TextBox и введите следующий код:

Option Explicit

#If Win32 Then ’32-разрядная версия VB

Private Const LB_FINDSTRING = &H18F

Private Declare Function SendMessage Lib “User32” _

Alias “SendMessageA” (ByVal hWnd As Long, ByVal _

wMsg As Long, ByVal wParam As Long, lParam As _

Any) As Long

#Else ‘ 16-разрядная версия VB

Private Const WM_USER = &H400

Private Const LB_FINDSTRING = (WM_USER + 16)

Private Declare Function SendMessage Lib _

“User” (ByVal hWnd As Integer, ByVal wMsg As Integer, _

ByVal wParam As Integer, lParam As Any) As Long

#End If

Private Sub Form_Load()

List1.AddItem “Апельсин”

List1.AddItem “Банан”

List1.AddItem “Яблоко”

List1.AddItem “Персик”

List1.AddItem “Ананас”

List1.AddItem “Авокадо”

End Sub

Private Sub Text1_Change()

Dim pos As Long

List1.ListIndex = SendMessage(List1.hWnd, _

LB_FINDSTRING, -1, ByVal CStr(Text1.Text))

If List1.ListIndex = -1 Then

pos = Text1.SelStart

Else

pos = Text1.SelStart

Text1.Text = List1

Text1.SelStart = pos

Text1.SelLength = Len(Text1.Text) — pos

End If

End Sub

Private Sub Text1_KeyDown(KeyCode As Integer, _

Shift As Integer)

On Error Resume Next

If KeyCode = 8 Then ‘ Backspace

If Text1.SelLength <> 0 Then

Text1.Text = Mid$(Text1, 1, Text1.SelStart — 1)

KeyCode = 0

End If

ElseIf KeyCode = 46 Then ‘ Del

If Text1.SelLength <> 0 And _

Text1.SelStart <> 0 Then

KeyCode = 0

End If

End If

End Sub

Теперь запустите созданный вами проект (F5) и попробуйте ввести какой-либо текст.

Совет 153. Управление кнопками Minimize/Maximize на MDI-форме

В отличие от других форм MDI-формы не имеют свойств MinButton и MaxButton, с помощью которых можно включать или отключать кнопки Minimize и Maximize на форме. Если добавить следующий код к событию Load родительской MDI-формы, то он отключит обе эти кнопки. Если же вы хотите отключить только одну из них, то поставьте знак комментария у соответствующей строки:

Private Sub MDIForm_Load()

Dim lWnd As Long

lWnd = GetWindowLong(Me.hWnd, GWL_STYLE)

lWnd = lWnd And Not (WS_MINIMIZEBOX)

lWnd = lWnd And Not (WS_MAXIMIZEBOX)

lWnd = SetWindowLong(Me.hWnd, GWL_STYLE, lWnd)

End Sub

Затем введите следующий код (который включает объявления необходимых API-функций) в текст самого BAS-модуля:

Option Explicit

‘ описание API-функций и констант

#If Win32 Then

Private Declare Function SetWindowLong Lib “user32” _

Alias “SetWindowLongA” (ByVal hWnd As Long, _

ByVal nIndex As Long, ByVal dwNewLong As Long) As Long

Private Declare Function GetWindowLong Lib “user32” _

Alias “GetWindowLongA” (ByVal hWnd As Long, _

ByVal nIndex As Long) As Long

#Else

Declare Function SetWindowLong Lib “User” (ByVal hWnd _

As Integer, ByVal nIndex As Integer, ByVal dwNewLong _

As Long) As Long

Declare Function GetWindowLong Lib “User” (ByVal hWnd _

As Integer, ByVal nIndex As Integer) As Long

#End If

Const WS_MINIMIZEBOX = &H20000

Const WS_MAXIMIZEBOX = &H10000

Const GWL_STYLE = (-16)

Совет 154. Как сделать видимыми кнопки Minimize/Maximize

В некоторых случаях бывает необходимо задать свойство BorderStyle какой-либо формы как Fixed Dialog, и тогда VB не выводит кнопки Minimize и Maximize в поле заголовка формы. Теперь, если задать для этой же формы свойства MinButton и MaxButton как True, команды Minimize и Maximize в контекстном меню формы станут видимыми. Однако сами кнопки на форме останутся по-прежнему невидимыми. Чтобы исправить эту ошибку, введите описание API-функций и констант из предыдущего совета и запишите следующий код в стандартный модуль:

Public Sub SetCaptionButtons(Frm As Form)

Dim lRet As Long

lRet = GetWindowLong(Frm.hWnd, GWL_STYLE)

SetWindowLong Frm.hWnd, GWL_STYLE, lRet Or _

WS_MINIMIZEBOX * (Abs(Frm.MinButton)) Or _

WS_MAXIMIZEBOX * (Abs(Frm.MaxButton))

End Sub

Теперь следует вызвать подпрограмму SetCaptionButtons из события Form_Load, передав ссылку на вашу форму.