Visual2000 · Архив статей А.Колесова & О.Павловой

Советы тем, кто программирует на VB & VBA

Андрей Колесов, Ольга Павлова

© 1999, Андрей Колесов, Ольга Павлова
Авторский вариант. Статья была опубликована c незначительной литературной правкой в журнале "КомпьютерПресс" N 11/99 (с.180-182) и N 12 (с.176-177).


Совет 217a. Как передавать данные между формами

Читатель прислал нам такой вопрос:

Пишем программу на VB4/16, и не можем передать переменную или массив из одной формы в другую. Если Вас не затруднит, помогите советом.

Наш ответ таков:

Конечно, было проще ответить, рассмотрев конкретную конструкцию, которую пытаются реализовать читатели. Но лучше попробовать разобраться в принципах информационного взаимодействия двух форм.

1. Передача параметров через свойства объектов

Для этого создадим две формы Form1 и Form2, на каждой из которых разместим текстовое поле и командную кнопку. При такой последовательности создания форм в качестве стартовой формы приложения будет выбрана Form1. В VB4 этот параметр корректируется командой Tools|Options|Project|Startup Form, в VB5/6 — Project|Project Properties|General|Startup Object. Для элементов Form2 установим имена элементов управления как Text2 и Command2 соответственно (чтобы отличать зрительно объекты по их именам), а также Text2.Text = "Text2". Для кнопок Command1 и Command2 установим соответственно такие свойства Caption: "Запустить Form2" и "Вернуть управление в Form1". И напишем для них следующий программный код:

Private Sub Command1_Click() 
  Form2.Show 1 ' вывод Form2 в модальном режиме
End Sub

для формы Form2 (для внешнего элемента управления Text1 обязательно указание имени формы Form1):

Private Sub Form_Activate() 
  ' установка при запуске формы
  Text2.Text = Form1.Text1.Text
  Form1.Text1.Text = "" 'очистить поле
End Sub 
Private Sub Command2_Click() 
  ' Возврат в Form1
  ' переписываем содержимое Text2 в Text1
  Form1.Text1.Text = Text2.Text
  Form2.Hide ' закрыть форму
End Sub

Обратите внимание, что форма Form2 запускается в модальном режиме (значение параметра равно 1). Это означает, что фокус приложения переходит в Form2 — родительская форма Form1 остается видимой, но недоступной для каких-либо операций. Запустите приложение и нажмите кнопку "Запустите Form2" — появится форма Form2, при этом в поле Text2 будет записано содержимое Text1. (Само поле Text1 при этом очистится). Теперь нажмите "Вернуть управление в Form1" — управление вернется в Form1 и в поле Text1 будет установлено содержимое Text2. (Вы можете сами устанавливать содержимое полей, чтобы убедиться в правильности обмена информацией.)

Таким образом мы видим, как происходит передача информации от одной формы к другой в виде установки или чтения свойств элеменов управления. Все формы и находящиеся на них элементы управления являются глобальными: их свойства и методы доступны из процедур других модулей (в том числе BAS-модулей). Вот как выглядит полное имя элемента управления:

[Имя_Формы.]Имя_Элемента_Управления

При обращении к внутренним элементам управления (внутри данной формы) можно указывать сокращенное имя объекта (что мы и делаем), а при работе с внешними — полное, которое начинается с имени формы.

2. Передача данных через параметры процедур

Продолжим наши эксперименты. Создадим на форме Form1 еще одну командную кнопку, для которой установим свойства Name = Command11 и Caption = "Второй вариант запуска", и напишем для нее такой код:

Private Sub Command11_Click() 
  ' второй вариант запуска
  Call Form2.Text2_KeyPress(65)
End Sub

Для поля Text2 на форме Form2 напишем следующую процедуру:

Public Sub Text2_KeyPress(KeyAscii As Integer) 
  Show  ' запуск формы Form2 в немодальном режиме
  Text2.Text = "Значение = " & KeyAscii
End Sub

Запустите на выполнение приложение, нажмите на форме Form1 кнопку Command21 и вы увидите картину, изображенную на рис. 1.

Рис. 1

В этом примере мы обратились из формы Form1 напрямую к процедуре Form2 (в данном случае к Text2_KeyPress) и даже передали в нее информацию в виде параметра процедуры. Но обратите внимание, что выше мы установили для событийной процедуры Text2_KeyPress тип Public, вместо Private по умолчанию.

