Как сделать полоску загрузки vba

Добавил пользователь Alex
Обновлено: 10.09.2024

Совет 170. Используйте функцию CopyMemory из Win32 API

При разработке приложений довольно часто встречается простая задача пересылки N последовательных байтов из одной области оперативной памяти в другую. В VB проблема возникает, когда такая область памяти определена не именем переменной, а в виде адреса (указателя —Pointer). К сожалению, встроенные средства VB не позволяют работать с адресами, а порой это крайне необходимо.

Так, некоторые функции Win API, используемые для получения информации о ресурсах системы (например, VerQueryValue), возвращают в качестве параметра адрес области памяти FiAddr&, где лежит искомая информация. Но как переписать эти данные, к примеру, в целочисленный массив для дальнейшей обработки в среде VB? Это можно сделать с помощью функций Win API.

В составе Win16 API для этого имелась функция HMemCpy:

Она появилась только в расширенном варианте функций в Windows 3.1 (в версии 3.0 ее не было), а в пакете VB 3.0 ее описание было приведено только в файле WIN31WH.HLP.

Что же касается Win32 API, то в описании WIN32API.TXT для VB 4.0 и 5.0 о функции HMemCpy или о ее аналоге даже не упоминается. Более того, в книге Дэна Апплмана по Win32 API, на которую мы часто ссылаемся (о ней мы писали в Совете 133), ничего не говорится о подобной функции. Однако, поскольку такая функция действительно очень нужна, Дэн предлагает использовать процедуру agCopyData из его собственной библиотеки APIGI32.DLL, которая приводится на прилагаемом компакт-диске.

Все это выглядит весьма странно. Более тщательный поиск, проведенный с помощью специалиста московского отделения Microsoft Юрия Томашко, увенчался успехом — в Win32 есть функция копирования байтов CopyMemory, и ее описание появилось в VB 6.0 в файле WIN32API.TXT. Почему Microsoft скрывает от пользователей VB 4.0 и 5.0 ее существование, и почему Дэн Апплман хранит эту тайну — можно только гадать. Кстати, настоящее имя функции в библиотеке KERNEL32 —RtlMoveMemory:

Вот типичный пример применения этой функции:

CopyMemory можно использовать для создания очень полезных функций преобразования типов данных (см. Совет 171), а также для ускорения в сотни и даже тысячи раз выполнения операций, реализуемых традиционными средствами VB. Например, для копирования целых массивов или их фрагментов:

Совет 171. Копирование областей памяти в DOS

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

Например, функция копирования областей памяти пригодится и тем, кто еще работает в Basic/DOS. В силу специфики использования библиотек в этих версиях Basic (мы вновь сожалеем, что в VB/Win-проектах Microsoft не позволяет подключать к исполняемому модулю объектные библиотеки) такие внешние функции лучше всего было писать на Ассемблере. Посмотрите, какой простой код имеет функция StringCopy, написанная для варианта MASM 6.0, которая фактически является точным аналогом функции CopyMemory для режима DOS (только число байтов задается целочисленной переменной):

Ее описание можно сделать двумя способами:

  1. Адреса задаются с помощью двух 16-разрядных переменных — сегмент и смещение:
    • описание:
    • обращение:
  2. Полные адреса задаются с помощью 32-разрядных переменных:
    • описание:
    • обращение:

Совет 172. Как реализовать функции MKx$/CVx в VB/Win

Именно с этого совета (№ 3) три года назад мы начали свои публикации для пользователей VB в журнале КомпьютерПресс № 3’96. Напомнить о нем мы решили для иллюстрации применения функции CopyMemory.

Дело в том, что в DOS’овских версиях MS Basic (Quick, PDS, Visual) имелась группа очень полезных встроенных функций MKx$/CVx (x — тип числовых данных: I, L, S, D), которые почему-то пропали в VB/Win. (Их описание приводится во встроенной справке QBasic, которая входит в состав MS DOS 5.0 и 6.x.)

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

