Visual2000 · Архив статей А.Колесова & О.Павловой
Андрей Колесов, Ольга Павлова
© Андрей Колесов, Ольга Павлова, 2001Не стоит даже повторять, как важна для авторов обратная связь с читателями. И мы бы очень хотели, чтобы программисты не только указывали нам на наши оплошности, но и делились собственными находками и "трюками".
Промашки у нас, конечно, бывают. Например, Евгений Иванов справедливо заметил, что в Совете 307 мы, рассказывая об удалении каталога с помощью Windows API, совсем забыли сказать, что в большинстве случаев с этой задачей отлично справляется давно знакомая встроенная Basic-функция rmDir.
Приятно отметить, что журнал "КомпьютерПресс" и наши "Советы" читают не только в нашей стране, но и в дальнем зарубежье (кстати, хотя русскоязычных программистов в Европе и США гораздо меньше, чем в России, их активность на электронных форумах и в переписке заметно выше). Михаил Эскин, например, живет в Мюнхене и, как оказалось, является давним читателем наших публикаций. При этом он отмечает: "Ваши статьи стали постоянным моим спутником, несмотря на появление специальной литературы на прилавках". Спасибо!
В Германию российские журналы приходят с задержкой, поэтому Михаил только в ноябре прислал некоторые замечания по поводу статьи "Календарь наших дел", опубликованной в КомпьютерПресс N 5/2000. (Кстати, Михаил — автор серии статей о создании элементов управления в среде VB, опубликованных на сервере www.vbrussian.com.) Об этих замечаниях мы поговорим в последующих Советах.
Мы уже несколько раз отмечали, что у утилиты VisData (создание баз данных) существуют проблемы с вводом и просмотром русских текстов. Михаил Эскин отмечает, что это, скорее, проблема конфигурации конкретного компьютера, которая решается следующим образом: в разделе [FontSubstitutes] файла WIN.INI нужно добавить сверху строку Tahoma,0=Tahoma,204 и затем перезагрузить компьютер.
Действительно, после этого VisData стала нормально работать с русским текстом. Но мы все равно считаем, что в данном случае имеет место дефект VisData, который возможно устранить с помощью подобного "трюка", поскольку такой способ настройки утилиты для нормальной работы с русским языком нигде не описывается.
Михаил отметил также, что приведенный в статье "Календарь наших дел" код для чтения/записи свойства DeleteMe (для пользовательского элемента управления Memos) при его привязке к значению флажка chkDeleteMe
Public Property Get DeleteMe() As Boolean If chkDeleteMe.Value = 0 Then DeleteMe = False Else DeleteMe = True End If End Property Public Property Let DeleteMe(ByVal newDelete As Boolean) If newDelete Then chkDeleteMe.Value = 1 Else chkDeleteMe.Value = 0 End If End Property
Можно упростить, записав содержимое каждой из этих процедур в одну строку:
DeleteMe = -1 * chkDeleteMe.Value chkDeleteMe.Value = -1 * newDelete
Несмотря на то что речь здесь идет вроде бы об очень частной проблеме, остановимся на ней подробнее.
Мы сами ранее говорили, что для улучшения читаемости программы желательно сокращать количество строк кода (Совет 331). Однако это не должно провоцировать потерю управляемости программой и снижение ее эффективности (обратите внимание, что в Совете 331 речь шла о разных формах записи одних и тех же конструкций).
Нам кажется, что вариант, предложенный Михаилом Эскиным, приведет именно к таким негативным последствиям. По этому поводу отметим следующие моменты:
вместо "Where DeleteMe = True" написать — "Where DeleteMe = 1"
При этом автоматически решается проблема обмена данными между переменной и значением флажка, поскольку они оказываются тождественно равными (можно даже использовать значение флажка 2 — "может быть").
DeleteMe = -1 * chkValue
DeleteMe = (chkDeleteMe.Value = 1) chkDeleteMe.Value = IIf (1, 0, DeleteMe)Но с точки зрения "очевидности результата" такой код также не безупречен.
If chkDeleteMe.Value = 0 Then DeleteMe = False _ Else DeleteMe = True
Мы уже несколько раз отмечали, что, несмотря на всю схожесть VB и Office/VBA, у этих систем есть ряд серьезных различий, которые препятствуют прямому перенесению кода из одного вида проекта в другой и наоборот. Поэтому при написании кода, который предполагается для использования в разных системах, нужно специально тестировать возможность их использования в обоих вариантах. К сожалению, только изучая документацию, проверить это трудно.
Однако в общем случае следует иметь в виду, что VBA все же располагает более ограниченным набором функций по сравнению с VB (речь идет о встроенных возможностях самого языка, без учета объектов приложения, в котором используется VBA). Поэтому при прочих равных условиях для создания общих программных модулей (совместимых на уровне исходного кода) предпочтительнее среда VBA.
Это, в частности, касается и создания модулей формы. Мы уже писали (Совет 301), что Office/VBA использует для создания форм ActiveX-конструктор Microsoft Form 2.0, который доступен также в VB. То есть VB может создавать два типа: собственные VB-формы (Ruby Forms) и UserForms (VBA Forms). Однако проблема заключается в том, что, даже используя одинаковый конструктор, VB и VBA сохраняют модули формы в разных форматах. При этом VB может читать оба формата, а VBA — только свой собственный.
Соответственно, если вы намерены создавать модули формы двойного применения, это следует делать не просто с помощью MS Forms 2.0 , а обязательно с этой целью использовать Office/VBA.
И еще один совет, который из этого следует: для лучшей совместимости кода нужно в максимальной степени выносить код из процедур модулей формы в процедуры модуля кода или класса (эти компоненты пока — трудно сказать, что Microsoft придумает дальше — загружаются в обе среды разработки).
Перенос модулей формы в случае их несовместимости можно сделать следующим образом (например, из VB в VBA). Создайте в VBA визуальную форму со всеми компонентами. Задайте имена компонентов такие же, как в VB. Далее скопируйте содержимое кода из VB в VBA через буфер обмена.
Однако этот способ будет работать только при использовании Forms 2.0. При переносе VB-форм придется вручную корректировать имена некоторых событий и свойств. Например, в VBA события формы Initialize и Terminate соответствуют событиям Load и Unload в VB. В общем, с Microsoft не соскучишься.
Элемент управления DataGrid позволяет установить для ячеек одной или нескольких колонок таблицы свойство Button, которое обеспечивает их работу в режиме "кнопок". Например, установите
DataGrid1.Columns.Item(1).Button = True
В этом случае после щелчка мыши по ячейкам первой колонки будет выполняться событие ButtonClick. Программист может написать в этой процедуре любой специальный код, например вывести диалоговое окно с информацией (список, таблица и пр.), которая связана с данной ячейкой.
Программ создания HELP-файлов довольно много, ряд из них — свободно распространяемые или условно-бесплатные. Для VB и VBA, возможно, лучшим способом является использование утилиты Microsoft HTML Help Workshop, которая поставляется в составе ряда программных продуктов, в том числе MS Office 2000 Developer Edition. При желании ее можно скачать из Интернета. Описания работы этой утилиты имеются в целом ряде книг по VB.
Информацию из текстового файла, записанную в виде полей, разделенных запятыми, можно достаточно просто представить в виде набора данных ADO. Это достигается следующим образом:
connCSV.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _ "Data Source=" & FileName$ & _ ";Extended Properties='text;FMT=Delimited'"
В данном случае строка с параметрами соединения (Connection String) содержит раздел Extended Properties, который указывает, что используется текстовый файл с полями. Однако следует иметь в виду, что приведенный вариант обращения подразумевает наличие в первой строке текстового файла заголовков полей. Если же заголовков нет, следует указать в явном виде аргумент HDR:
connCSV.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _ "Data Source=" & FileName$ & _ ";Extended Properties='text;HDR=NO;FMT=Delimited'"
Порой требуется добавить элемент управления, например командную кнопку, к массиву подобных компонентов во время выполнения программы. Для этого можно использовать оператор Load:
Load object(index)
где object — имя массива, а index — номер нового элемента управления, который вы хотите добавить. Но при этом в исходном состоянии в массиве уже должен иметься хотя бы один элемент управления (нумерация индекса начинается с нуля). В более общем случае создание новой кнопки может выглядеть так, как описано ниже.
Создайте новый проект и добавьте к форме командную кнопку. Затем в окне Properties введите 0 для свойства Index — VB сразу преобразует одиночную кнопку в массив. Далее введите такой код для формы:
Private Sub cmdBtn_Click(Index As Integer) ' создание новой кнопки для массива элементов управления Dim btn As CommandButton Dim iIndex As Integer iIndex = cmdBtn.Count 'текущее числе элементов массива If iIndex <= 32767 Then ' можно добавлять Load cmdBtn(iIndex) Set btn = cmdBtn(iIndex) With btn ' установка свойств .Top = cmdBtn(iIndex - 1).Top + 620 .Caption = "Command" & iIndex + 1 .Visible = True End With Set btn = Nothing End If End Sub
Работа с реестром Windows может выполняться не только с помощью функций Windows API или объекта Registry (см. Совет 273), но и с применением свойства PrivateProfileString объекта System, который входит в состав библиотеки Microsoft Word 8.0/9.0 Object Library. Она автоматически подключается при работе с MS Word 97/2000 и может использоваться в любых инструментах, которые поддерживают работу с ActiveX-объектами. В частности, к среде VB или MS Office/VBA она подключается с помощью команды Project|Reference или Tools|Reference соответственно.
Вот как будет выглядеть чтение полного имени каталога, где находится Internet Explorer:
RegFile$ = "" ' пустое имя означает Системный Реестр SectionName$ = "HKEY_CURRENT_USER\Software\Microsoft\" _ & "Windows\CurrentVersion\App Paths\IEXPLORER.EXE") ' имя раздела KeyName$ = "Path" ' имя ключа IEPath$ = System.PrivateProfileString(RegFile$, SectionName$, KeyName$) If IEPath$ <> "" Then ' есть имя каталога MsgBox "Имя каталога с IE = " & IEPath$ End If
Соответственно запись нового значения параметра в реестр выполняется следующим образом:
System.PrivateProfileString(RegFile$, SectionName$, KeyName$)= IEPath$
Дополнительным преимуществом данного свойства является возможность работы не только с реестром, но и с любыми текстовыми файлами формата типа Win.INI, то есть возможность использовать для хранения параметров приложения персональные файлы-профайлы. Для этого достаточно просто задать в качестве первого параметра свойства имя соответствующего файла.
Например, при закрытии текущего документа Word можно автоматически фиксировать имя последнего использовавшегося документа:
System.PrivateProfileString("C:\MyWordSetting.ini", "MacroSettings", _ "LastFile") = ActiveDocument.FullName
А при загрузке Word можно автоматически открыть данный файл:
LastFile$ = System.PrivateProfileString("C:\Settings.Txt", _ "MacroSettings", "LastFile") If LastFile$ <> "" Then Documents.Open FileName:=LastFile$
Необходимо обратить внимание на следующие особенности применения свойства PrivateProfileString:
Например, если файл D:\MyFile.INI не существует, то после выполнения кода:
System.PrivateProfileString("d:\ MyFile.INI", "test1", "key1") = "andy"
Будет сформирован файл следующего содержания:
[test1] key1=andy