Visual2000 · Архив статей А.Колесова & О.Павловой
Андрей Колесов, Ольга Павлова
© 1998, Андрей Колесов, Ольга ПавловаКак известно, положение указателя (движка) линейки прокрутки характеризуется значением свойства 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)
Теперь в обоих случаях верхнее положение указателя будет соответствовать максимальному значению заданного диапазона данных.
Предыдущий совет поможет нам в создании элемента управления, представляющего собой числовой счетчик, внешний вид которого приведен здесь:
Как легко догадаться, на самом деле это два стоящих рядом элемента управления Text и VScroll с одинаковыми значениями свойств Top и Height, причем последний подобран таким образом, что указатель линейки прокрутки стал невидимым. Теперь, щелкая стрелки линейки, мы увидим автоматическое изменение числового значения с нужным нам шагом (SmallChange). Для практического использования такого счетчика следует также установить его начальное значение, например, при загрузке формы:
Sub Form_Load() VScroll1.Value = VScroll1.Max Call VScroll1_Change() End Sub
Порой бывает полезным узнать физическую длину текстовой строки (размер в единицах длины с учетом, в частности, размера и типа шрифта) при выводе ее на экран. В принципе для этого можно воспользоваться функциями 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 точки принтера. В 1 логическом сантиметре содержится приблизительно 567 твипов, а в 1 логическом дюйме - 1440 твипов. Логический сантиметр (дюйм) - это длина элемента экрана, равная 1 сантиметру (дюйму) при печати.
Теперь, если вы захотите выяснить протяженность какого-либо текста, просто вызовите данную функцию с текстовой строкой в качестве параметра. Проиллюстрируем это на следующем примере. Поместим на форму еще один элемент управления - поле текста. Мы хотим выяснить, какую он должен иметь ширину, чтобы целиком вмещать строковую переменную a$ (у текстового поля нет свойства AutoSize). Обратите внимание, что элементы управления Label и TextBox содержат разное количество символов и при равной ширине позволяют записывать различное число видимых символов. Поэтому мы добавляем два пробела к строке A$:
Private Sub Form_Load() txt1$ = "Исходный текст" Text1.Width = TextExtent(" " & txt1$) Text1.Text = txt1$ End Sub
Существует простой способ добавления различных пользовательских диалоговых окон ко всем ячейкам элемента управления 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 для осуществления ввода. Если хотите, то можете заменить этот запрос на любое другое пользовательское диалоговое окно.
Уже довольно давно, в Совете 80 мы обращали ваше внимание на специфику работы с переменными типа 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 - то, что нам и нужно было получить. Причем машинный код этого фрагмента стал компактнее из-за отсутствия дополнительных внутренних преобразований данных.
Ряд функций 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.
Существует множество различных способов выгрузить форму: щелкнуть кнопку 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
С помощью приведенной здесь программы вы можете создать поле текста (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) и попробуйте ввести какой-либо текст.
В отличие от других форм 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)
В некоторых случаях бывает необходимо задать свойство 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, передав ссылку на вашу форму.