Основной смысл такого преобразования — это возможность хранения и передачи разнотипных данных в виде одной строковой переменной, что является отличной альтернативой структурам данных Type с их жестким описанием полей на уровне исходного текста. В свое время мы очень широко использовали в своей практике этот прием для создания гибких, динамически настраиваемых структур данных.

Применение данных функций позволяет осуществлять весьма изящные преобразования данных. Например, в Совете 117 (КомпьютерПресс, № 10’97) мы говорили о проблеме обработки беззнаковых целых чисел и приводили пример слияния двух 16-разрядных целых чисел в 32-разрядное представление и наоборот. Сравните приведенный там пример со следующим вариантом:

С помощью функций Win API в VB/Win можно довольно просто реализовать эти полезные Basic-функции. Набор таких процедур приведен в модуле MKXCVX.BAS, а пример их применения — в модуле MKXCVXTS.BAS (см. листинг 1).

Совет 173. Как сделать фон формы в виде палитры цветов

Хотите сделать фон своей формы в виде палитры цветов, вроде той, что Microsoft любит выдавать на экране при работе своих установочных утилит SETUP.EXE? Такой стиль раскрашивания называется градиентным заполнением и легко реализуется с помощью следующей процедуры:

Теперь в событие Form_Activate соответствующей формы вставьте строку:

Цвет рисовки каждой полоски определяется с помощью функции RGB (Red-Green-Blue), основанной на смешении красного, зеленого и синего цветов. В приведенном выше примере закраска получается от черного до голубого цвета (как у Microsoft). Для черно-красного фона используйте такой вариант:

Совет 174. Помните о свойстве KeyPreview для формы

Как и многие другие элементы управления, форма имеет стандартные события для обработки нажатия клавиш — KeyDown, KeyPress и KeyUp (об особенностях использования этих процедур и кодов клавиатуры см. Совет 129 в КомпьютерПресс № 7’98, с.195). Их можно применять для управления кодами клавиш на уровне формы для всех находящихся на ней элементов управления.

Для этого следует сначала установить свойство KeyPreview формы как True. Тогда можно будет выполнять перехват всех событий KeyXX на уровне формы: в первую очередь будут выполняться процедуры формы, а уже потом — процедуры элементов управления.

Если вам необходимо заблокировать обработку операций с клавишами в элементах управления (а они выполняются вслед за обработкой событий формы), установите значения KeyAscii=0 и KeyCode=0 в событиях KeyPress и KeyDown/Up соответственно.

Однако следует иметь в виду, что некоторые элементы управления (когда они находятся в фокусе) перехватывают определенные операции с клавишами в любом случае, и эти события не доходят до процедур формы. Например, командная кнопка всегда реагирует на нажатие Enter, а списки — на клавиши управления курсором.

Совет 176. Используйте свойства Default и Cancel для командных кнопок

Довольно часто на форме располагаются две кнопки, обычно связанные с ее закрытием: Ok — выполнение некоторых действий, заданных формой, и Cancel — отмена каких-либо действий. При работе с клавиатурой подобные операции традиционно выполняются с помощью клавиш Enter и Esc. Чтобы задействовать проименение этих клавиш, можно использовать свойства Default и Cancel для командных кнопок.

Если вы установите свойство Default для кнопки как True, то в любой момент работы с формой при нажатии Enter будет выполняться событие Click данной кнопки. Аналогично, если установить свойство Cancel как True, то при нажатии Esc будет выполняться событие Click. Во избежание путаницы VB автоматически следит за выполнением двух правил:

  1. Свойство Default или Cancel может быть установлено как True только для одной кнопки на форме.
  2. Одна кнопка может иметь одновременно только одно свойство, установленное как True, — либо Default, либо Cancel.

Совет 177. Как определить имя накопителя CD-ROM

Если вам нужно определить имя накопителя на компакт-дисках, можете воспользоваться таким программным кодом:

Совет 178. Как избежать ненужного обновления наборов записей

