Советы тем, кто программирует на VB & VBA
Совет 322. Получение даты последней коррекции файла
Совет 323. Преобразование кода цвета из DOS в Windows
Совет 324. Как отличить обычные элементы управления от компонентов массива
Совет 325. Преобразование числа в двоичное представление и наоборот
Листинг 1. Функции sBin$ и iBin% — представление числа в двоичной записи и наоборот
Совет 326. Функции с двухсторонним обращением
Совет 327. Преобразование величины размера памяти
Совет 328. Переключение вида заголовков элемента управления ListView
Совет 329. Генерация динамических HTML-страниц
Листинг 2. Процедура ShowRecordSetASP выводит содержимое заданного набора данных в виде таблицы
Совет 330. Как отлаживать ASP-скрипты
Совет 331. Как улучшить читаемость кода
Совет 332. Проверка на недействительные символы
Совет 333. Проверка дубликатности элементов списка
Совет 334. Как организовать выбор каталога
Совет 322. Получение даты последней коррекции файла
Для работы иногда полезно получить данные о дате создания или последней коррекции файла. Для этого можно использовать функцию FileDateTime(ИмяФайла), например:
Dim MyDateTime As Date MyDateTime = FileDateTime("D:\Calc.bas)
С помощью этой же функции можно определить время создания каталога, но следует иметь в виду, что имя каталога в данном случае не должно заканчиваться на обратную косую черту:
MyDateTime = FileDateTime("D:\TMP\") ' имя каталога задано неверно MyDateTime = FileDateTime("D:\TMP") ' имя каталога задано правильно
Но получить дату создания корневого каталога (то есть диска) с использование FileDateTime нельзя.
Совет 323. Преобразование кода цвета из DOS в Windows
Как известно, в стандартном варианте DOS использовалось 16 цветов, а сейчас в Windows их 16 777 216 (16M). Точнее говоря, такая палитра определяется не операционной системой, а техническими характеристиками мониторов.
Несмотря на такое мощное расширение состава палитры, для решения многих задач бывает удобнее воспользоваться ограниченным количеством цветов. К тому же порой необходимо точно воспроизвести в VB-цвета, которые использовались в DOS (например, при работе с QuickBasic).
В любом случае, возникает задача преобразования кодов цветов из DOS в Windows, которая, казалось бы, легко решается с помощью встроенной функции QBColor:
WindowsColor = QBColor(DosColor)
Однако небольшое исследование показывает, что такой вариант представляет собой весьма приблизительное решение.
С помощью следующей конструкции:
For i = 0 To 16 Debug.Print i; QBColor(i) Next
можно получить таблицу соответствия кодов DOS и Windows.
DOS (QuickBasic) |
Windows (Visual Basic) |
||
---|---|---|---|
Код: десятичный (двоичный) |
Название (точный перевод с английского) |
Код шестнадцатеричный |
Название (перевод Microsoft) |
0 (0000) |
Черный |
000000 |
Черный |
1 (0001) |
Синий |
800000 |
Темно-синий |
2 (0010) |
Зеленый |
008000 |
Темно-зеленый |
3 (0011) |
Циан (Cyan) |
808000 |
Бирюзовый |
4 (0100) |
Красный |
000080 |
Малиновый |
5 (0101) |
Сиреневый |
800080 |
Сиреневый |
6 (0110) |
Коричневый |
008080 |
Оливковый |
7 (0111) |
Белый |
C0C0C0 |
Светло-серый |
8 (1000) |
Серый |
808080 |
Темно-серый |
9 (1001) |
Светло-синий |
FF0000 |
Синий |
10 (1010) |
Светло-зеленый |
00FF00 |
Зеленый |
11 (1011) |
Светлый циан |
FFFF00 |
Голубой |
12 (1100) |
Светло-красный |
0000FF |
Красный |
13 (1101) |
Светло-сиреневый |
FF00FF |
Розовый |
14 (1110) |
Желтый |
00FFFF |
Желтый |
15 (1111) |
Ярко-белый |
FFFFFF |
Белый |
Здесь хорошо видны принципы кодирования цветов в DOS и Windows. В обоих случаях цвет задается комбинацией красного, зеленого и синего (Red, Green, Blue). Но в первом варианте используется лишь один разряд (то есть две градации цвета — есть или нет данной составляющей), а во втором — целый байт (256 градаций). В DOS увеличение цветовой гаммы в два раза достигается наличием четвертого разряда, который задает нормальную или повышенную яркость (но не для каждого компонента, а для всей комбинации).
Казалось бы, на этом можно поставить точку, но из таблицы видно некоторое несоответствие в логике преобразования кода. Почти для всех цветов замена кода выполняется по такому правилу: для нормальной яркости двоичный разряд со значением 1 заменяется на &h80, для повышенной яркости — на &hFF. Но имеется также исключение из этого правила для цветов 7 и 8. Следует обратить внимание и на разные названия, например одинаковых цветов, которые используются в английском и русском вариантах Help. Оказывается, это не неточность перевода, а отражение реального несоответствия цветов в DOS и в Windows после преобразования кодов с помощью QBColor.
Визуальное сравнение изображений палитры из 16 цветов, полученных в среде QB и VB, показало заметное расхождение в красках, поэтому мы выполнили следующий эксперимент:
импортировали графический файл с палитрой QB в VB и прочитали RGB-код каждого цвета. По результатам замеров мы сделали свою функцию преобразования кодов цветов из DOS в Windows:
Public Function QBColorMy(DosColor%) As Long ' Преобразование кода из QB (16 цветов) в VB (RGB - 16М) ' Значения RGB-кодов получены на основе исследования ' 16-цветной палитры, полученной в QB/DOS Select Case DosColor Case 0: QBColorMy = &H0 ' Black Case 1: QBColorMy = &HAA0000 ' Blue Case 2: QBColorMy = &HAA00& ' Green Case 3: QBColorMy = &HAAAA00 ' Cyan Case 4: QBColorMy = &HAA ' Red Case 5: QBColorMy = &HAA00AA ' Magenta Case 6: QBColorMy = &H55AA& ' Brown (! нарушение алгоритма) Case 7: QBColorMy = &HAAAAAAA ' White Case 8: QBColorMy = &H555555 ' Gray Case 9: QBColorMy = &HFF5555 ' Light Blue Case 10: QBColorMy = &H55FF55 ' Light Green Case 11: QBColorMy = &HFFFF55 ' Light Cyan Case 12: QBColorMy = &H5555FF ' Light Red Case 13: QBColorMy = &HFF55FF ' Light Magenta Case 14: QBColorMy = &H55FFFF ' Yellow Case 15: QBColorMy = &HFFFFFF ' Bright White End Select End FunctionЗдесь следует обратить внимание на следующие моменты.
- Для нормальной яркости двоичный разряд со значением 1 меняется на &hAA, а для повышенной яркости — на &hFF. Но в последнем случае нулевой двоичный разряд меняется на шестнадцатиричное 55 (в двоичном варианте это выглядит как 01010101).
- Как ни странно, код 6 опять отличается от ожидаемой величины (было бы логично увидеть &hAAAA). При этом нужно отметить, что этот код действительно Brown (коричневый) — именно так он называется в QB. В документации VB он именуется как Yellow, что явно не соответствует действительности.
Совет 324. Как отличить обычные элементы управления от компонентов массива
Для того чтобы найти программным образом на форме элемента управления определенного типа, например TextBox, можно воспользоваться следующим кодом:
Dim ctl As Control For Each ctl In Controls If TypeName(ctl) = "TextBox" Then MsgBox ctl.Name & " - это элемент управления TextBox." End If Next ctl
Но данный вариант не может отличить обычные элементы управления от массивов. Для решения этой проблемы следует воспользоваться такой конструкцией:
TypeName(Controls(ctl.Name))
Для обычных элеменов управления она будет выдавать «TextBox», а для компонентов массива — «Object». Программа, которая будет выдавать более точную информацию, может выглядеть следующим образом:
Dim ctl As Control For Each ctl In Controls ' MsgBox TypeName(ctl) If TypeName(ctl) = "TextBox" Then If TypeName(Controls(ctl.Name)) = "TextBox" Then MsgBox ctl.Name & " - обычный TextBox" Else MsgBox "Элемент массива" & ctl.Name & _ "/ Индекс = " & ctl.Index End If End If Next ctl
Совет 325. Преобразование числа в двоичное представление и наоборот
В VB (и QB) имеются функции для представления целого числа в символьном виде в восьмеричной (Oct) и шестнадцатеричной (Hex) системах счисления:
Print Hex(59) ' будет напечатано 3B Print Oct(59) ' будет напечатано 73
Соответственно можно также использовать специальный вид констант для задания чисел в восьмеричном или шестнадцатеричном виде:
iValue = &H3B ' = 59 (приставка &h) iValue = &O73 ' = 59 (приставка &o — второй символ буква "O")
Еще раз хотим обратить внимание на нюанс, связанный с преобразованием типов Integer в Long и наоборот. Проблема заключается в том, что мы имеем дело со знаковыми числами, в которых старший, знаковый разряд должен обрабатываться особым образом. Покажем эту ситуацию на примере шестнадцатеричной константы.
Правило формирования константы &H таково: если в ней указано поле приставки менее пяти символов, то формируется переменная Integer, в противном случае — Long. Если необходимо сразу получить тип Long, то константа должна иметь в конце суффикс &.
Соответственно, вы можете получить такой неожиданный результат:
Const MyConst As Long = &hFFFF Print Hex$(MyConst) ' будет напечатано FFFFFFFF
Это объясняется тем, что &hFFFF соответствует значению — 1 (Integer). При переводе этого числа в тип Long получается код &hFFFFFFFF. Если же требуется сформировать число типа Long, в котором два старшие байта были нулевыми, то следует указать суффикс &:
Const MyConst As Long = &hFFFF& Print Hex$(MyConst) ' будет напечатано FFFF
Если число задано в виде символьной записи, то его можно преобразовать следующим образом:
MyStr$ = "3B" MyValue& = Val ("&h" & MyStr$)
Однако довольно часто бывает полезно представлять числа в двоичной системе счисления. Хотя шестнадцатеричный и восьмеричный вид чисел хорошо показывать двоичную структуру числа, все же такое преобразование требует внимания и не гарантирует от ошибок. Даже опытному программисту может понадобиться некоторое время, чтобы записать число, в котором 7-й, 8-й и 10-й разряды (считая справа! — 1011000000) были бы равны единице, а остальные — нулю.
Для решения этой задачи можно использовать функции iBin% и sBin$, приведенные на листинге 1:
Print Hex(iBin%("1011000000") ' будет напечатано 2C0 Print sBin$(34) 'будет напечатано 100010
Здесь нужно обратить внимание на то, что значительная часть кода связана с особой обработкой знакового разряда. Мы реализовали эти функции для целой типа Integer, учитывая два обстоятельства.
- Именно такой диапазон чисел чаще всего используется для представления в двоичный формат (16 разрядов — это довольно много для визуального восприятия).
- Для Long необходимо написать такие функции, чтобы они же по умолчанию работали и для Integer. Все это — из-за автоматического преобразования типов данных при обращении к процедурам и проблем такого преобразования, описанных выше. В этой связи приведем такой пример: строка "1000000000000000" (15 нулей справа) в терминах Integer равна –32768, а Long –32768. Таким образом, для двух типов данных необходимы разные процедуры. (В VB 7 обещано появление Overloading — возможности использования одного и того же идентификатора для обозначения разных процедур. Выбор нужной процедуры зависит от типа аргумента.)
Листинг 1. Функции sBin$ и iBin% — представление числа в двоичной записи и наоборот
Public Function sBin$(Source%) ' Преобразование целого числа в ' символьное представление в двоичной системе счисления Dim StrBin$, lValue& ' эта конструкция нужна для учета знакового разряда If Source < 0 Then 'знаковый разряд равен единице lValue = (Source And &H7FFF) Or &H8000& Else: lValue = Source End If Do While lValue > 0 StrBin = Chr((lValue Mod 2) + 48) + StrBin lValue = lValue \ 2 ' деление нацело Loop If StrBin = "" Then sBin = "0" Else sBin = StrBin End Function Public Function iBin%(Source$) ' Преобразование символьной строки ' (числа в двоичной системе счисления) в целое число Dim lValue&, i% For i = 1 To Len(Source) If Mid(Source, i, 1) <> "0" Then lValue = lValue + 2 ^ (Len(Source) - i) End If Next ' эта конструкция нужна для учета знакового разряда If (lValue And &H8000) > 0 Then iBin = (lValue And &H7FFF) Or &H8000 Else: iBin = lValue End If End Function
Совет 326. Функции с двухсторонним обращением
Довольно часто встречаются случаи парных процедур, которые выполняют связанные как бы прямые и обратные операции. Для удобства работы с ними полезно обозначить один из них идентификатором, при этом конкретная операция должна определяться формой записи:
y = MyFunc(x) MyFunc(y) = x
Примером такого решения является популярная функция Mid:
Sym$ = Mid$(MyStr$, i, k) ' выбирает из строки фрагмент Mid$(MyStr$, i, k) = Sym$ ' вставляет фрагмент строку
Обратите внимание, что в первом случае конструкция называется функцией, а во втором — оператором (Statement). Если бы мы писали второй вариант сами, то данная процедура имела бы тип Sub, а ее запись выглядела бы так:
Call MyMid(MyStr$, i, k, Sym$)
Однако если использовать процедуры типа Property, то можно создать пару таких связанных функций одним именем. Например, мы хотим сделать две процедуры, одна из которых должна выбирать младший байт из длинной переменной, а другая — вставить его обратно:
Public Property Get LowByte(LongVar As Long) As Byte ' прочитать младший байт переменной LongVar LowByte = LongVar And &HFF End Property Public Property Let LowByte(LongVar As Long, ByVal NewByte As Byte) ' Записать в младший байт переменной LongVar новое значение NewByte LongVar = (LongVar And &HFFFFFF00) Or NewByte End Property
Обращение к ним будет выглядеть следующим образом:
Dim LongVar$, ByteVar As Byte LongVar = &h12345678 ByteVar = LowByte(LongVar) Print Hex(ByteVar) ' напечатано 78 ByteVar = &hF1 LowByte(LongVar) = ByteVar Print Hex(LongVar) ' напечатано 123456F1
На самом деле такие связанные функции представляют собой созданные выше iBin% и sBin$ (см. Совет 325). Их можно легко преобразовать в пару процедур Property:
Public Property Get MyBin(Source%) As String ' Число Source% в символьную запись двоичного кода MyBin = sBin$(Source%) End Property Public Property Let MyBin(Source%, ByVal NewStr As String) ' Символьную запись двоичного кода NewStr$ в число Source% Source% = iBin%(NewStr) End Property
В этом случае обращение к ним будет выглядеть следующим образом:
MyBin(intBin) = "1011000" Print Hex(intBin) ' напечатано 58 StrBin$ = MyBin(intBin) MsgBox (StrBin) ' напечатано 101100
Совет 327. Преобразование величины размера памяти
В составе Internet Explorer имеется библиотека SHLWAPI.DLL (Shell Windowing API), в которую включена функция, преобразующая число байтов в символьную строку типа "1,41 KB" или "1,32 MB". Обращение к этой функции выглядит следующим образом:
Private Declare Function StrFormatByteSize _ Lib "shlwapi" Alias "StrFormatByteSizeA" _ (ByVal dw As Long, ByVal pszBuf As String, _ ByVal cchbuf As Long) As Long Public Function FormatKB(ByVal Amount As Long) As String Dim Buffer As String, i% Buffer = Space$(255) ' резервируем буфер Call StrFormatByteSize(Amount, Buffer, Len(Buffer)) i = InStr(Buffer, vbNullChar) If i > 1 Then FormatKB = Left$(Buffer, i - 1) End Function
Совет 328. Переключение вида заголовков элемента управления ListView
С помощью приведенного ниже кода можно переключать представление заголовков элемента управления ListView из плоского вида в объемное и наоборот:
Private Declare Function GetWindowLong _ Lib "user32" Alias "GetWindowLongA" _ (ByVal hwnd As Long, ByVal nIndex As Long) As Long 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 SendMessage _ Lib "user32" Alias "SendMessageA" _ (ByVal hwnd As Long, ByVal wMsg As Long, _ ByVal wParam As Long, lParam As Any) As Long Private Const GWL_STYLE = -16 Private Const LVM_FIRST = &H1000 Private Const LVM_GETHEADER = LVM_FIRST + 31 Private Const HDS_BUTTONS = &H2 Private Sub Command1_Click() Call ToggleHeader(ListView1.hwnd) End Sub Public Sub ToggleHeader(lsvhWnd As Long) Dim hHeader As Long, lStyle As Long hHeader = SendMessage(lsvhWnd, LVM_GETHEADER, 0, ByVal 0&) lStyle = GetWindowLong(hHeader, GWL_STYLE) SetWindowLong hHeader, GWL_STYLE, lStyle Xor HDS_BUTTONS End Sub
Совет 329. Генерация динамических HTML-страниц
Полтора года назад мы опубликовали (КомпьютерПресс 5’99) две статьи, посвященные созданию Web- и DHTML-приложений. Нам удалось создать вполне работоспособные примеры, однако технология разработки показалась нам весьма запутанной. Возможно, создавать динамические HTML-страницы проще с помощью знакомой ASP-технологии? С помощью VB это делается достаточно просто. Ниже приведены содержание ASP-страницы, которое можно сделать с помощью самого простого текстового редактора, и код процедуры, которая используется для генерации HTML-кода.
Тестовая Active Server Page:
<html> <head></head> <body> <h2>Далее будет приведена некоторая информация пользователя </h2> <% Dim objRSServer Set objRSServer = Server.CreateObject("TestASP.Htmlcrt") objRSServer.WriteSomeThing Response, "Привет всем!" %> </body> </html>
Программный код (проект TestASP.DLL, модуль класса Htmlcrt):
Public Sub WriteSomeThing _ (ASPResponse As ASPTypeLibrary.Response, SomeText$) With ASPResponse .Write "<font size=4 color='#cc3366'>" .Write SomeText$ .Write "</font>" End With End Sub
В данном случае все выглядит достаточно понятным. Сначала мы создали ActiveX DLL с именем TestASP, в модуле класса Htmlcrt которой написали процедуру WhiteSomeThing (она превратилась, таким образом, в метод объекта TestASP.Htmlcrl). При этом для нашей DLL мы сделали ссылку на библиотеку Microsoft Active Server Page Object Library, чтобы получить доступ к объекту Response. Далее мы написали ASP-страницу с тривиальным скриптом — сначала создается объект TestASP.Htmlcrt. Затем — следует обращение к его методу WhiteSomeThing с передачей объекта Response и некоторого набора параметров. В данном случае передается текстовая строка, которая выводится в HTML-страницу.
Напомним, что обращение к ASP-странице должно выполняться через Internet Infomation Server (NT) или Personal Web Server (Windows 9x). В последнем случае ASP-файл должен быть помещен в одну из папок сервера, обращение к которому из браузера выполняется по адресу http://localhost/MyDir/Test.ASP.
Понятно, что никакого практического применения подпрограмма WhiteSomeThing не имеет, поэтому предлагаем вашему вниманию процедуру ShowRecordSetASP (листинг 2), которая выводит на HTML-страницу содержимое заданного набора данных. Теперь, сделав в ASP обращение к методу ShowRecordSetASP:
objRSServer. ShowRecordSet(Response, _ "Provider=Microsoft.Jet.OLEDB.3.51;" & _ "Persist Security Info=False;" & _ "Data Source=C:\vb-db\nwind.mdb", _ "Select FirstName +' ' + LastName as Имя_Фамилия," & _ "Title as Должность, " & _ "BirthDate as ДатаРождения, HomePhone as ДомашнийТелефон" & _ " from Employees Order by LastName, FirstName", _ "Список сотрудников"
с помощью браузера можно будет посмотреть полезную информацию, хранимую на Web-сервере (рис. 1).
Листинг 2. Процедура ShowRecordSetASP выводит содержимое заданного набора данных в виде таблицы
Public Sub ShowRecordSetASP _ (ASPResponse As ASPTypeLibrary.Response, _ strConnectString$, strSQL$, strHeading$) ' Данная процедура: ' 1. Устанавливает соединение с источником данных ADO ' (нужна ссылка на MS ActiveX Data Objects 2.x Library ' 2. Формирует набор данных ' 3. Выводит содержимое набора данных в виде таблицы ' в HTML-коде, который записывается в ASP-страницу ' с помощью объекта Response ' Необходима ссылка на библиотеку MS Active Server Pages Object ' Должна быть записана в ActiveX DLL '----------------------------------- Dim cnn As ADODB.Connection Dim rs As ADODB.Recordset Dim fldField As ADODB.Field Dim iCount%, RowBGColor$ Set cnn = New ADODB.Connection Set rs = New ADODB.Recordset cnn.Open strConnectString$ Set rs = cnn.Execute(strSQL) ' ' формирование таблицы With ASPResponse .Write "<table cellpadding=3 border=0><tr>" .Write "<tr><td width=100% height=18" & _ " colspan = " & rs.Fields.Count & _ " bgcolor='#666699'>" & _ "<p align=Center><font size=4 color='#ffffff'>" & _ strHeading & "</font></tr>" 'вывод заголовков колонок -- название полей .Write "<tr>" For Each fldField In rs.Fields .Write "<td bgcolor='#8f9fe9'>" & fldField.Name & "</td>" Next .Write "</tr>" ' вывод записей в виде строк таблицы iCount = 1 Do While Not rs.EOF .Write "<tr>" ' чередование цветов строк If iCount > 0 Then RowBGColor = "'#c9c9c9'" Else: RowBGColor = "'#f5f5f5'" End If For Each fldField In rs.Fields .Write "<td bgcolor=" & RowBGColor & ">" & _ fldField.Value & "</td>" Next .Write "</tr>" rs.MoveNext iCount = iCount Xor 1 Loop .Write "</table>" End With rs.Close cnn.Close Set rs = Nothing Set cnn = Nothing Close #1 End Sub
Совет 330. Как отлаживать ASP-скрипты
Отладить процедуры для создания ASP-страниц непросто: необходимо писать код, потом создавать DLL, затем обращаться из браузера к ASP. Увидев ошибки, найти их причину, исправить в VB программу, создать DLL...
С этой проблемой мы столкнулись при разработке довольно простой процедуры ShowRecordSetASP (см. совет 329), решив ее следующим образом.
Мы сделали стандартный VB-проект, в котором создали процедуру ShowRecordSet такого вида:
Public Sub ShowRecordSet(strConnectString$, strSQL$, strHeading$) ... Nf=FreeFile Open "d:\file.htm" For Output As #Nf Print #Nf, "<html><head></head><body>" ' формирование таблицы Print #Nf, "<table cellpadding=3 border=0><tr>" ...' мы опустили код, который приведен в ShowRecordSet Print #Nf, "</body></html>" Close #Nf End Sub
Так выглядит процедура, формирующая нужный фрагмент кода в виде готового HTM-файла, который сразу можно просматривать браузером. Затем после отладки мы убрали строчки:
Open "d:\file.htm" For Output As #Nf Print #Nf, "<html><head></head><body>" ... Print #Nf, "</body></html>" Close #Nf
и в режиме Replace заменили «Print #1,» на «.Write», а потом давили логические скобки:
With ASPResponse ... End With
Существует еще один вариант разработки кода для ASP-страниц. Делаем такую процедуру, которая формирует автономный HTM-файл:
Public Sub MyShowRecordSet(strConnectString$, strSQL$, strHeading$) Nf=FreeFile Open "с:\file.htm" For Output As #Nf Print #Nf, "<html><head></head><body>" Call MyShowRecordSetMain(strConnectString$, strSQL$, strHeading$) Print #Nf, "</body></html>" Close #Nf
Подпрограмма MyShowRecordSetMain записывает нужный код в HTM-файл.
Для ASP пишем еще пару подпрограмм:
Public Sub MyShowRecordSetASP _ (ASPResponse As ASPTypeLibrary.Response, _ strConnectString$, strSQL$, strHeading$) Nf=FreeFile FileName$ = "с:\TempFile.htm" ' формируем промежуточный текстовый файл Open FileName$ For Output As #Nf Call MyShowRecordSetMain(strConnectString$, strSQL$, strHeading$) Close #Nf ' переписываем его в ASP Call CopyFromFileToASP(FileName$, ASPResponse) Kill FileName$ ' удалить временный файл End Sub
Public Sub CopyFromFileToASP(FileName$, _ ASPResponse As ASPTypeLibrary.Response) ' переписываем содержимое текстового файла в ASP Dim a$ nf= FreeFile Open FileName$ For Input As #Nf Do while not eof(Nf) ' переписываем Line Input #Nf, a$ ASPResponse.Write a$ Loop Сlose #Nf End Sub
Совет 331. Как улучшить читаемость кода
Качество оформления программного кода — один из важных факторов, влияющих на эффективность разработки приложений. Прежде, когда программист имел дело с бумажными распечатками кода, считалось, что одна процедура не должна превышать 60-90 строк кода (1-1,5 страницы). Такой объем текста человек может охватить одним взглядом и, таким образом, легко воспринять логику работы данного фрагмента. С внедрением персональных компьютеров необходимость в бумажных листингах отпала, наступило время интерактивной работы с кодом с помощью экрана монитора.
В текстовом режиме экрана (когда мы работали с QuickBasic) мы придерживались правила, что объем процедуры не должен превышать 40-50 строк, то есть занимать не более двух экранных страниц, которые можно легко перелистывать «туда-сюда». Теперь, при работе в графическом режиме программист может гибко управлять размером шрифта и окна с кодом. Но чаще всего приведенные выше пределы числа строк можно рекомендовать и в этом случае.
С размером текста процедур непосредственно связан вопрос навигации между отдельными компонентами VB-проекта. Следует открыто признать, что навигация в VB оставляет желать лучшего. К сожалению, сегодня многие VB-программисты даже не представляют себе, что этот процесс можно сделать более удобным. Довольно удачным примером этого может служить окно выбора нужной процедуры, которое было реализовано в VB 3.0 (рис. 2). Оно гораздо удобнее существующего сегодня Object Browser хотя бы потому, что для модуля формы показываются все реально сформированные процедуры.
Из сказанного выше понятно, что повышение удобства читаемости программного кода связано, в частности, с уменьшением числа строк текста. В этой связи хотелось бы напомнить о возможностях синтаксиса VB, о которых, как выясняется, многие программисты даже не подозревают.
- Возможность написания нескольких операторов в одной строке. Для этого используется
разделитель операторов в виде двоеточия. Согласитесь, что такой код:
A = 1: C$ = "Строковая переменная": D = #09/30/2000#
или:
Select Case MyVar Case 0: OtherVar = 1 Case 1: OtherVar = 45 End Select
который читается достаточно хорошо, но при этом занимает меньше строк.
- Оператор If ... Then ... Else может писать в одну строку, причем в последнем
случае не нужно использовать закрывающую скобку End If. Вот несколько примеров
использования этой возможности:
Вместо
If k = 0 Then A =1 End If
можно написать
If k = 0 then A = 1
Вместо:
If k = 0 Then A = 1 B = 2 Else A = 2 B = 1 End If
можно написать:
If k = 0 Then A = 1: B = 2 Else A = 2: B = 1
или:
If k = 0 Then A = 1: B = 2 _ Else A = 2: B = 1
Совет 332. Проверка на недействительные символы
С этой задачей приходится сталкиваться довольно часто. Самый простой пример — ввод строки, которая должна содержать число. Ранее мы уже приводили простые процедуры собственной разработки, но сейчас хотим показать возможности библиотеки SHLWAPI.DLL, которая входит в состав Internet Explorer:
Declare Function StrSpn Lib "SHLWAPI" Alias _ "StrSpnW" (ByVal psz As Long, ByVal pszSet As Long) As Long Public Function IsDecimal (ByVal sString As String) As Boolean Const DECIMAL_NUM As String = "0123456789" Dim iPos iPos = StrSpn (StrPtr(sString), StrPtr(DECIMAL_NUM) ' если возвращается значение, не равное длине исходной строки, ' то значит найдены символы, не являющиеся цифрами IsDecimal = (iPos = Len(sString)) End Function
Совет 333. Проверка дубликатности элементов списка
При добавлении нового элемента списка полезно проверить, не содержит ли он уже такие строки. Это можно сделать с помощью следующего кода:
Declare Function SendMessageByString _ Lib "User32" Alias "SendMessageA _ (ByVal hWnd As Long, _ ByVal wMsg As Long, _ ByVal wParam As Long, _ ByVal lParam As String) As Long ... NewListItem$ = "Новый элемент списка" If SendMessageByString _ (List1.hWnd, &H1A2, -1, NewListItem$) = - 1 then List1. AddItem ' добавить новый элемент Else MsgBox NewListItem$ & "- такой элемент уже есть в списке" End If
Совет 334. Как организовать выбор каталога
Мы уже приводили пример решения этой задачи с помощью обращения к DLL-функциям (см. совет 205). Наш читатель Сергей Мичковский предложил более компактный вариант решения этой задачи:
Sub Main() Dim oShell As New Shell32.Shell Dim oFolder As Shell32.FolderSet oFolder = oShell.BrowseForFolder _ (UserControl.hWnd, "Выберите компьютер и нажмите кнопку OK", _ &H1000, &H12) MsgBox oFolder.Title End Sub
КомпьютерПресс 11'2000