Советы тем, кто программирует на VB & VBA
Совет 285. Как получить значения цветовой палитры
Совет 286. Будьте внимательны при использовании оператора Debug.Print
Совет 287. Как упростить математические вычисления в VBA
Совет 288. Как вычислить интервал между двумя датами, измеряемый в разных единицах
Совет 289. Как правильно прочитать имя каталога
Совет 290. Как заполнить поля электронного сообщения
Совет 291. Как открыть VB-файл в Notepad или в любом другом текстовом редакторе
Совет 292. Как ускорить операцию деления чисел с плавающей запятой
Совет 293. Как ускорить выполнение операций ввода-вывода для файлов
Совет 294. Как создать дополнение, закрывающее все окна проекта
Совет 295. Используйте ключевое слово WithEvents для связи форм MDI и MDIChild
Совет 296. Помните об операторе Option Base
Совет 297. Экспорт таблиц в виде текстовых файлов
Совет 285. Как получить значения цветовой палитры
В состав VB входит удобное средство преобразования отдельных значений Red, Green и Blue в одно цветовое значение типа Long – это функция RGB. К сожалению, VB не позволяет проводить обратное преобразование, но вы можете получить конкретные цветовые значения из шестнадцатеричного представления значения типа Long, создаваемого функцией RGB. Для этого создадим следующую функцию и поместим ее в стандартном модуле программы:
Public Type RGB_Type R As Long G As Long B As Long End Type Public Function ToRGB(ByVal Color _ As Long) As RGB_Type ' Dim ColorStr As String ColorStr = Right$(“000000” & _ Hex$(Color), 6) With ToRGB .R = Val(“&h” & Right$(ColorStr, 2)) .G = Val(“&h” & Mid$(ColorStr, 3, 2)) .B = Val(“&h” & Left$(ColorStr, 2)) End With End Function
Чтобы воспользоваться данной функцией, поместите в форму какое-либо изображение, задав необходимое имя в свойстве Picture формы, а затем введите такой код:
Private Sub Form_MouseUp(Button _ As Integer, Shift As Integer, _ X As Single, Y As Single) ' Dim RGB_Point As RGB_Type RGB_Point = ToRGB(Point(X, Y)) With RGB_Point Me.Caption = “R = “ & .R & _ “ G = “ & .G & “ B = “ & .B End With End Sub
Запустите программу на выполнение. Щелкая мышью на различных частях изображения, вы будете видеть в заголовке формы соответствующие значения RGB. Обратите внимание, что при работе в VB3 вам нужно получать эти значения по отдельности, поскольку VB не поддерживал возвращение заданных пользователем типов данных до версии VB4. Подобное преобразование можно осуществить более быстрым способом, если воспользоваться командой LSet, которая копирует содержимое одного заданного пользователем типа данных (user-defined type, udt) в другой. Для этого заменим функцию, находящуюся в стандартном модуле программы, на такую:
Public Type RGB_Type R As Byte G As Byte B As Byte Filler As Byte End Type Private Type RGB_Full_Type lngRGB As Long End Type Public Function ToRGB(ByVal _ vlngColor As Long) As RGB_Type ' Dim udtRGBFull As RGB_Full_Type udtRGBFull.lngRGB = vlngColor LSet ToRGB = udtRGBFull End Function
Совет 286. Будьте внимательны при использовании оператора Debug.Print
Вопреки распространенному мнению, операторы Debug.Print не всегда удаляются из исполняемых файлов. Продемонстрируем, например, такой случай. Создайте новый проект, поместите на форму командную кнопку Command1 и напишите для нее такой код:
Private Sub Command1_Click() Debug.Print DebugTime End Sub Public Function DebugTime() MsgBox “Привет!” End Function
Скомпилируйте программу, запустите ее на выполнение и щелкните командную кнопку. На экране, как ни удивительно, появится окно сообщения. Это, конечно же, искусственная ситуация, но можно легко представить себе случаи, когда оператор Debug.Print используется для печати возвращаемого значения функции. Если переменные передаются как параметры ByRef и если функция изменяет значения этих переменных, то подобная ошибка распространится и на исполняемый файл, а обнаружить ее будет крайне трудно.
Таким образом, сформулируем основные выводы:
- Передавайте параметры с помощью ключевого слова ByVal, за исключением тех случаев, когда вы уверены, что не будете изменять их, или когда вы применяете их в качестве «выходных» параметров.
- Соблюдайте осторожность при использовании оператора Debug.Print. Он может выполнять больше действий, чем вы думаете.
Совет 287. Как упростить математические вычисления в VBA
Существует возможность упростить некоторые вычислительные процедуры в Excel, а также добавить в Word отсутствующие в нем функции. К сожалению, большинство учебных пособий по VBA в Office 97 предназначены для изучения нематематических функциональных возможностей пакетов, например форматирования текста или вывода графических изображений. Отсутствие необходимой документации ставит в затруднительное положение тех, кто хочет использовать макросы для решения своих математических задач, поскольку обработка числовых значений в ячейке таблицы Word происходит иначе, чем в ячейке электронной таблицы Excel. Потратив некоторое время, вы, конечно же, найдете необходимое описание синтаксиса математических функций в справочной системе Word или Excel. Кроме того, можно воспользоваться руководством Microsoft Office 97: Visual Basic Programmer's Guide, изданным корпорацией Microsoft (русская версия была выпущена «Русской Редакцией»), где рассматриваются практически все вопросы на данную тему.
Здесь мы приводим два примера, которые в явном виде демонстрируют разницу при работе в Word и Excel. Они вычисляют кубическую сумму значений первых восьми ячеек столбца 1 и помещают результат в девятую ячейку столбца 1:
Для Word VBA:
Sub ColumnMath() Dim x As Long, i As Long Dim myTable As Table Dim myStr As String x = 0 Set myTable = _ ActiveDocument.Tables(1) For i = 1 To 8 x = x + myTable.Cell _ (i, 1).Range.Calculate ^ 3 Next I myStr = Str(x) myTable.Cell(9, 1).Range. _ InsertAfter (myStr) End Sub
Для Excel VBA:
Sub ColumnMath() Dim x As Long, i As Long Sheets(“Sheet1”).Activate x = 0 For i = 1 To 8 x = x + Cells(i, 1).Value ^ 3 Next i Range(“a9”).Value = x End Sub
Данные вычисления могут быть проведены целиком в рамках функциональных возможностей Excel, однако для этого потребуется создать дополнительный столбец. А вот как выполнить подобные действия внутри Word? Попытаемся разобраться в этом. Напомним, что Word VBA имеет неинтуитивный синтаксис для обработки ячеек. Чтобы прочитать значение ячейки, следует использовать ключевое слово Calculate. А чтобы записать какую-либо величину в ячейку, надо вначале преобразовать ее в строковую переменную, а затем применить абсолютно неочевидное ключевое слово InsertAfter. Нумерация таблиц в документе Word осуществляется последовательно, поэтому в первом примере мы имеем дело с первой таблицей документа. Каждая таблица рабочей книги Excel представляет собой одну большую таблицу, поэтому во втором примере мы работаем с первой таблицей рабочей книги. Кроме того, во втором примере Range(“a9”) можно заменить на Cells(9, 1) или на один из других вариантов, требующих использования ключевого слова Range.
Совет 288. Как вычислить интервал между двумя датами, измеряемый в разных единицах
Как вы уже наверняка знаете, для вычисления интервала между двумя датами можно использовать встроенную в VB функцию DateDiff. Но работать с этой функцией нужно очень внимательно, с учетом входящих в нее ограничений.
Отгадайте такую загадку. Заданы две даты в виде переменных DateStart и DateFinish. Чтобы определить временной интервал между ними, мы написали такую процедуру:
Print “Интервал в годах = “; DateDiff(“yyyy”, DateStart, DateFinish) Print “Интервал в месяцах= “; DateDiff(“m”, DateStart, DateFinish) Print “Интервал в днях = “; DateDiff(“d”, DateStart, DateFinish) Print “Интервал в часах = “ ; DateDiff(“h”, DateStart, DateFinish) Print “Интервал в минутах = “; DateDiff(“n”, DateStart, DateFinish) Print “Интервал в секундах= “ ; DateDiff(“s”, DateStart, DateFinish)
И получили такой парадоксальный результат:
Интервал в годах = 1 Интервал в месяцах = 1 Интервал в днях = 1 Интервал в часах = 1 Интервал в минутах = 1 Интервал в секундах = 1
ВОПРОС. Почему так произошло и о каких датах шла речь?
ОТВЕТ. Дело в том, что функция DateDiff определяет временной интервал элементарно — в соответствии с заданным первым параметром просто отбрасывает значения даты «после этой точки». То есть если вы задали «день», то отбрасываются часы (0 часов), если месяц — дни (первое число месяца). В соответствии с этим алгоритмом получается, что между 31 мая 2000-го и 1 июня 2000-го в единицах «месяц» разница — один месяц (что в определенном смысле совершенно верно).
В нашем же примере исходные значения даты были равны
DateS = “31.12.2000 23:59:59” DateF = “01.01.2001”
Изменение показателя текущего момента привело к изменению минут, часов, суток, месяца и года (и даже века и тысячелетия). Очевидно, что самое точное определение интервала дается в данном случае в секундах (этой точности вполне достаточно для решения большинства бытовых и деловых проблем). Но как интерпретировать величину типа 12 345 678 сек? Конечно, желательно получить информацию в более привычных единицах — месяцах, днях, минутах.
В таких случаях вам поможет подпрограмма DateIntervals, позволяющая передавать две даты и свои собственные переменные для указанного интервала:
Public Sub DateIntervals(ByVal DateS _ As Date, ByVal DateF As Date, _ ParamArray Prams()) ' If UBound(Prams) < 0 Then Exit Sub ' Dim i As Long, itr As String ' ' Если не задан день, то считаем его “сегодняшним” If DateValue(DateS) = 0 Then _ DateS = DateS + DateValue(Now) If DateValue(DateF) = 0 Then _ DateF = DateF + DateValue(Now) ' For i = 0 To IIf(UBound(Prams) > 5, 5, UBound(Prams)) If Not IsMissing(Prams(i)) Then If i = 0 Then itr = “yyyy” Else itr = Mid$(“mdhns”, i, 1) End If Prams(i) = DateDiff(itr, DateS, DateF) If DateAdd(itr, Prams(i), DateS) <= DateF Then _ Prams(i) = Prams(i) - 1 DateS = DateAdd(itr, Prams(i), DateS) End If Next i End Sub
Подпрограмма DateIntervals возвращает наибольший полный интервал указанного вами типа (год, месяц, день, час, минута, секунда) между двумя датами. Например, чтобы получить интервал времени в часах и минутах между 09:00 и 17:15, передайте в подпрограмму эти две даты, а также две переменные, задающие размерность интервала. Используйте запятые, чтобы пропустить более крупные ненужные интервалы:
Dim Hours As Variant, Minutes As Variant Call DateIntervals(Now, “23.05.2000”, _ , , , Hours, Minutes) MsgBox “Часов = “ & Hours & _ “, Минут = “ & Minutes
Подпрограмма вернет число “Часов = 8, Минут = 15”.
Однако здесь следует обратить внимание на такой любопытный момент. Если вы выполните такое обращение к функции:
Call DateIntervals(“28.02.2000”, “01.03.2001”, Years, , Days) MsgBox Years & “ “ & Days Call DateIntervals(“29.02.2000”, “01.03.2001”, Years, , Days) MsgBox Years & “ “ & Days
то получите для разных начальных дат один и тот же результат — 1 год и 1 день. Казалось бы, в подпрограмме есть ошибка, но это не так. Данный парадокс объясняется неопределенностью интервала в один год — он может быть 365 и 366 дней (так же, как и в один месяц). Соответственно в первом случае «год» является високосным (366 дней), а во втором — обычным (365 дней). Чтобы представить эту ситуацию, вообразите, что ваш знакомый говорит 31 января: «Позвони мне ровно через месяц» (или 29 февраля 2000 года — «ровно через год»). Когда же будет эта точная дата намеченного звонка?
Отметим также, что алгоритм расчета интервала с использованием привычных единиц можно выполнить и по-другому — сначала определить интервал в секундах, а потом выделить из него минуты, часы и сутки (с месяцами и годами тут возникнут те же проблемы). Но он будет выглядеть не так изящно, как приведенная выше подпрограмма.
В процедуре DateIntervals хотелось бы обратить внимание еще на три используемые нами конструкции:
1. Для передачи возвращаемых параметров мы используем массив Param() с ключевым словом ParamArray. Такая конструкция применима только в конце списка аргументов подпрограммы и указывает, что данный аргумент является массивом типа Optional элементов типа Variant. С его помощью можно задать произвольное число аргументов. Кроме того, ParamArray нельзя использовать вместе с ключевыми словами ByVal, ByRef, или Optional.
В принципе, можно было бы просто зарезервировать в вызывающей подпрограмме массив Param (0 To 5) и использовать непосредственно его. Но в данном случае подпрограмма выполнила бы расчет для всех элементов этого массива. Применение ParamArray позволяет нам пропускать «ненужные» параметры. Например, в нашем обращении мы получим результат в полных годах и весь остаток интервала — в секундах:
Call DateIntervals(“28.02.2000”, “01.03.2001”, Years, , , , ,Secs)
2. Для выбора нужного значения из двух вариантов мы используем функцию:
MyVal = IIf(expr, truepart, falsepart)
которая равнозначна такому варианту:
If expr Then MyVal = truepart Else MyVal = falsepart End If
3. При коррекции временного интервала мы использовали такую конструкцию:
If DateAdd(itr, Prams(i), DateS) <= DateF Then _ Prams(i) = Prams(i) - 1
Любители хитроумных преобразований данных могли бы предложить более «изящный» вариант:
Prams(i) = Prams(i) + (DateAdd(itr, Prams(i), DateS) > DateF)
имея в виду, что арифметическое значение логического выражения будет равно -1 (True) или 0 (False). Мы, со своей стороны, настоятельно не рекомендуем пользоваться неявными преобразованиями типов данных.
Совет 289. Как правильно прочитать имя каталога
Проблема заключается в том, что в некоторых случаях имя каталога содержит символ обратной косой черты в конце, а иногда — нет. Пример такой путаницы — обращение к свойству Path объекта App. Если приложение находится в подкаталоге, то такой черты в конце не будет (например,
C:\dir_x\dir_y\dir_z), но если оно располагается в корневом каталоге диска, то черта появится (например, С:\). Это нужно, в частности, учитывать при формировании полного имени файла, то есть вместо:
strFullFileName = App.Path & strFileName
нужно применять, например, такую конструкцию:
strFullFileName = App.Path & IIf(Right$( _ App.Path, 1) = “\”, ““, “\”) & strFileName
Если вам лень каждый раз писать этот код, можете создать функцию AppPath:
Public Function AppPath() As String ' Замените App.Path на ' AppPath во всем тексте программы AppPath = App.Path & _ IIf(Right$( App.Path, 1) = “\”, ““, “\”) End Function
Обратите внимание, что если вы автоматически добавляете обратную косую черту ко всем вызовам App.Path, а путь окажется корневым каталогом, то вы получите совершенно неинформативное сообщение об ошибке: Run-time error '5': Invalid procedure call.
Совет 290. Как заполнить поля электронного сообщения
ShellExecute является одной из наиболее гибких функций в Win32 API. С ее помощью можно передавать любое имя файла, а если расширение файла связано с какой-либо из программ, зарегистрированных на машине пользователя, то запускается соответствующее приложение, которое выводит или проигрывает указанный файл.
Здесь мы покажем, как использовать функцию ShellExecute для отправки электронных сообщений. Вы сможете задавать не только адрес получателя, но и списки получателей (CC и BCC), тему и текст сообщения, а также вставлять файл или его часть в отправляемое сообщение. Для этого необходимо создать строковую переменную, добавить список основных адресов (отделенных друг от друга точкой с запятой) и знак вопроса:
для копий CC (Копия): &CC= (список получателей) для невидимых (слепых) копий: &BCC= (список получателей) для темы сообщения: &Subject= (тема сообщения) для текста сообщения: &Body= (текст сообщения) для присоединения файла: &Attach= (путь к файлу, заключенный в кавычки)
Продемонстрируем это на примере. Создайте новый VB-проект, добавьте форму и разместите на ней шесть текстовых полей и одну командную кнопку cmdSendIt (см. рис. 1 — tip290.bmp). Напишите такой код в разделе Declarations:
Private Declare Function ShellExecute Lib _ “shell32.dll” Alias “ShellExecuteA” _ (ByVal hWnd As Long, ByVal lpOperation As _ String, ByVal lpFile As String, ByVal _ lpParameters As String, ByVal lpDirectory _ As String, ByVal nShowCmd As Long) As Long Private Const SW_SHOWNORMAL = 1
Затем введите следующий код для события Click командной кнопки:
Private Sub cmdSendIt_Click() Dim sText As String Dim sAddedText As String If Len(txtMainAddresses) Then sText = txtMainAddresses End If If Len(txtCC) Then sAddedText = sAddedText & “&CC=“ & txtCC End If If Len(txtBCC) Then sAddedText = sAddedText & “&BCC=“ & txtBCC End If If Len(txtSubject) Then sAddedText = sAddedText & “&Subject=“ & txtSubject End If If Len(txtBody) Then sAddedText = sAddedText & “&Body=“ & txtBody End If If Len(txtAttachementFileLocation) Then sAddedText = sAddedText & “&Attach=“ & Chr(34) & _ txtAttachementFileLocation & Chr(34) End If sText = “mailto:” & sText If Len(sAddedText) <> 0 Then Mid$(sAddedText, 1, 1) = “?” End If sText = sText & sAddedText If Len(sText) Then Call ShellExecute(Me.hWnd, “open”, _ sText, vbNullString, vbNullString, _ SW_SHOWNORMAL) End If End Sub
Здесь следует обратить внимание на два момента:
- Между знаками «амперсанд» (&) и «тэг» или знаками «тэг» и «равно» не нужно ставить пробелы.
- Из-за отсутствия возможностей форматирования текст сообщения будет состоять из одного параграфа.
Тем не менее, используя предложенный здесь способ, вы сможете создавать работающие почтовые аплеты всего за несколько секунд.
Примечание. Полная функциональность полей электронного сообщения может быть достигнута только в почтовых клиентах, совместимых с Microsoft Exchange. С другими почтовыми клиентами некоторые или даже все эти поля могут не работать.
Совет 291. Как открыть VB-файл в Notepad или в любом другом текстовом редакторе
Предположим, вы хотите сделать так, чтобы с помощью щелчка правой кнопки мыши можно было открыть VB-файл в редакторе Notepad и скопировать фрагмент кода для другого приложения, например для того, над которым вы сейчас работаете. Попробуйте сделать следующее.
Создайте текстовый файл с именем FormEdit.reg, введите в него такой код, сохраните, а затем закройте его:
REGEDIT4 [HKEY_CLASSES_ROOT\VisualBasic.Form\ _ shell\edit] @=“&Edit” [HKEY_CLASSES_ROOT\VisualBasic.Form\ _ shell\edit\command] @=“C:\Windows\Notepad.exe %1”
Дважды щелкните мышью на этом файле, и его содержимое автоматически загрузимся в Системный Реестр. Теперь щелкните правой кнопкой мыши на любом FRM-файле, и в появившемся «быстром» меню вы увидите команду Edit. Выполните ту же самую операцию для других текстовых VB-файлов – VisualBasic.ClassModule, VisualBasic.Module и VisualBasic.Project, а затем с помощью программы Regedit.exe в Windows проверьте полученные результаты.
Если вместо Notepad вы хотите использовать Word или любой другой текстовый редактор, напишите примерно такую командную строку (для Word):
@=“c:\\Program Files\\Microsoft Office\\ _ Office\\Winword.exe %1”
Здесь следует быть особенно внимательными — используйте две обратные косые черты и, кроме того, не ошибитесь, указывая полный путь к файлу Winword.exe.
Хотя VB4, VB5 и VB6 имеют различные точки входа в Реестре, подобную методику можно применять для любого текстового VB-файла, включая BAS, CLS, FRM и VBP. Она не подходит для FRX- или других двоичных файлов, но с ее помощью можно реализовать просмотр HTML-файлов. Указав путь к используемому по умолчанию браузеру, можно, щелкнув правой кнопкой мыши на любом HTML-файле, редактировать его как простой текст. Кроме того, можно настроить браузер на просмотр GIF-файлов, чтобы увидеть, что они собой представляют. Но самое главное — всякий раз, работая с Реестром, будьте крайне осторожны.
Совет 292. Как ускорить операцию деления чисел с плавающей запятой
Если вам приходится выполнять много арифметических операций деления чисел с плавающей запятой в VB, можете попробовать оптимизировать эти действия, осуществляя умножение на обратную величину. Например, вместо операции:
X / Y
выполните:
X * (1 / Y)
Чтобы увидеть, как это работает на практике, создайте новый проект в VB и введите следующий код в событие Form_Click:
Option Explicit Private Declare Function GetTickCount _ Lib “kernel32” () As Long Const NORMAL As Double = 1453 Const RECIPROCAL As Double = 1 / NORMAL Const TOTAL_COUNT As Long = 10000000 Private Sub Form_Click() Dim dblRes As Double Dim lngC As Long Dim lngStart As Long ' On Error GoTo Error_Normal ' lngStart = GetTickCount For lngC = 1 To TOTAL_COUNT dblRes = Rnd / NORMAL Next lngC MsgBox “Обычное время: “ & _ GetTickCount - lngStart lngStart = GetTickCount For lngC = 1 To TOTAL_COUNT dblRes = Rnd * RECIPROCAL Next lngC MsgBox “Время для обратной величины: “ _ & GetTickCount - lngStart Exit Sub Error_Normal: MsgBox Err.Number & “ - “ & Err.Description End Sub
Вы обнаружите, что скорость выполнения вычислений увеличится примерно на 15% при использовании метода умножения на обратную величину. Тем не менее будьте осторожны при округлении чисел — так, 3 / 3 = 1, но 3 * (0,333333...) = 0,999999....
Совет 293. Как ускорить выполнение операций ввода-вывода для файлов
Довольно часто в программах встречаются ситуации, когда нужно просто целиком скопировать содержимое файла в оперативную память. Например, это необходимо для выполнения копирования файлов или операций быстрого поиска контекста.
Для текстовых файлов можно предложить такие две процедуры чтения и записи файлов:
Public Function ReadFile(FileName _ As String) As String Dim FileNumber As Integer FileNumber = FreeFile Open FileName For Input As #FileNumber ReadFile = Input(LOF(FileNumber), FileNumber) Close #FileNumber End Function Public Sub WriteFile(FileName As _ String, Contents As String) Dim FileNumber As Integer FileNumber = FreeFile Open FileName For Output As #FileNumber Print #FileNumber, Contents; Close #FileNumber End Sub
Обратите внимание, что перед открытием файлов мы получаем свободный номер файла с помощью функции FreeFile. С применением таких функций операция копирования двух файлов принимает такой вид:
Call WriteFile(“c:\b.txt”, ReadFile(“c:\a.txt”))
Для копирования файлов произвольного формата следует использовать тип файлов Binary. Тогда операции чтения-записи будут выглядеть так:
Open FileInput$ For Binary As #FileInp FileString$ = Space$(LOF(FileInp)) Get #FileInp,, FileString$ ReadFile = FileString$ ... Open FileOutput$ For Bi As #FileOut Put #FileOut,, FileString$
Но в этом случае нужно сначала сделать проверку — может быть, выходной файл уже существует (если это так, то в него просто перепишутся первые Len(FileString$) символов).
Однако следует иметь в виду, что для точного чтения содержимого произвольных файлов использование строковой переменной не очень хорошо подходит. Дело в том, что в этом случае при чтении производится преобразование байтов из однобайтовой ANSII-кодировки в двухбайтовую Unicode (подробнее об этом см. КомпьютерПресс 10’99, CD-ROM). При записи производится обратное преобразование.
Поэтому для создания точной копии содержимого файлов следует использовать байтовый динамический массив:
ReDim ArrByte (1 To LOF(FileNumber)) Get #FileInp,, ArrByte ... Get #FileOut,, ArrByte
Совет 294. Как создать дополнение, закрывающее все окна проекта
При создании VB-проекта вы наверняка открываете множество различных окон, особенно выполняя операции поиска/замены внутри программного кода. В больших проектах количество открытых окон вырастает настолько, что становится обременительным закрывать их одно за другим. Поэтому мы предлагаем создать простое дополнение, которое бы выполняло эту работу за вас.
Создайте новый проект типа AddIn (значок Addin в окне New Project). Вы получите шаблон MyAddIn, в котором содержится некоторый программный код для построения дополнения. В код формы frmAddIn введите следующее:
Private Sub Form_Load() Dim w As Window For Each w In VBInstance.Windows ' закрывает все видимые ' окна кода и форм If (w.Type = vbext_wt_CodeWindow _ Or w.Type = vbext_wt_Designer) _ And w.Visible Then w.Close End If Next Unload Me End Sub
В окне Object Browser щелкните правой кнопкой мыши на проекте MyAddIn. В появившемся «быстром меню» выберите команду Properties и измените имя и описание дополнения. Затем в коде шаблона замените My Add-In на то имя, которое бы вы хотели видеть в меню Add-Ins в среде разработки VBE. Если вы работаете в VB6, то помимо этого необходимо еще поменять имя дополнения в конструкторе AddInDesigner. Создайте DLL-библиотеку (команда File|Make MyAddIn.dll), и ваше дополнение должно появиться в списке в Add-In Manager. Добавьте его к командам меню Add-Ins, а затем запустите — все открытые окна проекта закроются в одно мгновение!
Совет 295. Используйте ключевое слово WithEvents для связи форм MDI и MDIChild
Здесь приводится изящный способ передачи событий, таких как щелчки на панели инструментов или выделение команд меню, из родительской MDI-формы в активную дочернюю MDIChild-форму в многодокументном приложении. Предположим, что MDI-форма содержит элемент управления Toolbar с именем tbrMain, для которого введите следующий код:
Option Explicit Event ButtonClick(strKey As String) Private Sub tbrMain_ButtonClick(ByVal _ Button As MSComctlLib.Button) RaiseEvent ButtonClick(Button.Key) End Sub
Затем напишите такой код для каждой MDIChild-формы, которая должна получить событие ButtonClick:
Private WithEvents m_mdiParent As mdiParent Private Sub tbrMain_ButtonClick(ByVal _ Button As MSComctlLib.Button) RaiseEvent ButtonClick(Button.Key) End Sub Private Sub Form_Acitivate() Set m_mdiParent = mdiParent End Sub Private Sub Form_Deactivate() Set m_mdiParent = Nothing End Sub Private Sub m_mdiParent_ButtonClick _ (strKey As String) ' Пример кода, в котором значения ' Button.Key соответствуют кнопкам ' New, Change, Delete и Save Select Case strKey Case “New” PerformNewAction Case “Change” PerformChangeAction Case “Delete” PerformDeleteAction Case “Save” PerformSaveAction End Select End Sub
Использование этой подпрограммы аналогично объявлению элемента управления с именем m_mdiParent, у которого есть событие ButtonClick. Используйте события Activate и Deactivate, чтобы форма MDIChild являлась единственной, которая бы получала событие ButtonClick.
Совет 296. Помните об операторе Option Base
В общем случае при резервировании массива нужно указывать в явном виде его нижнюю и верхнюю границу индекса:
ReDim MyArray (LowBoundary To UpBoundary)
Но довольно часто мы используем более простую конструкцию, когда значение нижней границы задается по умолчанию:
ReDim MyArray (UpBoundary) Dim MyArrayStat(100)
Но чему же равна нижняя граница и сколько элементов на самом деле содержится в массиве?
В языках программирования используется два варианта соглашений: например, в Фортране — нумерация традиционно начинается с единицы, а в Бейсике — с нуля.
Во всех версиях Microsoft Basic (Quick for DOS, Visual for Windows) программист может управлять установкой значения нижней границы по умолчанию с помощью оператора Option Base. Он помещается в секции Declarations в начале программного модуля (форма, модуль кода, модуль класса и пр.) и его действие распространяется на все объявления массивов внутри модуля.
Таким образом, значение нижней границы для приведенного выше примера Dim MyArrayStat(100) будет следующим:
= 0 — если оператор Option Base отсутствует;
= 0 — задан Option Base 0;
= 1 — задан Option Base 1.
Все это нужно иметь в виду, если вы вставляете фрагмент программного кода в какие-либо модули (например, из своей библиотеки повторно используемых процедур), — значение нижней границы будет зависеть от наличия в данном модуле оператора Option Base.
Совет: Если для программы принципиально важно наличие или отсутствие нулевого индекса, то указывайте обе границы индекса в явном виде.
Еще одно замечание. В VB 5/6 имеется возможность автоматического создания и заполнения байтового массива при перезаписи в него строковой переменной:
Dim arrByte () As Byte Source$ = “Строковая переменная” arrByte = Source$
В этом случае всегда создается массив размерностью от 0 до (LenB(Source$)-1) независимо от наличия/отсутствия оператора Option Base.
Совет 297. Экспорт таблиц в виде текстовых файлов
Здесь приведена простая процедура, которая выводит в виде текстового файла записи из таблицы базы данных или из таблицы, сформированной в результате SQL-запроса. Эта информация потом может быть считана любым текстовым редактором или программой электронных таблиц. В этом примере Db — это глобальная переменная объекта, которая должна быть до обращения к процедуре определена как база данных. sSource — имя таблицы или SQL-запрос. Кроме имени выходного файла пользователь должен также задать код разделителя полей записи в текстовом файле:
Public Function TableToSpreadsheetMy(sSource As String, _ sFile As String, sSeparator As String) As Boolean ' Включение обработки ошибок On Error GoTo TableToSpreadsheet_Err ' ' Синтаксис обращения: ' If TableToSpreadsheet(“SELECT * FROM _ ' Authors”, “C:\Temp\Authors.csv”, Chr$(9)) = True _ ' Then.... ' Dim rsTemp As Recordset Dim sHeader As String Dim sRow As String Dim i As Integer, nFile As Integer ' Формирование набора данных ' Глобальная объект-переменная Db должна ' быть открыта ранее как база данных Set rsTemp = Db.OpenRecordset(sSource) With rsTemp TableToSpreadsheet = False ' Есть ли записи в наборе? If .RecordCount > 0 Then ' Создание выходного файла nFile = FreeFile Open sFile For Output As #nFile ' Вывод имен полей таблицы sHeader = .Fields(0).Name If .Field.Count > 0 Then For i = 1 To .Fields.Count - 1 sHeader = sHeader & sSeparator & .Fields(i).Name Next i End If Print #nFile, sHeader ' Вывод содержимого полей таблицы .MoveFirst Do Until .EOF sRow = .Fields(0).Value If .Field.Count > 0 Then For i = 1 To .Fields.Count - 1 sRow = sRow & sSeparator & .Fields(i).Value Next i End If Print #nFile, sRow .MoveNext Loop Close #nFile ' Target file is complete TableToSpreadsheet = True End If .Close End With Set rsTemp = Nothing ' закрываем временный объект Exit Function TableToSpreadsheet_Err: LogIt “TableToSpreadsheet : “ & Err.Description ' Запись информации об ошибке Resume Next End Function
КомпьютерПресс 7'20000