Приведенный здесь код пригодится для уменьшения влияния операции по обновлению наборов данных на ввод данных с клавиатуры. Для этого поместите на форму таймер (tmr_Timer) и установите свойство Interval как 1000 и свойство Enabled как False. Затем введите следующий код в событие txtFilter_Change текстового окна:

В событии Timer вызовите подпрограмму, которая обновляет ваш набор записей:

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

Совет 179. Упрощайте программный код

Конструкции, подобные следующей

встречаются в программах довольно часто. Но гораздо привлекательнее выглядит такой вариант:

Совет 180 (очень длинный). Будьте внимательны при работе с повторно используемыми BAS-компонентами в VB и VBA

Например, по ходу разработки приложения вам нужно использовать процедуру Proc1, которая хранится в модуле Module1.bas. Внешне эта задача решается в VB (автономном средстве разработки) и в VBA (точнее, в среде VBA некоторого конкретного офисного приложения, например Word) примерно одинаково: нужно загрузить этот модуль командой Project|Add Module или File|Import File соответственно. Но в этих операциях имеются и принципиальные отличия.

Как это происходит в Visual Basic

При работе с VB каждый используемый в нем BAS- и FRM-модуль продолжает оставаться автономным компонентом приложения, каждый хранится в виде отдельных файлов на диске. Собственно VB-проект — это совокупность автономных файлов с программным кодом, которые объединяются в один загрузочный EXE-модуль только в момент его создания. Разумеется, сейчас речь идет только о файлах BAS и FRM, которые могут формироваться непосредственно в среде VB, и код которых помещается в загрузочный модуль. Другие компоненты приложения — OCX, DLL и прочие являются внешними и формируются отдельно.

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

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

Хорошим решением при работе в среде систем MS Basic для DOS было использование вспомогательных BAS-процедур в виде объектных (сейчас их часто называют статическими) LIB-библиотек, которые потом включались в состав исполняемого модуля. Остается только сожалеть, что Microsoft почему-то упорно не хочет реализовать такой удобный и простой вариант в своем Visual Basic для Windows. Однако, начиная с версии VB4, подобные процедуры можно оформлять в виде ActiveX-серверов (DLL или EXE), к которым могут обращаться любые программы, поддерживающие технологии ActiveX (в том числе и приложения MS Office 97).

Однако речь идет о двух разных технологиях: одно из важных различий между объектными и динамическими библиотеками заключается в том, что в первом случае в состав исполняемого модуля записываются только те процедуры, на которые имеются ссылки. В варианте же DLL в исполняемый модуль вообще ничего не записывается, но при обращении к любой функции из такой библиотеки в память грузится весь файл целиком.

В то же время нужно иметь в виду то, что при использовании LIB-библиотеки производится загрузка не только кода процедуры, на которую имеется ссылка, но и всего объектного модуля, включая остальные записанные в нем процедуры. То же самое происходит и при загрузке повторно используемых компонентов в виде исходных модулей.

Мы говорим об этом потому, что хотим обратить ваше внимание на распределение процедур по модулям — это особое искусство, и о нем можно говорить отдельно. Тут есть два крайних варианта: либо поместить все вспомогательные процедуры в один модуль, либо каждую процедуру записать в отдельный модуль. Первый вариант представляется изначально неверным (слишком большая избыточность кода для конкретного приложения); второй вариант (который в свое время был классическим стилем программирования) тоже не оптимален, так как заставляет программиста работать с огромным числом файлов. Оптимальный вариант лежит где-то посередине, не говоря уже о том, что объединение взаимосвязанных процедур (например, на уровне общих данных) в один модуль бывает просто необходимо.

Как обстоит дело в Office 97

Тут все происходит совсем не так, как в VB.

BAS-модуль, загруженный в Office/VBA (это относится и к Word, и к Excel) командой File|Import File, автоматически теряет логическую связь с соответствующим исходным BAS-файлом, хранимым на диске, и становится сугубо внутренним компонентом данного приложения. Соответственно, все изменения, сделанные впоследствии в BAS-файле, никак не влияют на состояние уже загруженного модуля. И наоборот, коррекция кода, выполненная внутри VBA, никак не влияет на состояние исходного модуля. Если вы хотите использовать созданный (или измененный) код такого модуля, его нужно специально записать на диск командной File|Export File. Все это относится и к FRM-модулям.

