Андрей Колесов
Ольга Павлова
Совет 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). Обратите внимание, что элементы управления La
bel и 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 как Da
ta1 (компонент 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, команды Minimi
ze и 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, передав ссылку на вашу форму.