Очевидно, что такое использование встроенной событийной процедуры является довольно искусственным и ограниченным по возможностям — мы можем использовать только весьма простые, предопределенные варианты передачи параметров. К тому же можно столкнуться с тем, что эти процедуры неожиданно выполняют еще какие-то встроенные операции, которые нам тут не нужны.

Гораздо лучше для реализации второго варианта создать в Form2 простую процедуру, например с именем Start3 (но обязательно типа Public):

Public Sub Start3(StartName$) 
  Show 'запуск формы Form2
  Text2.Text = StartName$
End Sub

а в процедуре Command11_Click (форма Form1) записать такой код:

Private Sub Command11_Click() 
  Call Form2.Start3("Третий вариант запуска")
End Sub

Очевидно, что в этом случае мы может реализовать любой вариант передачи параметров из Form1 в Form2, в том числе и массивы.

Совсем другой вопрос — что мы будем делать с передаваемыми параметрами? В приведенном здесь примере мы их сразу использовали для формирования свойств объектов. А можно переписать эти данные в глобальные переменные на уровне данного модуля (формы), которые затем передать в другие процедуры.

3. Передача данных через глобальные переменные

Еще один вариант передачи данных между модулями — использование глобальных переменных, доступ к которым разрешен из любых процедур приложения. Однако нужно иметь в виду, что в VB такие переменные можно создавать только в модуле кода BAS. Поэтому если вы хотите, чтобы обе созданные нами формы работали с общими данными, то нужно создать BAS-модуль и в нем описать соответствующие глобальные переменные:

Global MyArray$(), MyVar As Integer

В форме Form1 создадим еще одну командную кнопку Command12 и запишем для нее такой код:

%
Private Sub Command12_Click() 
  ' передача данных через глобальные переменные
  ReDim MyArray$(200)
  MyArray$(1) = "Элемент1"
  MyVar = 1001
  Call Form2.Start4
End Sub

В форме Form2 сформируем еще одну процедуру:

Public Sub Start4()
  Form1.Text1.Text = MyArray$(1)
  Text2.Text = MyVar
  Show
  MsgBox UBound(MyArray$)
End Sub

Запустите приложение на выполнение и убедитесь, что при нажатии кнопки Command12 передача данных в Form2 выполняется правильно. Обратите внимание, что в модуле формы Form1 мы не только устанавливаем значения глобальных переменных, но и можем изменять размер массивов, объявленных динамическими.

В начало статьи

Совет 217. Использование функции Split для вычисления количества подстрок