Эти различия при работе в VB и VBA видны уже из названий команд ввода/вывода модулей (Load/Save и Import/Export), а также в представлении модулей в окне проектов. В случае VB (рис. 2) BAS-модуль представлен и именем файла (в скобках), и его свойством Name (Attribute VB_Name в первой строке файла). При этом если загружается BAS-модуль старой структуры (без Attribute VB_Name), то формируется стандартное имя ModuleX. При работе в VBA модуль идентифицируется только свойством Name без какого-то упоминания об исходном файле (рис. 3).

Объясните, пожалуйста, почему желательно загружать основную форму так:

Private m_OMyForm as frmMyForm

sub Main()
set m_OMyForm = new frmMyForm
m_OMyForm.Show
end sub

Почему не ограничится: frmMyForm.Show .

Может это где-то уже описано, дайте ссылки.

Private m_OMyForm as frmMyForm

sub Main()
set m_OMyForm = new frmMyForm
m_OMyForm.Show
end sub

Почему не ограничится: frmMyForm.Show .

потому что в первом случае ты имеешь указатель на объект формы и можешь по этому указателю замочить форму из любой части программы, впрочем как и показать/скрыть.

Объясните, пожалуйста, почему желательно загружать основную форму так:

Private m_OMyForm as frmMyForm

sub Main()
set m_OMyForm = new frmMyForm
m_OMyForm.Show
end sub

Почему не ограничится: frmMyForm.Show .

Because frm is declared As New, the form is not created until the first time the variable is used in code — in this case, when ANewMethod is invoked. After the code above is executed, the form remains created, but not loaded.

Если ты в классе frmMyForm в Unload напишешь
set frmMyForm = Nothing,
то я даже не знаю, как это назвать :-).

Так что я теперь делаю так:
в Form_Unload:

For Each f In Forms
Unload f
Set f = Nothing
Next
' а теперь и главная форма:
Set frmMain = Nothing

В любом случае, когда выполняется инструкция (Set frmMain = Nothing) основная форма ещё существует. Можно её и не чистить. Мне главное было, чтобы при повторной загрузке неосновных форм инициализировались их переменные.

А так не пробовал?
Sleep - чтобы счетчик не показывал 100% загрузку процессора :-))))

Ну условие-то, можно, положим любое придумать :-).

А End мне принципиально не нравится.

Потому что наличие "хвостов" - это сигнал о том, что в программе возможен Memory Leak.

Потому что наличие "хвостов" - это сигнал о том, что в программе возможен Memory Leak.

Рис. 1.40. Пример использования полосы прокрутки

Рис. 1.40. Пример использования полосы прокрутки

Для левого текстового окна выберем Petrov в качестве значения свойства Name, а у левой полосы прокрутки для аналогичного свойства установим значение Scroll Petrov. Соответственно, у правого текстового окна свойство Name Sidorov, а у правой полосы прокрутки Name — ScrollSidorov.

У полосы прокрутки часто используются следующие свойства:

  • Value — значение, соответствующее положению движка (ползунка) на полосе прокрутки;
  • Мах — значение, соответствующее нижнему (или право му при горизонтальной полосе прокрутки) положению движка;
  • Min — значение, соответствующее верхнему (или левому при горизонтальной полосе прокрутки) положению движка;
  • SmallChange — значение, соответствующее изменению значения Value при щелчках на стрелке полосы прокрутки;
  • LargeChange — значение, соответствующее изменению значения Value при щелчках на полосе прокрутки.

У текстового окна (как у любого другого элемента) есть свойство Тор, которое определяет положение элемента по вертикали, начиная от верхнего края листа. А при изменении положения движка левой полосы прокрутки автоматически выполняется процедура ScrollPetrov_Change() , для правой полосы, соответственно, ScrollSidorov_Change() .