Как мы уже писали в Совете 198 ("КомпьютерПресс" 06'99, компакт-диск), в состав VB6 вошла новая функция, предназначенная для проведения синтаксического разбора строковых переменных. Для этого вы просто указываете конкретный разделитель внутри строки, а VB создает одномерный массив, состоящий из подстрок.

Однако функция Split обладает еще одной возможностью — вы можете использовать ее для быстрого подсчета количества подстрок внутри длинной строки. Предположим, что вы хотите определить, сколько раз слово "лес" встречается в строковой переменной "Мы пошли в лес собирать лесные ягоды". Для этого сначала проведите синтаксический разбор данной строки, а затем с помощью функции UBound подсчитайте количество элементов в полученном массиве, как это видно из следующего примера:

strTungTied = "Мы пошли в лес собирать лесные ягоды"
aryWood = Split(strTungTied, "лес")
MsgBox "Слово 'Лес' встречается в " & vbCrLf & _
        strTungTied & "'" & vbCrLf & _
        UBound(aryWood) & " раз(а)."

Теперь запустите этот код на выполнение, и VB сообщит вам, что слово "лес" встречается в заданной нами фразе два раза.

В начало статьи

Совет 218. Будьте осторожны при использовании методов Delete объекта FileSystemObject в VB

Как вы уже, вероятно, знаете, что объектная библиотека Microsoft Scripting Runtime Library упрощает управление файлами и папками. Так, методы DeleteFolder и DeleteFile позволяют удалять любые ненужные элементы. Однако при их использовании необходимо быть особенно внимательными: в случае нахождения ошибки они прекращают свое выполнение и не совершают отказа от изменений, сделанных к этому моменту времени. Например, если метод DeleteFolder удаляет две папки из десяти, а затем возникает какая-либо ошибка, то VB прерывает выполнение и удаленными оказываются только первых два элемента. Здесь следует также помнить и том, что некоторые другие методы объекта FileSystemObject работают аналогичным образом.

В начало статьи

Совет 219. Заменяйте длинные строки, содержащие путь, на объектные переменные (продолжение Совета 218)

Большинство методов, управляющих дисководами, папками или файлами в объектной библиотеке Microsoft Scripting Runtime Library, требуют задания аргумента filespec — полного пути, представленного в виде строковой переменной. Однако поскольку свойство Path используется по умолчанию для этих трех типов элементов, вы можете применять объектные переменные вместо строк, содержащих пути. Например, чтобы скопировать одну папку в другую с помощью объектных переменных, напишите следующее:

fldr1.Copy fldr2, False

или

MyFSO.CopyFile file1, file2, False

В начало статьи

Совет 220. Для ограничения размеров VB-формы пользуйтесь элементом управления ActiveX

При создании VB-формы часто бывает необходимо запретить пользователю увеличивать ее за пределы каких-либо установленных размеров. Для этого можно использовать событие Resize формы и проверить, не превышают ли ее текущие размеры заданное значение (в твипах). Если это так, то VB уменьшает форму и устанавливает ее размеры в соответствии с максимальным значением, например так:

Private Sub Form_Resize()
  If Me.Width > 2800 Then Me.Width = 2800
End Sub

Однако такой способ все же дает возможность изменить размеры формы за пределы установленных значений — он просто возвращает форму обратно в ее границы, когда вы отпускаете клавишу мыши. Кроме того, если щелкнуть кнопку "развернуть", то форма раскроется, займет все пространство экрана, а потом появится сообщение об ошибке — нельзя изменять размеры формы в режиме "развернуть". Конечно же, VB предлагает несколько путей решения данной проблемы программным образом, но они не всегда достаточно корректны. Так, форма может возникать в различных частях экрана по мере выполнения кода, который изменяет ее размеры. В Руководствах по графическому интерфейсу говорится, что пользователь всегда прав, поэтому лучше сделать так, чтобы он не мог никаким образом перейти за указанные границы формы.

Для этой цели существует бесплатный элемент управления ActiveX, называемый ARFormExtender. Поместите этот невидимый компонент на форму и установите его свойства MaxHeight, MinHeight, MaxWidth, MinWidth и это все! Теперь, запустив программу на выполнение, вы не сможете даже перетащить границы формы за пределы заданных значений. Форма останется неизменной, если вы также щелкните кнопку "развернуть".

Кроме того, ARFormExtender обрабатывает свойство ResizeContents. Если установить последнее как True, элемент управления ARFormExtender будет изменять размеры и взаимное расположение всех компонентов, расположенных на форме, в соответствии с изменением размеров самой формы. Кроме того, любое изменение размеров формы запускает собственное событие Resize элемента управления ARFormExtender, которое передает параметры WidthChange и HeightChange. Эти две величины отражают вертикальное и горизонтальное изменения (в твипах), произошедшие со времени последнего запуска события Resize.

Загрузить элемент управления ARFormExtender или получить более подробную информацию о его возможностях можно по адресу: http://www.sevillaonline.com/ActiveX/.

В начало статьи

Совет 221. Как показать стандартное диалоговое окно Properties для файла

Если вы хотите создать стандартное Windows-приложение, желательно, чтобы оно имело диалоговое окно Properties, которое обычно вызывается командой File|Properties. Для этого можно воспользоваться API-функцией ShellExecuteEx:

Option Explicit

Private Type SHELLEXECUTEINFO
  cbSize As Long
  fMask As Long
  hWnd As Long
  lpVerb As String
  lpFile As String
  lpParameters As String
  lpDirectory As String
  nShow As Long
  hInstApp As Long
  lpIDList As Long
  lpClass As String
  hkeyClass As String
  dwHotKey As Long
  hIcon As Long
  hprocess As Long
End Type

Private Declare Function ShellExecuteEx Lib _
  "shell32" (lpSEI As SHELLEXECUTEINFO) As Long
Private Const SEE_MASK_INVOKEIDLIST = &HC

Private Sub Form_Click()
  Call ShowFileProperties("c:\windows\system\msvbvm50.dll")
End Sub

Private Sub ShowFileProperties(ByVal aFile As String)
  Dim sei As SHELLEXECUTEINFO
  sei.hWnd = Me.hWnd
  sei.lpVerb = "properties"
  sei.lpFile = aFile
  sei.fMask = SEE_MASK_INVOKEIDLIST
  sei.cbSize = Len(sei)
  ShellExecuteEx sei
End Sub

В начало статьи

Совет 222. Настройка цветов и шрифтов в строке состояния приложения

Для настройки шрифтов и цветов в создаваемой вами строке состояния можно воспользоваться элементом управления PictureBox и API-функцией SendMessage. Каждая строка состояния может отображать объект Picture, поэтому сначала вы устанавливаете необходимые элементы Background, Foreground и Font для невидимого компонента PictureBox, а затем присваиваете изображение PictureBox объекту Picture в строке состояния. Объект Panel работает со свойством Width вместо свойства Height (Высота), в то время как функция SendMessage может получать значение этой высоты.

Поместите элемент управления PictureBox на форму и установите его свойства следующим образом:

Свойство       Значение
Name           picStatus
AutoRedraw     True
Visible        False

Перед вызовом подпрограммы PanelText измените объект Font строки состояния в соответствии с параметрами объекта Panel:

Option Explicit

Private Type RECT
  Left As Long
  Top As Long
  Right As Long
  Bottom As Long
End Type

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 WM_USER = &H400
Private Const SB_GETRECT = (WM_USER + 10)

Private Sub PanelText(sb As StatusBar, Index As _
  Long, aText As String, bkColor As Long, fgColor As Long)
  Dim R As RECT
  SendMessage sb.hWnd, SB_GETRECT, Index - 1, R
  With picStatus
    Set .Font = sb.Font
    .Move 0, 0, (R.Right - R.Left + 1) * _
      Screen.TwipsPerPixelX, (R.Bottom - _
      R.Top + 1) * Screen.TwipsPerPixelY
    .BackColor = bkColor
    .Cls
    .ForeColor = fgColor
    picStatus.Print aText
    sb.Panels(Index).Text = aText
    sb.Panels(Index).Picture = .Image
  End With
End Sub

Private Sub Form_Load()
   PanelText StatusBar1, 1, "Сообщение 1", _
     QBColor(1), QBColor(11)
End Sub

В начало статьи

Совет 223. Как выровнять текст в командной кнопке

Вам никогда не приходилось выравнивать текст в командной кнопке? Если да, то вы, наверняка, добавляли соответствующие пробелы в свойстве Caption. Однако это можно сделать и по-другому с помощью API-функции SetWindowLong. Введите следующий код в стандартный BAS-модуль, а затем вызовите его, передавая в качестве аргументов командную кнопку и необходимую комбинацию значений для вертикального и горизонтального выравнивания:

Option Explicit

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 Const BS_LEFT As Long = &H100
Private Const BS_RIGHT As Long = &H200
Private Const BS_CENTER As Long = &H300
Private Const BS_TOP As Long = &H400
Private Const BS_BOTTOM As Long = &H800
Private Const BS_VCENTER As Long = &HC00

Private Const BS_ALLSTYLES = BS_LEFT Or _
  BS_RIGHT Or BS_CENTER Or BS_TOP Or _
  BS_BOTTOM Or BS_VCENTER

Private Const GWL_STYLE& = (-16)

Public Enum bsHorizontalAlignments
  bsLeft = BS_LEFT
  bsRight = BS_RIGHT
  bsCenter = BS_CENTER
End Enum

Public Enum bsVerticalAlignments
  bsTop = BS_TOP
  bsBottom = BS_BOTTOM
  bsVCenter = BS_VCENTER
End Enum

Public Sub AlignButtonText(cmd As CommandButton, _
  Optional ByVal HStyle As bsHorizontalAlignments = _
  bsCenter, Optional ByVal VStyle As _
  bsVerticalAlignments = bsVCenter)

  Dim oldStyle As Long
  ' получает текущий стиль
  oldStyle = GetWindowLong(cmd.hWnd, GWL_STYLE)
  ' очищает существующие установки выравнивания
  oldStyle = oldStyle And (Not BS_ALLSTYLES)
  ' устанавливает новый стиль и
  ' обновляет кнопку
  Call SetWindowLong(cmd.hWnd, GWL_STYLE, _
    oldStyle Or HStyle Or VStyle)
  cmd.Refresh
End Sub

Private Sub Form_Load()
  Call AlignButtonText(Command1, bsLeft, bsTop)
End Sub

В начало статьи

Совет 224. Управление шириной столбцов элемента управления DBGrid в процессе выполнения программы

DBGrid — один из самых популярных элементов управления для вывода данных в виде таблицы. Однако если изменить ширину столбцов сетки в процессе выполнения программы, Visual Basic не запомнит текущие значения и при следующем запуске программы выведет таблицу в неизменном виде. Здесь мы приводим текст подпрограммы, с помощью которой можно сохранять ширину столбцов для повторных запусков:
Option Explicit

Sub DBGridLayout(Operation As String)
  'сохраняет ширину столбцов
  Dim lWidth As Long
  Dim clm As Column
  Dim lDefWidth As Long
  '
  lDefWidth = DBGrid1.DefColWidth
  For Each clm In DBGrid1.Columns
    With cl
      Select Case LCase(Operation)
        Case "save"
          lWidth = .Width
          SaveSetting App.Title, "Cols" _
                  CStr(.ColIndex), lWidth
        Case "load"
          lWidth = GetSetting(App.Title, _
                  "Cols", CStr(.ColIndex), _
          lDefWidth)
          .Width = lWidth
      End Select
    End With
  Next clm
End Sub

Как видно из вышеприведенного текста, подпрограмма использует функции SaveSetting и GetSetting для хранения текущих значений ширины столбцов в разделе VB Регистра. Для применения данной подпрограммы в своем приложении вызовите ее из событий Load и Unload родительской формы. Затем укажите операцию, которую должна выполнить подпрограмма, как это показано ниже:

Private Sub Form_Load()
  DBGridLayout "Load"
End Sub

Private Sub Form_Unload(Cancel As Integer)
  DBGridLayout "Save"
End Sub

В начало статьи

Совет 225. Простой способ запомнить события запуска VB-формы

Если вам трудно запомнить последовательность событий при запуске VB-формы, попробуйте запомнить следующую простую фразу:

I Like RAP (я люблю рэп)

которую можно расшифровать следующим образом:

I — Initialize
Like — Load
R — Resize
A — Activate
P — Paint

Если вам неудобно признаться в том, что вы любите рэп, можно всегда заменить эту фразу на другую, например: I Loathe RAP (я ненавижу рэп).

В начало статьи

Совет 226. Обмен переменными между разными приложениями

Иногда возникает необходимость обмениваться переменными между различными приложениями, например при совместном использовании базы данных. Для этого создадим ActiveX EXE с модулем класса Class1. Здесь нельзя создать библиотеку ActiveX DLL, поскольку она выполняется внутрипроцессно in-process в каждом клиенте. Добавьте к проекту модель (команда Project|Add Module) и опишите переменную, которая будет передаваться в другое приложение, в качестве общей.

Код в ActiveX EXE будет выглядеть следующим образом:

********* Код модуля **********

Global gCon As Database
Global glngNrOfObjects As Long

********* Код класса **********

Public Property Set DBConn(Con As Database)
  Set gCon = Con
End Property

Public Property Get DBConn() As Database
  Set DBConn = gCon
End Property

Private Sub Class_Initialize()
  ' эта переменная хранит количество
  ' созданных объектов
  glngNrOfObjects = glngNrOfObjects + 1
End Sub

Private Sub Class_Terminate()
  ' уменьшение количества объектов
  glngNrOfObjects = glngNrOfObjects - 1
  ' если нет никаких объектов,
  ' связь прерывается
  If glngNrOfObjects = 0 Then
    If Not gCon Is Nothing Then
      gCon.Close
      Set gCon = Nothing
    End If
  End If
End Sub

Теперь установите ссылку к библиотеке ActiveX EXE в своем проекте и используйте ее, например, так:

*** Код приложения, использующего ActiveX EXE ***

Dim clsDBConn As Class1

Private Sub Form_Load()
  Set clsDBConn = New Class1
  ' связь устанавливается, когда
  ' создан первый объект
  If clsDBConn.DBConn Is Nothing Then
    Set clsDBConn.DBConn = Workspaces(0). _
    OpenDatabase("\VisStudio\Vb98\Biblio.mdb")
  End If
End Sub

Private Sub Form_Unload(Cancel As Integer)
  Set clsDBConn = Nothing
End Sub

Вам нет необходимости писать код для подсчета экземпляров объектов и для закрытия базы данных, если вы работаете с простой переменной, а не ссылкой на объект. В этом случае также поменяйте Property Set на Property Let.

И наконец, главное. Не забудьте установить ссылку к объектам DAO, так как они используют объект базы данных.

В начало статьи