Теперь более точно сформулируем задачу для программной разработки. Будем считать, что у нас в подчинении работают два менеджера и необходимо ежедневно сопоставлять результаты их работы. Скажем, количественная оценка каждого сотрудника изменяется в интервале от 0 до 100 баллов. Технически для отражения результата работы менеджера руководителю необходимо передвинуть движок соответствующей линейки прокрутки. В этом случае текстовое окно должно синхронно перемещаться на листе по вертикали, а в самом окне отображаться количество баллов. При этом, когда движок в верхнем положении, текстовое окно должно располагаться на уровне верхней границы полосы прокрутки. Соответственно, при нижнем положении движка текстовое окно должно располагаться на уровне нижней границы полосы прокрутки. Для реализации этого в окне свойств для полос прокрутки установим значения Min, равные 0, а значения Мах и Height равными 100. Установим также для этих элементов: SmallChange = 1 и LargeChange = 5 . Теперь можно написать процедуры, которые выполняются при передвижении движков левой (листинг 1.23) и правой (листинг 1.24) полос прокрутки.

' Листинг 1.23. Процедура, выполняемая при перемещении движка левой линейки Private Sub ScrollPetrov_Change() Petrov.Top = ScrollPetrov.Value + ScrollPetrov.Top Petrov.Text = "Петров " + CStr(ScrollPetrov.Value) Inten = 155 + ScrollPetrov.Value - ScrollPetrov.Min Petrov.ForeColor = RGB(Inten, Inten, Inten) Petrov.BackColor = RGB(0, Inten, 0) End Sub

' Листинг 1.24. Процедура, выполняемая при перемещении движка правой линейки Private Sub ScrollSidorov_Change() Sidorov.Top = ScrollSidorov.Value + ScrollSidorov.Top Sidorov.Text = "Сидоров " + CStr(ScrollSidorov.Value) Inten = 155 + ScrollSidorov.Value - ScrollSidorov.Min Sidorov.ForeColor = RGB(Inten, Inten, Inten) Sidorov.BackColor = RGB(inten, 0, 0) End Sub

После этого в следующих трех строках производится изменение фонового и основного цветов текстового окна. Как уже ранее отмечалось, интенсивность цвета меняется от 0 до 255. В результате выполнения этих строк максимальная интенсивность зеленого цвета для фона обеспечивается при нижнем крайнем положении движка. Для основного цвета интенсивность также будет максимальной. При другом крайнем положении движка интенсивности устанавливаются равными 155. Зеленый фон в этом случае станет более бледным, а цвет букв серым.

UserForm.ScrollBar – это элемент управления пользовательской формы, представляющий из себя полосу прокрутки с кнопками, реагирующий как на перемещение ползунка, так и на нажатие кнопок.

Элемент управления ScrollBar

Элемент управления ScrollBar предназначен в VBA Excel для ввода пользователем числовых данных, которые ограничены минимальным и максимальным значениями. Увеличение или уменьшение значения полосы прокрутки осуществляется с указанным шагом при помощи ползунка или кнопок.

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

Полоса прокрутки в VBA Excel используется в паре с элементом управления TextBox или Label. Вспомогательный элемент необходим, чтобы отобразить текущее значение ScrollBar на пользовательской форме.

Свойства элемента ScrollBar

* По умолчанию свойство Delay равно 50 миллисекундам. Это означает, что первое событие (SpinUp, SpinDown, Change) происходит через 250 миллисекунд после нажатия кнопки, а каждое последующее событие – через каждые 50 миллисекунд (и так до отпускания кнопки).

** По умолчанию включена автоматическая ориентация, которая зависит от соотношения между шириной и высотой элемента управления. Если ширина больше высоты – ориентация горизонтальная, если высота больше ширины – ориентация вертикальная.

В таблице перечислены только основные, часто используемые свойства полосы прокрутки. Все доступные свойства отображены в окне Properties элемента управления ScrollBar.

Читайте также: