Visual Basic с нуля. Введение. С чего начать?

Пособие-самоучитель on-line "Visual Basic с нуля"
Введение. С чего начать?
Дата создания 02.10.2004 {Автор 4us}
Отредактировано 22.06.2005 {4us}

Итак, мой друг, если ты открыл эту страницу, значит всерьез решил заняться программированием. Однако, если ты решил стать профессиональным программистом и зарабатывать этим себе на жизнь, тебе прямая дорога в C++ и Assembler. Потратив года три на их изучение и присовокупив к ним Delphi и Pascal, ты возможно станешь вполне грамотным специалистом в области создания коммерческих программных продуктов. Или через месяц плюнешь и решишь учиться игре на арфе. Но если ты хочешь, не тратя понапрасну своего драгоценного здоровья и не менее драгоценного времени легко и просто создавать полноценные программные продукты под Windows, еще и получая при этом удовольствие, тебе прямая дорога в VB. Посидев недельку у компьютора, ты сможешь сделать вполне приличную программку для своих нужд. А еще через недельку ты будешь вполне состоявшимся программистом начального уровня. Если этого не произойдет, то можешь продавать компьютер и покупать игровую приставку или арфу.

Из этого отнюдь не следует, что VB - это примитивный язык, освоить который можно, не прилагая никаких усилий. На самом деле это мощнейщее и серьезнейшее средство программирования. Тот, кто говорит, что "программит на барсике два года и знает его вдоль и поперек", по крайней мере страдает манией величия. Тот, кто говорит, что на Visual Basic нельзя сделать полноценного программного продукта (или даже коммерческого) просто не знает его возможностей. Просто начиная с малого, надо углублять свои знания, искать новые пути реализации казалось бы уже знакомых задач.

Конечно, для VB не все подвластно. Одним из его слабых мест является работа с графикой. Так, что если ты хочешь написать на нем "Far Cry", то лучше эту идею оставить, хотя VB позволяет работать с DirectX. На самом деле графическую игру ты не напишешь ни на каком языке просто в силе "неподъемности" задачи. Кроме того, там, где VB не справляется на помощь приходят API-функции. Но и внутренние возможности Visual Basic очень и очень обширны.

Visual Basic наиболее приближен к человеческой логике, заботлив, легок и прост в обращении. Код программы написанной в VB компактнее написанной в C++ минимум в пять раз. Бейсик сам, без твоего вмешательства распределяет память, решает конфликты с Windows, подсказывает ошибки компиляции и т.д. Написав несколько строк кода можно запустить программу и посмотреть, что получилось. Конечно, он требует наличия соответствующих бибилиотек. Поэтому при переносе проекта на компьютер, где не установлен VB, приходится и перетаскивать набор необходимых библиотек. Но для этого существуют специальные программы, которые готовят инсталляционные пакеты.

Итак, если ты не передумал, самое время озаботиться приобретением VB. В принципе, существуют три его редакции в порядке возрастания, так сказать, комплектации - учебная Learning Edition, профессиональная Professional Edition и коммерческая Interprise Edition. На этом сайте используется профессиональная русифицированная версия Microsoft Visual Basic 6 Professional Edition, но в общем-то подойдет любая.

Короче, идешь на рынок и покупаешь пластинку с VB. Если ты не в ладах с английским - это очень плохо, но преодолимо. Тогда лучше покупать русифицированную версию (это - где кое-что переведено на русский). Скорее всего тебе предложат одну пластинку ценой 100-150 рублей, и называться она может как угодно: “Visual Basic 6.5” “Visual Basic 6.2”. Это значит, что перед тобой пиратская версия продукта Microsoft. К настоящему VB 6 идет приложение MSDN, т.е огромный Help на трех пластинках, да и цена его совсем другая. Однако нам на это наплевать, сгодится и пиратский. Главное, чтобы он работал. Потому-что поначалу HELP на английском тебе не очень поможет. Для начала можно пользоваться Help'ом для VB 5. Скачать его можно вверху страницы. Но на самом деле MSDN - очччень полезная штука.

Значит, приходишь домой, включаешь свой компьютер (если ты хочешь научится программированию, значит знаешь, какой кнопкой это сделать) и устанавливаешь VB себе на жесткий диск следуя инструкциям на пластинке.Он у тебя по умолчанию пропишется в “C:\Program Files\Microsoft Visual Studio” (это путь, если кто не знает) и на рабочем столе появится иконка. Художественной красоты она не имеет, но позволит нам этот VB запустить, что и нам надо. Ну, теперя жмакаем на иконку пару раз и радостно грузим этот самый VB. На экране у нас должен появиться он, наш родимый. Поверх него будет окно “Новый проект” (или “New Project”), которое дает нам возможность выбрать, чего мы там будем создавать. Выглядеть это должно примерно так как на рисунке 1. А поскольку мы будем делать exe-программы, а выделена иконка “Стандартный EXE”, то нам и выбирать-то ничего не надо, просто щелкаем по кнопке “Открыть” (“Open”).

Рисунок 1.

Ну вот, теперь за дело.

Copyright © 2004 4us


Visual Basic с нуля. Глава1. Форма.

Глава 1. Форма. Что с ней делать?
Скачать исходник программы "Форма"
 Дата создания 02.10.2004 {Автор 4us}

Загружай VB, и в путь. Итак создаем первый “Стандартный EXE” (как в Главе 1). На экране имеем примерно такую картинку:

Рисунок 2.

Теперь мы найдем место, где будем заносить собственно программу. Для этого в верхнем ряду окна нажимаем кнопку “Вид”. В выпавшем меню первая строка и есть та, которая нам нужна: “Код”. Показано на рисунке 3.

Рисунок 3.

У нас поверх окна формы Проект1 – Form1 (Форма) появится окно Проект1 – Form1 (Код). Сверху этого окна у нас два выпадающих списка. Слева - список доступных объектов, справа – события для этих объектов. Объектов у нас сейчас два – (General) и Form. Жмем на строку Form. В поле окна кода появились две строки. Что это значит. Первая строка:

Private Sub Form_Load()

говорит о том, что мы создали подпрограмму (Private Sub) или правильней процедуру, которая будет выполняться при загрузке (_Load) нашей формы (Form). Последняя строка

End Sub

просто говорит о том, что процедура завершена. Вот только выполнять-то пока нечего. В теле процедуры у нас нет ничего. А поэтому мы сейчас туда чего-нибудь напихаем. Первым делом напишем в нашей форме какой-нибудь текст. По идиотской традиции самая первая программа должна выводить на экран надпись типа “Hello, world” (Здравствуй мир). Мы напишем “Я стану программистом”. Для этого вписываем между двух уже имеющихся у нас строк еще одну. У нас будет

Private Sub Form_Load()

Form1.Print "Я стану программистом"

End Sub

Теперь можно бы посмотреть как это работает, только ни черта у нас не выйдет. Чтобы эта надпись появилась в форме надо залезть в окно “Свойства – Form1”. На рис. 2 (см. выше) оно подписано как “Свойства выделенного объекта” и в левой колонке найти свойство “AutoRedraw”. В этой строке только в правой колонке из выпадающего списка выбрать “True”. Теперь окно перерисуется вместе с нашим чудесным текстом. После этого нажмем кнопочку для стрелочку для запуска нашей программы (показано на рис.4) и наслаждаемся ее работой.

Рисунок 4.

Свойство перерисовки, как и всякое другое можно задавать программно. Это намного правильней, так как в ходе выполнения программы часто приходится менять свойства. Перед нашей строчкой добавим еще одну:

Form1.AutoRedraw = True

А в свойствах формы изменим AutoRedraw с True (Истина) на False (Ложь). Запустим программу. Она работает совершенно также. Аналогично можно менять другие свойства формы. В дальнейшем по мере необходимости мы будем обращаться к ним.

Ясное дело, форму можно растягивать. Можно менять ее положение на экране, двигая квадратик в окне “Размещение формы”.

Еще одно. Можно создавать несколько форм и управлять объектами одной из другой. Для этого в меню выберите “Проект – Добавить форму – Открыть”.

Только если после этого запустить программу, второй формы не увидишь. Чтобы сделать ее видимой, надо прописать такой в Form1 код:

Form2.Show

Теперь наш проект надо сохранить. Выбери “Файл – Сохранить проект” или нажми на изображение дискетки. Появиться окно. Но лучше сохранять не туда, куда предлагает VB, а создать папочку для этого, так как проект включает в себя несколько файлов. Чтобы не получилась каша из файлов разных проектов, файлы каждого надо складывать в свою директорию. Сохраняй пока файлы под теми же именами, которые предлагает VB.

Последний этап – создание выполняемого (т.е. EXE) файла, то ради чего мы это все делали. Нажми “Файл – Создать Проект1.exe…” сохраняй и готово. Теперь ты можешь закрыть VB и запускать свой EXE-файл. Поздравляю. Ты создал первую программу на Visual Basic. Вариант этой программы, только несколько расширенный, я привожу ниже. В ней показано изменение некоторых свойств формы. Скопируй ее в тело подпрограммы и поэкспериментируй с ней. Обрати внимание на комментарии. Они выделяются зеленым цветом и пишутся после апострофа. Нужны они только для пояснений и программой не выполняются.

'******вот некоторые возможности работы с формой*****

Private Sub Form_Load()
Form1.AutoRedraw = True 'включает перерисовку формы 1
Form1.BackColor = vbBlack 'меняет цвет формы 1 на черный
Form1.ForeColor = &HFF& 'меняет цвет текста формы 1 на красный
Form1.BorderStyle = 3 'меняет рамку (бордюр) формы 1 на неизменяемую
Form1.Font.Size = 18 'меняет размер шрифта формы 1 на 18
Form1.Font.Underline = True 'подчеркивает текст в форме 1
Form1.Caption = "Моя первая программа" 'устанавливает заголовок формы 1
Form1.Print "Я стану программистом" 'выводит текст на форму 1
Form2.Show 'делает видимой форму 2
Form2.Width = 9000 'устанавливает ширину формы 2 равной 9000 твипов
Form2.WindowState = 1 'сворачивает форму 2
End Sub

Скачать код можно вверху страницы.

Copyright © 2004 4us


Visual Basic с нуля. Глава 2. Основные компоненты формы.

Глава 2. Основные объекты формы.
Скачать исходник программы "Калькулятор"
 Дата создания 07.10.2004 {Автор 4us}

Итак, сейчас мы сделаем небольшую программу с использованием наиболее употребимых компонентов формы. Запустим VB. У на откроется окно формы Проект1 – Form1 (Form). Слева, там где компонеты формы найдем командную кнопку (желтая подсказка CommandButton). Щелкнем по ней, а потом мышь переведем на поле формы и нажав на клавишу мыши растянем пунктирный прямоугольник до нужного нам размера. Отпустив клавишу мыши мы увидим, что на форме появилась командная кнопка с надписью Command1 (Рис.5). Справа, в окне свойств мы увидим свойства этой кнопки, так как выделена именно она.

Рисунок 5.

Добавим на форму еще пять кнопок(Command), два лейбла(Label) и три текстбокса (TextBox). У тебя должно получится что-то вроде того (Рис.6)

Рисунок 6.

Попробуем из всего этого барахла сделать калькулятор. В окне свойств для каждой кнопки уберем в свойстве Caption надпись «Command…» и впишем для каждой кнопки соответсвенно +, -, * ,/, Очистить, Корень. Это будут названия наших кнопок. Теперь, чтобы надписи на кнопках читались лучше, в окне свойств щелкнем по свойству Font и в открывшемся окне Font выберем додходящий шрифт, размер и жирность. Вообще-то дизайн – это личное дело каждого и в дальнейшем я на этом подробно останавливаться не буду. Короче у тебя должно получиться что-то типа на рис. 7.

Рисунок 7.
Пора уже код писать, а мы все кнопочки двигаем. Поэтому перейдем теперь к окну кодов (в верхней строке нажимаем «Вид» и выбираем «Код». В левом окне Проект1 – Form1(Код), там где “(General)” из выпадающего меню выбираем строчку Form. У нас появилась процедура загрузки формы, как в главе 1. Ну меняем заголовок формы и убираем некрасивые записи в леблах и тексте. В теле процедуры пишем:

Form1.Caption = "Калькулятор"

Чтобы вписать что-нибудь в Поле Text, нужно использовать свойство Text. У лейбла аналогичное свойство Саption.

Text1.Text = "" 'присвоим текстбоксам
  значение
Text2.Text = "" 'пустой
  строки
Text3.Text = ""
Label2.Caption = "Действие" 'Присвоим лейблам
Label1.Caption = "Результат:" 'соответствующие
  значения

Теперь для каждой кнопки напишем свою подпрограмму для обработки каждого действия.

В поле (General) выбираем Command1. У нас появилась новая процедура для первой кнопки (у меня она соответствует «+»). Она очень похожа на процедуру формы, только событие здесь другое – нажатие кнопки (Click). Пишем строку для обработки операции сложения. Однако надо учесть, что в текстбоксах содержится строковая информация, а сложение двух строк приводит к их сцеплению, т.е. если в Text1 у нас будет 10, а в Text2 – 5, то результатом сложения в Text3 станет 105 (10 & 5), а не 15, как нам думалось. Поэтому нам придется преобразовать каждое значение в числовое с помощью функции Val(строка):

Text3.Text = Val(Text1.Text) + Val(Text2.Text)

Кроме того, чтобы пользователь видел, какую операцию он произвел, изменим содержимое верхнего лейбла:

Label2.Caption = "Плюс"

Используя стандартные операторы арифметических действий, создадим подпрограммы для всех кнопок.

На всякий случай я приведу основные математические операторы в табличке:

Оператор | Название оператора | Пример операции | Результат
+ | сложение | c=2+5 | с=7
- | вычитание | c=2-5 | с=-3
* | умножение | c=2*5 | с=10
/ | деление | c=2/5 | с=0,4
\ | целая часть от деления | c=2\5 | с=0
Mod | остаток от деления | c=2 Mod 5 c=9 Mod 4 | c= 2 (5*0+2) c= 1 (4*2+1)
^ | возведение в степень | с=2^5 | с-32
& | слияние строк (конкатенация) | c=2 & 5 | с=25
= | Оператор присваивания, а никакое не “равно” | с=с+2 | если с было равно 5, то станет 7

Для того, чтобы кнопка Command6 выполняла извлечение квадратного корня используем функцию. Она будет выглядеть так: Sqr(число). Вот весь код этой программы. Исходник программы “Калькулятор” можно скачать вверху страницы. Да, вот еще, что. Если мы будем делить на ноль, программа аварийно прервется, и появится сообщение об ошибке. Как это преодолевать, мы поговорим несколько позднее.

‘****************************** НАЧАЛО***********************

Private Sub Command1_Click() ‘подпрограмма нажатия кнопки Command1
Text3.Text = Val(Text1.Text) + Val(Text2.Text) 'операция сложения
Label2.Caption = "Плюс" 'указаваем в метке математическую операцию
End Sub

Private Sub Command2_Click()
Text3.Text = Val(Text1.Text) * Val(Text2.Text) 'операция умножения
Label2.Caption = "Умножить"
End Sub

Private Sub Command3_Click()
Text3.Text = Val(Text1.Text) - Val(Text2.Text) 'операция вычитания
Label2.Caption = "Вычесть"
End Sub

Private Sub Command4_Click()
Text3.Text = Val(Text1.Text) / Val(Text2.Text) 'операция деления
Label2.Caption = "Разделить"
End Sub

Private Sub Command5_Click()
Text1.Text = "" 'присвоим
  текстбоксам значение пустой строки
Text2.Text = ""
Text3.Text = ""
End Sub

Private Sub Command6_Click()
Text3.Text = Sqr((Text1.Text)) 'функция вычисления корня
Text2.Text = "" 'очистка Text2, так как
  он не нужен при этой операции
Label2.Caption = "Кв. корень"
End Sub

Private Sub Form_Load() ‘процедура
  загрузки формы
Form1.Caption = "Калькулятор" 'меняем
  заголовок формы
Text1.Text = "" 'присвоим текстбоксам
  значение пустой строки
Text2.Text = ""
Text3.Text = ""
Label2.Caption = "Действие" 'присвоим лейблам
Label1.Caption = "Результат:" 'нужные
  значения
End Sub

‘****************************** КОНЕЦ***********************

Пожалуйста, обратите внимание, что VB выполняет операторы не в том порядке, в каком они записаны в окне кода. Когда вы запускаете программу, загружается форма и наступает событие Form_Load. И все. Выполняются только те операторы, которые написаны в теле этой процедуры. После программа, грубо скажем, останавливается. Если же мы нажмем, например кнопку Command1, то возникает событие Command1_Click и выполняются операторы только в процедуре первой кнопки. Теперь вы можете сами доработать программу, поменять шрифты, расположение, добавить новые кнопки для новых математических операций.

Да, поскольку мы уже использовали математические функции “Извлечение квадратного корня” и “Преобразование строки в число”, я перечислю остальные:

Abs (n) абсолютное значение n
Atn (n) арктангенс n в радианах
Cos (n) косинус угла n (n - в радианах)
Exp (n) константа e в степени n
Rnd (n) генерирует случайное число от 0 до 1
Sgn (n) равно –1, если n<0; 0 если n=0; +1 если n>0
Sin (n) синус угла n (n - в радианах)
Sqr (n) квадратный корень n
Str (n) преобразует число n в строку
Tan (n) тангенс угла n (n - в радианах)
Val (n) преобразует строку n в число

Copyright © 2004 4us


Visual Basic с нуля. Глава 3. Переменные, оператор If, таймер.

Глава 3. Переменные. Инструкция If...Else. Таймер.

Скачать исходник программы "Часики"
или "Однорукий бандит"

 Дата создания 15.10.2004 {Автор 4us}

Переменные. Option Explicit.

Все, пора поговорить о переменных. Без них ничего делать мы больше не можем. Я не буду долго и нудно грузить тебя теорией. Но минимум знать надо.

Очень желательно, чтобы программа начиналась с инструкции Option Explicit. Она будет требовать явно объявлять переменные. То есть, если ты написал имя переменной с ошибкой, то при запуске программы VB найдет это место и ткнет тебя носом, потому что имя переменной с ошибкой не совпадает с именем объявленной переменной. Переменную надо объявлять явно с помощью инструкции Dim.

Сейчас я поясню эту муть на примере, иначе сам не понимаю, что написал.

‘объявляем явно переменную MyStroka как строковую (т.е. текстовую)

Dim MyStroka As String

‘потом присваиваем ей значение “переменная”, но имя переменной ‘написано с ошибкой

MySroka=”переменная”

При запуске программы VB сразу найдет это место, как ошибку, и выделит его. Если у нас не будет прописана инструкции Option Explicit, то наша ошибочная переменная будет определена, как новая. Но значение правильной переменной MyStroka мы не присвоили, потому что присвоили ее новой переменной с ошибкой в имени. Тогда в дальнейшем, когда мы захотим получить значение правильной переменной, его в ней не просто не будет.

Вот вроде понятно.

Чтобы не дурить себе всем этим голову, нажми кнопку “Инструменты” (Tools), выбери “Опции” и в окрывшемся окне ОПЦИИ на вкладке “Редактор” поставь галочку напротив надписи “Требовать определение переменной”, потом OK. Теперь при создании новой программы, (Но только новой. В старую эту инструкцию надо прописывать в (General) вручную.) VB автоматически пропишет Option Explicit.

Таким образом любую надпись, кроме зарезервированных VB слов можно сделать переменной. Но начинаться имя должно со буквенного символа. Например Peremenaia, Ima1, X, LastNumber, z001. Не надо использовать в именах математические операторы. И нельзя объявлять две переменные с одинаковыми именами. В принципе, для удобства чтения кода и солидности существуют определенные правила написания имен переменных. Но я их придерживаться не буду. Если охота, поищи в Интернете что-нибудь про Венгерское соглашение.

Нас же интересуют переменные следующего типа

Long ‘длинное целое число со знаком от –2 147 483 648 до 2 147 483 647

Integer ‘целое число со знаком от –32 768 до 32767

String ‘строка символов

Boolean ‘логическое, принимает два значения или “True” (Истина) или “False” (Ложь)

Пока что нам этого хватит. Если интересно, то более подробно типы данных, поддерживаемые VB можно посмотреть здесь.

Теперь, где их объявлять. Если ты объявишь переменную в теле процедуры объекта, например кнопки, то доступна она будет только в этой процедуре. В другой, скажем, кнопке, ее использовать нельзя. Чтобы переменную использовать во всей форме, надо объявить ее в разделе деклораций (это в самом верху кода –(General) – (Declarations). Ну а чтоб она была видна во всей программе (в разных, например, формах) она должна быть объявлена в стандартном модуле с ключевым словом Public. Но модулей мы пока не создавали и оставим это до поры в стороне. Ну вот, на первое время этого о переменных хватит. Чтобы было совсем понятно, вот пример объявления переменных:

Option Explicit

Dim MyStream As
  String
Dim K14 As Long
Dim Flag As Boolean
Dim Numder As Integer

Private Sub Form_Load()
Dim Flag2 As Boolean
Dim l99little As String
End Sub

Таймер. Программа “Часики”.

Теперя, таймер. Слева, где компоненты формы, они же инструменты-элементы, нарисован такой будильничек. Это таймер и есть. Он всегда невидимый, но нужный элемент. Всего их одновременно можно использовать около 30 штук. Но нам столько их до фига не надо. Поместим один его на форму. На основе его мы сделаем сейчас маленькие часики. В свойствах этого таймера поставьте следующие установки:

Enabled – False (по умолчанию True) означает, что таймер выключен

Interval – 1000 (время, в миллисекундах, через которое таймер срабатывает, если конечно включен). Таймер будет срабатывать каждые 1000 мск, т.е. каждую секунду, как в нормальных часах.

Кроме того, нам понадобятся три лейбла: Label1 для секунд, Label2 для минут и Label3 – сами догадайтесь для чего. Правильно, для отображения соответственно часом, минут и секунд. Все.

Поставьте в свойствах лейблов такие установки:

Alignment – 2-Центровка (Это чтоб цифры были по центру лейблов)
BorderStyle – 1-Фиксировано (это чтоб размер формы нельзя было изменить)
Caption – 00 (на всякий случай, т.е. значение лейблов изначально будет “00”)

Нажмите Font и выберите размер шрифта 14, а начертание – полужирное. После этого подберите подходящий размер окон Лейблов, чтобы цифры в них выглядели покрасивше.

Должно получиться как на рис.8.

Рисунок 8.

Сделай подпрограмму Form_Load .

Option Explicit

Private Sub Form1_Load()

Затем в теле процедуры, как у всякой приличной программы меняем заголовок формы.

Form1.Caption = "Часики"

Дальше, для того, чтобы наши часы сразу показывали текущее время, надо его взять из компьютера. Чтобы получить системное время есть функция Time (Label1.Caption=Time). Но она дает полное время одной строкой (16:23:42). Нам надо выбирать из нее секунды, минуты и часы, чтобы распихать по свои лейблам. Для этого возьмем готовые функции, которые предоставят нам это все по частям:

Label1.Caption = Second(Time) 'стало быть секунды
Label2.Caption = Minute(Time) 'ну минуты
Label3.Caption = Hour(Time) ' и часы

Теперь, если мы запустим программу, у нас будет показываться текущее время, но вот только часики-то стоят.

Запустим таймер.

Timer1.Enabled = True

Вот она, булева алгебра. То есть Timer1.Enabled может быть либо включен (True), либо выключен (False).

Процедура Form1_Load()закончена

End Sub

Чтобы получить хоть какие-нибудь блага от запущенного таймера, создадим процедуру, которая будет исполняться при наступлении события срабатывания таймера

Private Sub Timer1_Timer()

Наши секунды должны увеличиваться на одну за каждое срабатывание таймера. Поэтому пишем счетчик

Label1.Caption = Label1.Caption + 1

И вот мы вплотную подошли к проблеме условного оператора If. или инструкции If. . Суть этой инструкции в том, что он позволяет при неком создавшемся условии производить изменения в ходе программе.

Оператор условия If.

По-русски оператор If будет выглядеть следующим образом:

Если Вася любит Машу Тогда
Маша клевая

В противном случае Если Вася любит Петю Тогда 'если не исполняется верхнее условие
Вася козел

............

И в противном случае 'если не выполняется ни одного условия
Вася дурак
Конец Если

Куски оператора If выделены синим цветом. Если записать это программно, то

If Vasia+Masha =”Love” Then
Masha=”Good”

ElseIf Vasia+Petia=”Love” Then
Vasia=”Kozel”

Else
Vasia=”Durak”
End If

Оператор If проверяет первое условие, верно ли (True) выражение Vasia+Masha=”Love”. Если верно, выполняются операторы, стоящие за ним, но до ветки ElseIf. Если выражение неверно (False), то следующие за первой строкой операторы не выполняются, а VB переходит к следующей части оператора ElseIf и так пока не проверит все условия. Если ничего не выполняется, обрабатываются операторы, стоящее за веткой Else.

Все очень просто. Это вообще мой любимый оператор. Без него – никуда.

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

If Vasia+Masha =”Love” Then
Masha=”Good”
Else
Vasia=”Durak”
End If

Это, так называемые, блоковые формы оператора If, самые длинные. Можно его использовать в одну строку, т.е. если выполняется условие, то то-то:
If Vasia+Masha =”Love” Then Masha=”Good” Else Vasia=”Durak”
Или
If Vasia+Masha =”Love” Then Masha=”Good”

А теперь применим знание этого оператора на практике. Чтобы у нас показания времени в нашей программе «Часики» до десятка были с ноликами 01, 02, 03, а не 1, 2, 3 мы проверяем условие, меньше ли длина числа, записанного в лейбле двух. Если да, то прибавляем впереди нолик. Для этого используем функцию Len("строка"), которая определит число символов в строке. Если в строке, например 5 символов, то Len даст число 5. У нас получится:

If Len(Label1.Caption) < 2 Then Label1.Caption = "0" & Label1.Caption ‘для первого лейбла

Но ведь и при загрузке системного времени в подпрограмме Form1_Load нам тоже нужна такая проверка. Не будем же мы переписывать одно и тоже в двух разных процедурах. Мы сделаем свою процедуру и будем обращаться к ней по мере необходимости. Назовем ее, к примеру Zero. В конце нашей программы на свободном месте набираем на клавиатуре:

Private Sub Zero()
If Len(Label1.Caption) < 2 Then
  Label1.Caption = "0" & Label1.Caption
If Len(Label2.Caption) < 2 Then
  Label2.Caption = "0" & Label2.Caption
If Len(Label3.Caption)< 2 Then
  Label3.Caption = "0" & Label3.Caption
End Sub

Таким образом у нас теперь есть своя собственная подпрограмма, которая называется Zero. Никаких событий для ее выполнения нет, и чтобы она работала, в нужном месте, просто пишем ее название Zero. А нужные эти места в последней строчке в порцедурах Form1_Load() и Timer1_Timer() прямо (перед End Sub).

Ну вот, с ноликами все в порядке, зато часы не работают. В подпрограмме Timer1_Timer() у нас всего один оператор. Чтобы часы работали, как настоящие, нам надо, чтобы, когда секунд становилось больше 59, они обнулялись, при этом минуты увеличивались бы на один. Используя оператор If это плевое дело:

If Label1.Caption > 59
  Then
Label1.Caption = "0"
Label2.Caption = Label2.Caption + 1
End If
'То же самое для Лейбла2:
If Label2.Caption > 59 Then
Label2.Caption = "0"
Label3.Caption = Label3.Caption + 1
End If

Ну, а для Лейбла3 вообще фигня. Надо, чтоб, если больше 23 часов, показывало не 24, а 00:

If Label3.Caption > 23 Then Label3.Caption = "0"

Теперь ставим ссылочку на нашу программу проверки ноликов Zero :

Zero

И готово, т.е.

End Sub

Вы ребята, извиняйте, программа всего 27 строчек. Я разжевал ее как мог. Код прописан ниже от и до. Кто хочет, может скачать исходник программы вверху станицы.

Option
  Explicit

Private
  Sub Form_Load()
Form1.Caption = "Часики"
Label1.Caption = Second(Time)
Label2.Caption = Minute(Time)
Label3.Caption = Hour(Time)
Timer1.Enabled = True
Zero
End Sub

Private
  Sub Timer1_Timer()
Label1.Caption = Label1.Caption + 1
If Label1.Caption > 59 Then
Label1.Caption = "0"
Label2.Caption = Label2.Caption + 1
End If
If Label2.Caption > 59 Then
Label2.Caption = "0"
Label3.Caption = Label3.Caption + 1
End If
If Label3.Caption > 23 Then Label3.Caption
  = "0"
Zero
End Sub

Private
  Sub Zero()
If Len(Label1.Caption) < 2 Then
  Label1.Caption = "0" & Label1.Caption
If Len(Label2.Caption) < 2 Then
  Label2.Caption = "0" & Label2.Caption
If Len(Label3.Caption) < 2 Then
  Label3.Caption = "0" & Label3.Caption
End Sub

Теперь вот что. Вам надо самостоятельно разобраться с другой программой – «Однорукий бандит». Если сможете, будете программистами, если нет, пишите, попробую объяснить. Я даю исходник с подробнейшими-преподробнейшими комментариями. Скачайте сверху страницы.

«Однорукий бандит» - имитация работы известного аппарата для азартных игр. Смысл программы в следующем. В трех Лейблах отображаются меняющиеся цифры от 1 до 7. Для работы с ними используются три переменные Number. Их изменение управляется тремя таймерами. Первоначально интервалы у таймеров одинаковы – 10 мск. В трех переменных Zamedlenie генерируются случайные числа.

Zamedlenie1 = Int(Rnd * 20) + 10

Работает это так. Rnd генерирует псевдослучайное число от 0 до 1. На двадцать умножаем, чтобы получить целую часть числа (можно умножать на что угодно, но не меньше 10, а то не получится целая часть). Функция Int отбрасывает дробную часть. Прибавляем в конце 10, чтобы исключить значение 0, а то таймер не остановится. Т.е., если Rnd генерирует, например, число 0,156, то

Zamedlenie1= Округляем (0,156*20) + 10 = 3 + 10 = 13. Очень просто.

При каждом срабатывании таймеров увеличивается его интервал на значение замедления, пока Интервал не становится больше 300 (это задано программно: Timer1.Interval > 300

После этого таймер выключается. Чтобы узнать, все ли таймеры выключились, я использую переменную Flag, которая накапливает в себе количество выключившихся таймеров. Когда Flag=3, считаем, что все три таймера закончили работу (а так оно и есть) и начинаем обрабатывать результаты ( подпрограмма Raschet() ). Изначальный капитал игрока храниться в переменной Summa, а при различных выигрышах она изменяется на сумму трех переменных Bonus, а при проигрыше на переменную BonusMinus. Почему ее значение отрицательное? Просто мне так захотелось, хотя это, конечно, извращение. В Лейблах 4 и 5 выводятся всякие прикольные надписи, в зависимости от результата игры. Ну и кроме того, чтобы во время работы таймеров кнопка не нажималась, она по состоянию переменной FlagClick либо позволяет запустить таймеры, либо меняет свою надпись.

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

В этой программе мы впервые объявляем переменные. Ничего в этом сложного нет. Главное, чтоб тип переменной приблизительно соответствовал нашим надобностям. Просто посмотри, как это деляется.

Ну вот, практически и все. Остальное ты должен додумать сам. Удачи.

Copyright © 2004 4us


Visual Basic с нуля. Глава 4. ASCII-код. ScrollBar. Цикл For...Next. Текстовые функции.

Глава 4. ASCII-код. Объект формы ScrollBar. Цикл For...Next .
Скачать исходник программы ASCII-reader
 Дата создания 21.10.2004 {Автор 4us}

ASCII-код.

В этой главе мы попробуем поработать с объектом формы ScrollBar'ом. А попутно будем знакомится с новыми операторами и прочей фигней. Все рассмотрим на примере программы ASCII-reader. Объясняю смысл. Мы в текстовое поле Text1 будем записывать различные символы, а наша программа будет брать их по одному и представлять в виде ASCII-кода. В этой программке мы заодно разберем и цикл For..Next.

Что такое ASCII-код? Как известно, компьтер буковок-то и не различает. А чтобы он отличал А от Б, каждому символу присваивается свой ASCII-код. Например строчная латинская буква a имеет код 97, а заглавная латинская А - 65. Всего кодов 255: с 0 по 31 - это управляющие коды, такие, как табуляция, возврат каретки и т.п.; с 32 по 127 -это символу буковок, циферек и знаков и всяких знаков препинания; с 128 по 255 - это символы псевдографики, иностранные алфавиты (расширенная таблица символов). И вот когда мы шлепаем по клавиатуре, она, т.е. клава, и генерирует ASCII-код того, что нарисовано на кнопках.

Чтобы узнать конкретный ASCII-код имеющегося у нас символа, используется функция

Asc ("символ")

А, чтобы наоборот, узнать, какой символ соответствует конкретному коду, есть обратная функция

Chr (номер кода)

Эту теоретическую байду должен знать каждый уважающий себя пацан.

ScrollBar.

Если ты обратил внимание, почти любая программа под Windows имеет такие ползунки, позволяющие пользователю перемешаться по данным вверх-вниз или влево-вправо. Есть они и у нас. Называется этот объект ScrollBar. Сам объект получает имя по умолчанию HScroll1, если это вертикальный ползунок, или VScroll1, если - горизонтальный. Свойства и события у обоих одинаковые, поэтому, чтобы не раздувать код, используем один СкроллБар - вертикальный. Я использую в основном его.

Итак, основные рабочие свойства ScrollBar это:

.Value - это главное значение, оно меняется, когда мы двигаем ползунок ScrollBar' а.

.Max - задает максимальное значение, до какого будет менятся значение .Value. Главный недостаток этого объекта в том, что значение .Max, а стало быть и .Value не моджет превышать 32767. Это всегда надо учитывать при работе с этим объектом.

.Min - задает минимальное значение .Value. Здесь тоже ограничения до -32768.

.SmallChange - задает, насколько меняется .Value, если мы кликаем мышью по стрелочкам вверху или внизу объекта ScrollBar

.LargeChange - задает, насколько меняется .Value, если мы кликаем мышью по полю скроллбара, а не по стрелочке. т.е. соответствует, как-бы листанию страниц.

Чтобы реально посмотреть, как работает этот объект, создадим новый проект и положим на форму Text1 и VScroll1. Затем дважды щелкнем мышью по объекту VScroll1. У нас откроется окно кода и создастся процедура VScroll1_Change().

Впишем в нее такую строчку кода: Text1.Text = VScroll1.Value и запустим программу. Как только мы начинаем двигать ползунок, в текстовом поле отражается значение VScroll1.Value от 0 до 32767 (поскольку мы ничего не задали иначе.

Остановим программу и создадим процедуру Form_Load(), выбрав в верхнем левом окне кода значение Form и щелкнув по нему. Впишем в эту процедуру значения свойств, о которых мы только-что говорили. Должно получиться следующее:

Option Explicit

Private Sub Form_Load()
VScroll1.Value = 0
VScroll1.Max = 32767
VScroll1.Min = -32768
VScroll1.LargeChange = 100
VScroll1.SmallChange = 1
End Sub

Private Sub VScroll1_Change()
Text1.Text = VScroll1.Value

End Sub

А чтобы ползунок не мигал так противно, поставим в окне свойств VScroll1 свойство TabStop в значение False. Запустим программу. Значение .Value мы задали 0, а поскольку интервал у нас от 32767 до -32768, то ползунок стоит посредине. При щелканьи мыши по стелочкам вверху и внизу, значение .Value изменяется на 1, а при щелканьи по полю VScroll1 - на 100. Рис. 8 должен помочь тебе во всем разобраться.

Рисунок 8.

После того, как мы разобрались, как в принципе работает ScrollBar, применим наши знания на практике. Мы нашу учебную заготовку переделаем в программку для определения ASCII-кодов.

Добавим на форму текстовые поля (Text2 и Text3) Сделаем их небольшими прямоугольниками и расположим их в ряд под большим полем Text1, которое у нас уже есть. Щелкнем по нему правой кнопкой мыши и в меню выберем "Копировать", потом щелкнем правой кнопкой по форме и в выпавшем меню выберем "Вставить". Появится меню запроса, создавать ли control array (Рис 9).

Рисунок 9

Если мы выберем НЕТ, то на форму просто скопируется новое текстовое поле Text4. Мы же выберем Да. Тогда у нас создастся группа текстовых полей под названием Text2. Имена у них будут Text2(0), Text2(1) и т.д. Повторим эту операцию еще три раза с тем, чтобы у нас получилось пять полей Text2 (от 0 до 4) и расположим их столбиком. Тоже сделаем с объектом Text3. В Textы2 у нас будут выводится символы из Text1 по отдельности, а в Textы3 - значения ASCII-кода. Еще добавим кнопку Command1. Выглядеть это должно примерно следующим образом (Рис.10):

Рисунок 10.

А теперь спрашивается, нафига мы выбрали создание Control array? А именно из-за этих циферек в скобочках. Все пять TextBox'ов представляют собой как-бы один объект (массив текстбоксов). И мы можем перебирать все его элементы по номеру в скобках. Это свойство и называется оно .Index. Кроме того, чтобы результы работы нашей программы выглядели по-привлекательнее, в окне свойств всех элементов Text2 и Text3 (их можно выделить все разом) кликните на свойство Font и в окне "Выбор шрифтов" выберем начертание - полужирное, а размер - 14. Кроме того, чтобы выставить наши данные по центру, свойство Alignment поставим 2- центровка. Ну и уж, чтобы не выводились дурацкие надписи Text2 и Text3 сотрем свойство Text (это уж по одному надо делать). Для кнопки Command1 в окне свойств в свойстве Caption напишем слово КОД.

Теперь пропишем в процедуре Form_Load() следующее:

Form1.Caption = "ACSII-reader"
Text1.Text = "Сюда можно ввести всякие буковки и циферки , Fuck их!"

Первая строка определяет название нашей формы, а вторая заполняет Text1 каким-нибудь текстом. Чтобы текст переносился по строкам, нужно в окне свойств Text1 свойство MultiLine поставить в состояние True. Кроме того сюда же можно добавить полосу прокрутки (свойство ScrollBars в 2 - вертикаль). Это будет ваще круто!

Цикл For ... Next.

Теперя вот что. Далее мы будем использовать цикл For ... Next. Это очень важная и удобная вещь. И используется почти в каждой программе. Выглядит он так:

For переменная=число To другое число Step Шаг

Всякие операторы

Next переменная

Здесь переменная - это числовая, обязательно объявленная оператором Dim переменная, число - любое число с которого начинается цикл, но может быть и переменной, другое число - это число, которым цикл заканчивается, Шаг - это на сколько за один цикл изменяется переменная. Если не ставить, то по умолчанию Step принимается за единицу. Всякие операторы - это любой программный код, который ты вставишь. Он будет выполняться каждый цикл. Например такой цикл

For x=0 To 10 Step 2
Text1. Text = Text1.Text &" " & "x"
Next x

добавляет изменяющееся значение x в Text1. Цикл сработает шесть раз, в первый проход x примет значение 0, и в Text1 запишется 0, во второй переменная x примет значение 2 (0+Step), в третий 4 и т.д. В Text1 выведется

0 2 4 6 8 10

Далее в разделе (General) руками, т.е. с помощью кнопок на клавиатуре, запишем объявление необходимых нам переменных, собственной одной:

Option Explicit
Dim ZnachenieScroll As Long 'сюда будет считываться значение 'Vscroll1.Value,
' которое будет изменяться всякий раз при изменении положения 'ползунка

Значения переменной будут доступны всем процедурам нашей формы.

В процедуре Form_Load() изменим операторы и свойства, чтобы получилось следующее:

Private Sub
  Form_Load()
VScroll1.Value = 1
VScroll1.Min = 1
VScroll1.LargeChange = 5
VScroll1.SmallChange = 1
Form1.Caption = "ACSII-reader" '
  это заголовок нашей формы(вместо Form1)
Text1.Text = "Сюда можно ввести всякие буковки и циферки , Fuck их!"
  ' этот текст вводится автоматом в Text1
ShowKod ' это имя нашей процедуры, которую мы создадим
  ниже
End Sub

В теле процедуры уберем нашу тестовую строчку и напишем две новые:

Private Sub VScroll1_Change()
ZnachenieScroll = VScroll1.Value 'записываем текущее значение VSvroll1 в переменную.
' Можно было обойтись и без нее, но я хотел приллюстрировать различное объявление переменных.
ShowKod ' это опять тоже имя нашей процедуры, которую мы создадим ниже
End Sub

Теперь, мы создадим следующую собственную процедуру ShowKod() имя которой мы уже использовали в процедурах Form_Load() и VScroll1_Change(). Разберем ее подробно:

Private Sub ShowKod()
Dim X As Long 'обратите внимание, что здесь мы переменную объявляем локально, внутри процедуры, для организации цикла. Ее значение будет недоступно в других процедурах нашей формы.

'далее мы определяем, сколько символов записано в Text1. Делается это с помощью функции Len(). Затем присваиваем это значение свойству .Max, т.е. наш ползунок не будет давать значение .Value больше, чем количество символов.
VScroll1.Max = Len(Text1.Text)

'Теперь, для того, чтобы выводить наши значения по 5, используем цикл For...Next. Начальное значение цикла принимаем за значение VScroll1.Value, а конечное на 4 больше. Таким образом, если наш ползунок имеет положение, например, 4, то в цикле мы будем считывать сиволы из Text1 4, 5, 6, 7 и 8.

For X = ZnachenieScroll To ZnachenieScroll + 4

'Далее с помощью функции Mid (в ней три аргумента: 1- текст, откуда считываем, 2 - с какого знака, считая слева начинаем считывание и наконец 3 - сколько знаков считываем) мы считываем по одному пять знаков (потому что цикл будет работать пять раз). Но переменную Х мы не можем использовать как есть для определения, в какой из пяти Text2 записать полученный символ. Ведь при значении ползунка, например 5, потребуется объект Text2(5), а у нас его нету, только с 0 до 4. Возникнет ошибка и программа аварийно прервется. Поэтому мы из значения X вычитаем значение ползунка. Тогда мы всегда вложимся в зону 0-4. Подумайте, как это происходит.

Text2(X - ZnachenieScroll).Text = Mid(Text1.Text, X, 1)

'Ну и теперь полученное значение в Text2(X-ZnachenieScroll) мы с помощью функци Asc() преврашаем в ASCII-код. Единственое но. Мы с помощью оператора If (без него никуда) и все той же функции Len() проверяем не пустое ли значение в Text2, т.е. не равна ли длина строки нулю, а то опять возникнет ошибка. Если длина строки - ноль, тогда мы в Text3 вписываем пустое значение (можно что угодно).

If Len(Text2(X - ZnachenieScroll).Text) > 0 Then
Text3(X - ZnachenieScroll).Text = Asc(Text2(X - ZnachenieScroll).Text)
Else
Text3(X - ZnachenieScroll).Text = ""
End If

'Все. конец цикла .
Next X
End Sub

Да, чтобы мы могли вписать любой свой текст и запустить программу по-новой, напишем коротенький код для кнопки Command1.

Private Sub Command1_Click()
ShowKod
End Sub

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

Copyright © 2004 4us


Visual Basic с нуля. Глава 5. MsgBox. Иконки, курсоры. Выход из программы. InputBox. Запрет вызова второй копии.

Глава 5. Функция MsgBox. Иконки, курсоры в программе. Выход из программы. Функция InputBox. Запрет вызова второй копии.
Скачать исходник программы "Я-не вирус"
 Дата создания 23.10.2004 {Автор 4us}

В этой главе, други моя, разберем то, что делает программу удобней и привлекательней для пользователя.

Функция MsgBox для вывода диалогового окна.

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

MsgBox ("сообщение", число и тип кнопок, "заголовок окна", "имя файла справки если есть", номер раздела справки если есть имя файла)

Два последних аргумента необязательны. Они используются, если у тебя создан файл помощи (Help).

"сообщение" - это сообственно сообщение пользователю, например, "Ну ты дурак, нет такого файла" (максимальная длина приблизительно 1024 символа)

"заголовок окна" - это то, что выводится в верхней полосочке (заголовке) окна, например, "Это твоя ошибка"

А вот число и тип кнопок разберем подробнее. Это число получается из складывания трех чисел или констант (кому как удобнее):

  • количество и тип кнопок
  • вид сообщения (какой значок будет в окне)
  • какая кнопка является основной (по умолчанию - первая)
Тип кнопок |  |  | Вид сообщения |  |  | Основная кнопка
константа | или число | выводимые кнопки | константа | или число | значок сообщения | константа | или число | номер основной кнопки
vbOKOnly | 0 |  | VbCritical | 16 |  | VbDefaultButton1 | 0 | первая
VbOKCancel | 1 |  | VbQuestion | 32 |  | VbDefaultButton2 | 256 | вторая
VbAbortRetryIgnore | 2 |  | VbExclamation | 48 |  | VbDefaultButton3 | 516 | третья
VbYesNoCancel | 3 |  | VbInformation | 64 |  | VbDefaultButton4 | 768 | четвертая
VbYesNo | 4 |  |  |  |  | VbApplicationModal (на уровне приложения) | 0 | модальное окно ( не свернется, пока на кнопку не нажмешь)
VbRetryCancel | 5 |  |  |  |  | VbSystemModal (на уровне системы) | 4096

Теперь, например, если нам нужно вывести на экран предупреждающий запрос с тремя кнопками Да, Нет, Отмена и чтоб главной была кнопка Нет, мы пишем цифру 3+48+256 число 307:

PushButton = MsgBox ("Да ты баран! Нажми Нет!", 307, "Нельзя так делать!!!")

или если использовать константы

PushButton = MsgBox("Да ты баран! Нажми Нет!", vbYesNoCancel + vbExclamation + vbDefaultButton2, "Нельзя так делать!!!")

Получится как на рис. 11.

Рисунок 11.

Видишь, Выделена кнопка "Нет". Значит она главная. Это если пользователь давнет клавишу "Enter", то сработает "Нет", а не "Да"

PushButton - это должна быть объявленная переменная. Это я придумал для нее такое имя.В нее записывается число, обозначающее, какую же кнопку нажал пользователь, чтобы мы могли заставить нашу программу среагировать как-нибудь. А числа (вместо них тоже можно писать константы) эти соответствуют следующим кнопкам:

Константа | Число | Какая кнопка нажата
vbOK | 1 | OK
vbCancel | 2 | Отмена (Cancel)
vbAbort | 3 | Прервать (Abort)
vbRetry | 4 | Повторить (Retry)
vbIgnore | 5 | Пропустить (Ignore)
vbYes | 6 | Да (Yes)
vbNo | 7 | Нет (No)

Сечас мы создадим шедевр программирования, где используем диалоговое окно и разберем кое-что еще. Программа наша называется "Я - не вирус", а замечательность ее в том, что она вообще ничего не умеет делать. Смысл ее в том, что, когда пользователь пытается выйти из программы, возникает событие для формы _Unload. Это событие может быть вызвано оператором Unload, выбором команды "Закрыть" из системного меню формы, закрытием приложения из Списка задач (Task List) Windows, закрытием MDI формы, для которой текущая форма является дочерней, или выходом из Microsoft Windows во время работы приложения. При этом значение аргумента этой процедуры Cansel принимает значение False. Присвоение аргументу Cansel любого ненулевого значения предохраняет форму от удаления, но не останавливает другие события, например, выход из Microsoft Windows. Но нам на это налевать. Мы вставляем в эту процедуру MsgBox c запросом на выход. Если нажимается кнопка Отмена выхода, мы присваиваем локальной переменной Cansel значение True, и выход отменяется, а мы переходим к процедуре FikVam(), которая выводит соответствующий текст в форму.

Обратите внимание, что для того, чтобы в диалоговом окне писать сообщение в несколько строк, я использовал CHR(13). А как раз 13 - это ASCII-код перевода строки.

Вот код этого кошмара.

Option Explicit
Dim PushButton As Long ' объявляем переменную для нажатой кнопки

Private Sub Form_Load() ' процедура загрузки формы
Form1.Caption = "Я - не вирус!" 'прописываем заголовок формы
Form1.Print " ************************************" 'пишем на форме честно, что это за штука
Form1.Print " Ты запустил классную программу! "
Form1.Print " Только я ни черта делать не умею!"
Form1.Print " Зато я и вреда не приношу! "
End Sub

Private Sub Form_Unload(Cancel As Integer) 'процедура выгрузки формы
PushButton = MsgBox("Что ж ты гад делаешь!!" & Chr(13) & "Я к те, как к родному, а ты..." & Chr(13) & "Нажми Отмену сечас же!", 273, "Ты же закрываешь меня!") ' вывод диалогового окна при попытке закрыть форму
If PushButton = 1 Then ' условие, если нажата кнопка ОК
Cancel = False ' подтверждаем выход, хотя это необязательно, она и так выйдет
Else ' в противном случае
Cancel = True ' отменяем выход
FikVam 'ссылка на процедуру, которая срабатывает при нижатии Отмена
End If 'конец условия
End Sub

Private Sub FikVam() 'процедура, которая срабатывает при нижатии Отмена
Form1.Cls ' очистка формы
Form1.Print "Вот Молодец! Лучше я, чем вирус" ' вывод текста на форму
End Sub

Как вставить в программу иконку и курсор.

Сейчас сделаем нашу никчемушку красивой. Во-первых заменим идиотский значок Бейсика на наш. Для этого в окне свойств Form1 ткнем в свойство Icon и в открывшемся окне Загрузить Иконку выберем подходящую. В исходнике программы я положил иконку scin.ico для примера. Если у тебя нет иконок, можно пойти двумя путями: найти их в Интернете (их там полно) или изготовить самому. Я считаю, что достойная программа должна иметь свою, уникальную иконку, а не стыренную у других. Мы что, такие безрукие, что иконку сделать не сможем? Ты можешь скачать простую самопальную программку ICA Converter (не мою) для иконкоделанья. Не самая крутая, зато бесплатно и худо-бедно работает.

Теперь выберем для нашей формы другой курсор. Для этого в окне свойств Form1 найдем свойство MousePointer и выберем там 99 - Custom. Это значит, что значком будет тот файл, который мы выберем (расширение должно быть .ico или .cur). Потом ткнем в свойство MouseIcon и в открывшемся окне Загрузить Иконку выберем подходящую (в исходник я положил hns.cur). Надо сказать, что так сказать штатные курсоры Windows лежат в "C:\WINDOWS\Cursors". Ну конечно лучше сделать свой.

Для полного обалдения можно еще воткнуть фоновую картинку в форму Form1, чтобы наш шедевр выглядел привлекательней. Для этого в окне свойств Form1 ткнем в свойство Picture и в открывшемся окне Загрузить Рисунок выберем подходящий. Надо только брать картинку разумного размера. В исходнике я положил картинку 00027.jpg размером 118Kb c небольшой контрастностью, чтобы лучше была видна надпись и подогнал размер формы под нее, а также в окне свойств выставил размер и тип (свойство .Font) и цвет (свойство .ForeColor) шрифта.

Функция InputBox для получения данных от пользователя

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

Синтаксис этой функции таков:

InputBox("сообщение", "заголовок окна", "данные по умолчанию", расстояние между левой границей экрана и левым краем экрана,то же для верхней границы, "имя Help-файла" , номер раздела Help-файла)

Обязательным здесь является только первый аргумент (), но желательно использовать второй и третий. Четвертый и пятый - при необходимости, шестой и седьмой - если есть Help (а у нас его нет). Смысл аргументов такой же, как и в MsgBox'е. Итак

  • "сообщение" - это, что мы пишем для пользователя, например, "Введи свое имя" (максимальная длина приблизительно 1024 символа)
  • "заголовок окна" - это то, что выводится в верхней полосочке окна, например, "Авторизация!"
  • "данные по умолчанию" -это то что выведется в поле окна, как бы предложение юзеру
  • расстояние между левой границей экрана и левым краем экрана - ну это понятно, только расстояние это в Твипах (такая неведомая единица измерения, принятая по-умолчанию в Бейсике, я думаю, что 1 пиксель приблизительно равен 27 твипам, но зуб за это не дам)
  • то же для верхней границы - то же самое, только по вертикали

Последние два аргумента нам пока ни к чему.

Добавим теперь в нашу замечательную программу "Я - не вирус" InputBox так, чтобы при загрузке он очень строго спрашивал пароль юзера, а поскольку тот его не знает, пускал бы и так. Вставим в процедуру Form_Load, сразу после определения заголовка формы (Form1.Caption = "Я - не вирус!") следующий код:

Dim Password As String 'объявим локальную переменную, чтоб записать то, что введет юзер

Password = InputBox ("Этот программный продукт доступен только по паролю." _ 'выведем окно InputBox
& "Если Вы приобрели его законно, Прочитайте пароль на диске и введите его в это окно.", _
"Авторизация пользователя", "Ж4д17") 'это я такой пароль придумал - Ж4д17

If Password ="Ж4д17" Then 'если юзер воспользовался нашим паролем

PushButton = MsgBox ("Ты что, не смог ничего придумать?" & Chr(13) & _
"А пароля, то и нет", 0 + 48, "Результат авторизации")

ElseIf Password = "" Then 'в противном случае, если юзер ничего не ввел или нажал кнопку Отмена
PushButton = MsgBox ("Думаешь, можно так взять и уйти? " _
& Chr(13) & Password & Chr(13) & "Ну-ка заходи, сволочь", 0 + 48, _
"Результат авторизации")

Else ' в противном случае, если юзер ввел свой пароль
PushButton = MsgBox ("Ты что, не смог ничего придумать?" & Chr(13) & _
"А пароля, то и нет", 0 + 48, "Результат авторизации")
End If

Обрати внимание, что так, как некоторые строки кода получаются очень длинными, я использую перенос. Для этого надо поставить курсор в то место, с которого хотите перенести строчку и нажиматьпробел, нижнее подчеркивание и ENTER. Не надо только разрывать строчку, заключенную в кавычки, а то это вызовет ошибку.

Запрет вызова второй копии программы

Теперь, чтоб достойно закончить главу и нашу программу, запретим вызывать нашу программу, если она уже запущена. Для этого перед кодом, который мы только что написали впишем еще четыре строчки:

If App.PrevInstance = True Then
MsgBox "Программа Я - не вирус уже загружена. Не надо вызывать ее еще раз.", 16, "Ошибка пользователя"
End
End If

Здесь все ясно. При запуске программы "Я - не вирус" App.PrevInstance проверяет, запущена ли эта программа ранее. Если нет, то App.PrevInstance принимает значение False, если запущена, то True. Это условие проверяется оператором If. Выводится диалоговое окно MsgBox, и вторая копия закрывается.

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

Copyright © 2004 4us


Visual Basic с нуля. Глава 6. Drive, Dir и File ListBox'ы. PictureBox. ImageBox.

Глава 6. Выбираем файл: DriveListBox, DirListBox, FileListBox. Функции Right и Left. Графические объекты PictureBox. ImagеBox.
Скачать исходник программы "IcoMaker"
 Дата создания 28.10.2004 {Автор 4us}

DriveListBox, DirListBox, FileListBox

Я все-таки подумал, что пора потихоньку начинать работать с файлами. Начнем с объектов, которые помогут пользователю выбрать необходимый файл. Откроем новый .exe проект, и поместим на форму три объекта (контрола) . DriveListBox, DirListBox и FileListBox (Рис 12).

Рисунок 12.

Теперь положим на форму Text1и в окне его свойств поставим свойство MultiLine в True (это чтобы текст не выводился в одну строку). Сюда мы будем помещать основные возвращаемые этими объектами значения. Для этого напишем коротенький код. В окне кода для каждого объекта откроем следующие процедуры и впишем в них по три строчки кода:

Option Explicit

Private Sub Drive1_Change()
Text1.Text = Drive1.Drive & vbCrLf
Text1.Text = Text1.Text & Drive1.ListIndex & vbCrLf
Text1.Text = Text1.Text & Drive1.ListCount
End Sub

Private Sub Dir1_Click()
Text1.Text = Dir1.Path & vbCrLf
Text1.Text = Text1.Text & Dir1.ListIndex & vbCrLf
Text1.Text = Text1.Text & Dir1.ListCount & vbCrLf
End Sub

Private Sub File1_Click()
Text1.Text = File1.FileName & vbCrLf
Text1.Text = Text1.Text & File1.ListIndex & vbCrLf
Text1.Text = Text1.Text & File1.ListCount
End Sub

Обрати внимание: каждая процедура выводит в Text1 три значения. Чтобы они не сливались в одну строчку, а печатались с новой строки, мы используем константу vbClLf. Она и переводит строчки (но только при условии, если у Text1 включено свойство MultiLine).

Объект Drive1 выводит имена дисков, Dir1 - имена папок, и File1- имена файлов.

Потыкай в эти объекты мышкой и посмотри, какие значения возвращают эти объекты.

Первое свойство (для каждого объекта - свое) возврашает строковое выражение, показывающее имя диска (Drive1.Drive), путь (Dir1.Path) или имя файла (File1.FileName). Нас это свойство интересует больше всего. Свойство .Path может использоваться и с объектом File1 (File1.Path), но не с Drive1.

Второе свойство .ListIndex для Drive1 и File1 возвращает порядковый номер диска или файла в текущей директории, начиная с нулевого. Для Dir1 она возвращает -1 (минус один) для корневой папки и далее с нуля порядковый номер для вложенных в нее папок. В обратную же сторону порядок вложения идет со знаком минус. Проще говоря, посмотри на Рис.13. Синими цифрами показаны значения Dir1.ListIndex.

Рисунок 13.

Теперь с этой фигней должно быть понятнее.

Третье свойство ListCount возвращает общее количество дисков, папок в текущей папке или файлов в текущей папке.

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

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

А посему, сотрем весь код, который мы написали для тренировки и напишем код так, чтобы у нас в Text1 выводилось полное имя файла, который мы выбрали:

Option Explicit

Dim PathSearch As String 'переменная для хранения значения пути

Dim DiskName As String 'переменная для имени диска

Private Sub Form_Load()
PathSearch = App.Path
Drive1.Drive = PathSearch
Dir1.Path = PathSearch
File1.FileName = PathSearch
End Sub

Private Sub Dir1_Change()
File1.Path = Dir1.Path
End Sub

Private Sub Drive1_Change()
DiskName = Drive1.Drive
Dir1.Path = DiskName & "\"
File1.Path = Dir1.Path
End Sub

Private Sub File1_Click()
If Right (File1.Path, 1) = "\" Then
Text1.Text = File1.Path & File1.FileName
Else
Text1.Text = File1.Path & "\" & File1.FileName
End If
End Sub

В коде должно быть все относительно понятно. Однако поясню. Мы объявили две переменные как строковые, одну для хранения значения пути, другую для имени диска. Чтобы при загрузке программа корректно отображала папки, диски и пр., мы в процедуре Form_Load с помощью функции App.Path ставим тот путь, где находится наша программа. Это очень удобно. В дальнейшем мы будем создавать служебные файлы и папки для хранения данных, необходимых нашей программе. И с помощью функции App.Path VB всегда найдет наш рабочий каталог. Использование промежуточной переменной PathSearch (как впрочем и DiskName) необходимо для корректной работы программы.

Далее. Когда мы выбираем диск в Drive1, процедура Drive1_Change через промежуточную переменную DiskName присваивает значение нового диска для Dir1, и в свою очередь значение нового пути для File1. Аналогично при выборе папки в Dir1 процедуа Dir1_Change присваивает новое значение пути для File1. Процедура же File1_Click собирает полное имя файла из пути File1.Path и имени файла File1.FileName. Правда, она осуществляет проверку, оканчивается ли File1.Path обратным слежом (\). Если да, то мы его не вставляем. Это необходимо, потому что имя диска возвращается со слежом, а если есть папка, то без. При этом мы применяем функцию Right.

Функции Left и Right

Мы уже использовали в Главе 4 функции Len, которая определяла число знаков в строке и Mid, с помощью которой мы можем выдернуть из строки необходимое количество знаков. Теперь мы к ним добавим еще две, очень простые:

Right ("строка",число знаков)

Left ("строка",число знаков)

Смысл их в том, что мы выьираем для себя из текстовой строки "строка" справа (функция Right) или слева (функция Left) необходимое нам количество символов число знаков в собственную строку. Эта собственная строка и есть то значение, которое возвращает функция.

Например, такая запись Right ("бармалей",3) возвратит нам значение "лей", а Left ("метрополитен",5) возвратит "метро". Вот и вся математика (арифметика).

Идем дальше.

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

Графические объекты PictureBox. Image.

Это довольно-таки сложная тема. Если не удается въехать в это все, надо просто брать как есть, чтоб потом использовать, не особо вдумываясь. Это просто прием. Ну начинаю грузить...

Все, что мы понаписали, мы используем для создания программки для пробразования графических файлов .jpg или .bmp в иконки .ico. На самом деле файлы ico имеют свой, особый формат и реально мы получим файл bmp размером 32х32 с расширением ico. Но это сейчас не главное. В дальнейшем мы, может сделаем настоящий редактор иконок.
Итак, сперва мы поменяем свойство нашей формы ScaleMode с 1 - Twip на 3 - Пиксель. Потому как с твипами непривычно и трудно работать.

Мы будем использовать два новых элемента PictureBox и Image. В них можно загружать, изменять и сохранять графические файлы. Разместим на нашей форме два PictureBox (Picture1 и Picture2) и один Image (Image1). Я надеюсь, что ты их найдешь. Кроме того положим одну командную кнопку Command1 и один Label1. Назовем кнопку Command1 (свойство Caption) "Сделать иконку 32х32".

Выглядеть это должно приблизительно, как на Рис. 14

Рисунок 14.

Установим свойство Picture1 ScaleMode в окне свойств тоже в 3 - Пиксель, как и у формы.

Вставим в процедуру File1_Click последней строкой (перед End Sub) такую штуку:

Picture1.Picture = LoadPicture(Text1.Text)

Эта штука - функция, которая загружает в PictureBox картинку, путь которой отобразится в Text1.

Теперь, с помощью нашей программы выберем какой-нибудь графический файл в компьютере и кликнем по нему. В наш PictureBox загрузится картинка. Хочу предупредить, что из-за недостатка оперативной памяти (менее 256 Mb) в твоем компьютере и большого размера файла картинки (более 1 Mb) этот процесс может не состояться или занять значительное время. Поэтому бери маленькие по объему (300-600 Кб) картинки.

Далее мы видим, что мы не фига не видим. Картинка видна не вся, а только кусочек, который позволяет размер Picture1. Можно, конечно, в окне свойств Picture1 поставить свойство AutoSize из False в True, но тогда Picture1 может так увеличиться, что не влезет в форму и все равно ее видно не будет. Можешь попробовать, но потом восстанови это свойство. Мы так делать на будем.

Что же нам делать? Для начала нам нужно узнать, какого же размера эта чертова картинка и запомнить этот размер. Для этого у Picture существуют свойства .Picture.Height для высоты и .Picture.Width для ширины. Однако, к сожалению значения, которые мы получим, будут в метрической системе HiMetric и чтобы перевести их в пиксели придется использовать методы ScaleX и ScaleY. Ну да ничего, не так страшно. Объявим в (General) две переменные Shirina и Visota

Dim Shirina As Long
Dim Visota As Long

а после нашего оператора загрузки картинки, в процедуре File1_Click, пишем две строки, которые и дадут нам ширину картинки в пикселях:

Shirina = ScaleX(Picture1.Picture.Width, vbHimetric, vbPixels)
Visota = ScaleY(Picture1.Picture.Height, vbHimetric, vbPixels)

где Picture1.Picture.Width - ширина в HiMetric, vbHimetric - константа, из какой метрической системы переводим, vbPixels - константа, в какую метрическую систему переводим.

Далее для красоты выведем ширину и высоту в Label1

Label1.Caption = Shirina & " x " & Visota

Зная реальный размер картинки мы можем вписать ее в заданный размер, например Image1, не зря ж мы его клали, надо использовать как-нибудь. Я установил его размер 265 на 265 пикселей, исходя из размера нашей формы. И нам надо подогнать размеры нашей картинки, пропорционально изменив длину и ширину. Сперва надо узнать вертикальная картинка (высота больше ширины) или горизонтальная (ширина больше высоты). Это мы сделаем условием If. В нем мы вычислим коэффициент Koeff (его тоже придется объявить), разделив большую сторону картинки на длину соответствуюшей стороны Image1. (Раcчет коэффициента можно сделать и по-другому: взять отношение большей стороны к меньшей Picture1).

If Shirina > Visota Then
Koeff = Shirina / Image1.Width
Else
Koeff = Visota / Image1.Height
End If

Как бы не хотелось, а придется объявлять еще две переменные для хранения нового размера для меньшей картинки. Их можно объявить локально, здесь же, в процедуре File1_Click

Dim ShirinaNew As Long
Dim VisotaNew As Long

Получаем новые значения для ширины и длины Image1.

ShirinaNew = Shirina / Koeff
VisotaNew = Visota / Koeff

У есть Image1 свойство Stretch. Если оно False (по умолчанию), то сам объект Image1 меняет свой размер по размеру картинки, а если True, то картинка меняется по размеру Image1, что нам сейчас и нужно.

Image1.Stretch = True

Выставляем новые значения размеров Image1

Image1.Height = VisotaNew
Image1.Width = ShirinaNew

и передаем картинку в Image1

Image1.Picture = Picture1.Picture

Тут еще вот какие нюансы. Поскольку мы каждый раз меняем размер Image1, надо его восстанавливать. Делать это лучше в начале нашей процедуры File1_Click.

Image1.Width = 265
Image1.Height = 265

Кроме того ее желательно очищать, впрочем как и Picture1. Для этого им передаются пустые значения.

Picture1.Picture = LoadPicture("")
Image1.Picture = LoadPicture("")

А кроме того, поскольку мы в Picture1 все равно ни черта не видим, сделаем ее невидимой, для чего в окне свойств изменим ее свойство Visible c True на False.

Теперь перейдем к тому, то ради чего мы все это затеяли: как же нам все-таки иконку-то сделать?

Изменим следующие свойства Picture2 на значение 36: Height, Width, ScaleHeight и ScaleWidth. (Почему 36, а не 32 не знаю. Если поставить 32, то иконки получаются размером 28х28). Свойство AutoRedraw, которое отвечает за перерисовку, обязательно поставим в True. Создадим процедуру Command1_Click для нашей командной кнопки. В ней объявим локальную переменную Ima для создания нового имени, под которым мы запишем нашу иконку.

Private Sub Command1_Click()
Dim Ima As String

Теперь, используя известные нам функции Len и Mid отсечем справа 4 знака (это предполагается расширение 3 знака и точка) и вместо него прибавим расширение .ico.

Ima = Mid(Text1.Text, 1, Len(Text1.Text) - 4) & ".ico"

Теперь используем метод PaintPicture. Он позволит нам перерисовать картинку из Picture1 в Picture2, масштабировав ее при этом

Picture2.PaintPicture Picture1, 0, 0, 32, 32, 0, 0, , , vbSrcCopy

Это происходит так. Picture2.PaintPicture записывает картинку из Picture1 в Picture2 с координатами в Picture2 0,0 c размером Picture2 32,32 и координаты в Picture1 0,0. vbSrcCopy - этот параметр - константа - необязателен (им устанавливается побитная операция).

Далее с помощью оператора SavePicture мы записываем полученную в Picture2 иконку в файл.

SavePicture Picture2.Image, Ima

Готово! Заработало. У нас крутейшая программка. Скачать исходник можно вверху страницы. Конечно это в общем-то всего лишь пример, но тем не менее у нее большой потенциал. Поработай с исходником, попробуй сделать, чтобы имя иконки мог задавать пользователь, сделай иконки другого размера, измени дизайн, разработай, короче, интерфейс.

Copyright © 2004 4us


Visual Basic с нуля. Глава 7. Работа с текстовыми файлами. Цикл Do...Loop.

Глава 7. Работа с текстовыми файлами. Цикл Do...Loop.
Скачать исходник программы "Demo"
 Дата создания 20.11.2004 {Автор 4us}

Ну, учимся работать с текстовыми файлами. Это нам нужно, чтобы прочитать существующий файл, изменить его, или создать свой. Крому того, в текстовом файле удобно хранить служебные данные собственной программы, например установки пользователя, результаты обработки данных и т.п.
Текстовой файл, собственно по названию, содержит в себе текстовые символы, ну и символы типа возврат каретки или перевод строки. Традиционно они имеют расширение .txt для текста, .dat для данных, .bat для исполняемых (командных) файлов, ну Windows еще использует .ini, .log, .inf. Читать и записывать их надо последовательно, символ за символом, начиная с первого.

Итак, чтобы что-нибудь сделать с файлом, прежде всего, его надо открыть.
При открытии файла для последовательного доступа нельзя считать какой-то один, к примеру, знак из середины файла, для этого есть другие способы доступа, но об этом не сегодня.

Для открытия файла используется оператор Open. Синтаксис его такой:

Open путь For режим As #номер файла

Примечание: для открытия файлов прямого доступа, или двоичных файлов оператор Open содержит еще кое-какие аргументы, но сейчас мы их не используем и опускаем.

Все указанные аргументы должны обязательно присутствовать:
путь - это полный путь к открываемому файлу - (строковое выражение) пишется в кавычках, может состоять из нескольких строк, соединенных знаком конкатенации &. (Например "c:\MyText\referat.txt" или "c:\Mytext\" & "referat.txt")
режим - способ открытия файла и представляет собой ключевые слова. В этой главе му будем использовать Input для считывания данных из файла, Output - для записи данных в файл, и Append - для добавления данных в файл. В режиме Input можно еще раз открыть уже открытый файл под другим номером, не закрывая его, в режимах Output и Append надо сначала закрыть файл, а потом открыть его снова.
#номер файла - это всего лишь номер, по которому VB индентефицирает открытый файл. Он может быть любой от #1 до #255. Однако, лучше всего использовать функцию FreeFile (см. в примере), которая сразу возвратит свободный номер в переменную, и тебе нет нужды искать по своему коду, открыт ли у тебя файл под номером, например #3.

Кстати сказать, после того, как передача или прием данных в (из) файл(а) будут закончены, его надо закрыть, делается это оператором Close. Если этот оператор написать без всяких аргументов, он закроет все открытые файлы. Если же нам надо закрыть файл, скажем номер 2, то пишем Close #2. Попробуем работать с файлами на примерах:

СЧИТЫВАНИЕ из текстового файла.

Создадим новый .exe проект в какой-нибудь папке и в ту же папку положим небольшой текстовой файл (у меня например "Справка.txt"). На форме разместим кнопку Command1 и текстовое поле Text1. В свойствах Text1 свойство Multiline поставим в True (чтобы текст переносился по строкам), а свойство ScrollBars в 2 - Вертикаль (чтобы можно было прокручивать текст, если он не уместился) смотри рис.15.
Рисунок 15.
Откроем окно кода и в (Genarel) объявим переменные F для хранения номера свободного файла, MyText и AllMyText - для хранения считываемого текста:

Dim F As Long
Dim MyText As String
Dim AllMyText As String

Создадим прцедуру Command1_Click() (щелкнув мышью по Command1 в выпадающем списке в окне кодов в верхнем левом поле, там где (General)). В теле образовавщейся процедуры. Можно также щелкнуть два раза по кнопке Command1 в режиме Объект и перейти в режим Код - там появится нужная нам процедура.

Private Sub Command1_Click()

Напишем для определения номера свободного файла

F = FreeFile

и откроем файл для чтения

Open App.Path & "\Справка.txt" For Input As #F

Обратите внимание, что для указания пути файла мы опять используем функцию App.Path. Если бы мы написали просто "c:\VB\example\Справка.txt", то при переносе нашей паки с программой на другой диск или компьютер этот путь оказался бы ложным. А функция App.Path всегда возвращает путь, где лежит наша программа. Только перед именем файла не забывайте ставить обратный слеж (\), иначе запись работать не будет.
Для считывания строки текста существует оператор Line Input. Попробуем считать с его помощью содержимое файла в переменную MyText :

Line Input #F, MyText

Передадим значение MyText переменной в Text1, чтобы увидеть, что мы там считали:

Text1.Text = MyText

затем закроем файл

Close #F
End Sub

и запустим нашу программу. Когда мы нажмем кнопку Command1, то в Text1 появиться содержимое файла "Справка.txt". Проблема только в том, что, если,( как и случилось в нашем файле "Справка.txt"), при создании текста применялось нажатие кнопки ENTER (перевод строки), то оператор Line Input считает текст до этого знака и все, потому, что строка кончилась. Чтобы этого избежать, мы применим цикл и будем считывать строки до конца файла. Для этого придется разобраться с новым для нас циклом по условию Do... Loop.

Цикл по условию Do...Loop

Это очень простая вещь. Цикл работает до тех пор, пока не выполнится какое-нибудь условие. Например мы будем увеличивать переменную X до тех пор, пока оне не станет равной, например, 100. Для этого применяется аргумент Until:

Do Until x>=100
x=x+1
Loop

при этом когда цикл закончится x будет равно 100. Можно записать его по другому, при этом смысл работы цикла не изменится:

Do
x = x + 1
Loop Until x >=100

Можно цикл записать наоборот, и он будеn выполняться до тех пор, пока соблюдается какое-то условие. Для этого применяется аргумент While (делать, пока...) :

Do While x < 100
x = x + 1
Loop

или же:

Do
x = x + 1
Loop While x < 100

Во всех четырех случаях результат будет один. Цикл остановится, когда X=100. Только надо обращаться с ними аккуратно. Если ты поставишь условие, которое изначально ложно, цикл не выполнится ни разу, и наоборот если условие будет истинно всегда, возникнет бесконечный цикл и программа по-сути зависнет. Вот этот цикл не будет выполняться:

Do While x =100
x = x + 1
Loop

а этот будет бесконечным

Do Until x < 0
x = x + 1
Loop

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

Private Sub Command1_Click() '1
Text1.Text = "" '2
F = FreeFile '3
Open App.Path & "\Справка.txt" For Input As #F '4
Do Until EOF(F) '5
Line Input #F, MyText '6
Text1.Text = Text1.Text & MyText & vbCrLf '7
Loop '8
Close #F '9
End Sub '10

По строкам здесь:

1 - объявление нашей процедуры
2 - очистка Text1
3 - присваиваем переменной F свободный номер файла
4 - открываем файл "Справка.txt" для последовательного чтения
5 - цикл по условию: до тех пор пока не конец файла
6 - DO в цикле считываем каждую строчку
7 - Присоединяем очередную строку к Text1 и ставим перевод строки (vbCrLf) чтоб красиво было
8 - LOOP завершаем цикл
9 - закрываем наш файл
10- конец процедуры

Ну вот и готово.

Сейчас мы считаем файл другим способом, с помощью функции Input. Синтаксис ее такой:
Input(число, [#]номерФайла)

Смысл ее такой, что она из файла [#]номерФайла, открытого в режиме Input считывает число знаков. Мы используем ее также, как оператор Line Input, только считывать будем не по одной строчке, а по одному символу, и собирать их будем в переменной AllMyText, значение которой присвоим потом текстбоксу Text1. Поместим на нашу форму еще одну кнопку Command2 (свойство Caption присвоим "Функция Input") и создадим для нее прцедуру, где и используем функцию Input.

Private Sub Command2_Click()
F = FreeFile
Open App.Path & "\Справка.txt" For Input As #F
Do Until EOF(F)
MyText = Input(1, #F)
AllMyText = AllMyText & MyText
Loop
Close #F
Text1.Text = AllMyText
End Sub

Здесь все почти в точности, как и в предыдущем примере, только суть несколько другая.

Ну и в дополнение попробуем прочитать наш файл с помощью оператора Input #. Это не самая лучшая идея, так как эта инструкция считывает файл, желательно, записанный с оператора Write #. Синтаксис такой:

Input #номерФайла, списокПеременных

а смысл в том, что она из последовательно открытого в режиме Input файла с номером #номерФайла считывает в переменные списокПеременных данные, разделенные запятыми. Этот оператор для считывания обычных текстов не годится, а вот данных, а потом использование их в переменных - сколько угодно. Но мы попробуем считать все-таки этот наш файл и этим оператором.
Опять кладем на форму кнопку Command3 и там ваяем по тому же принципу:

Private Sub Command3_Click()
F = FreeFile
Open App.Path & "\Справка.txt" For Input As #F
Do Until EOF(F)
Input #F, MyText
AllMyText = AllMyText & ", " & MyText
Loop
Close #F
Text1.Text = AllMyText
End Sub

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

ЗАПИСЬ в текстовой файл.

Как и для считывания, прежде, чем записывать в текстовой файл, нужно получить к нему доступ. Для этого используем оператор Open с ключевым словом Output.
Добавим на форму еще кнопку Command4. Создадим для нее процедуру и напишем там:

Private Sub Command4_Click()
F = FreeFile
Open App.Path & "\Запись.txt" For Output As #F

Таким образом мы открыли файл с последовательным доступом, который называется "Запись.txt" в нашей текущей директории под свободным номером #F, для записи (For Output). При этом, если такого файла не существует, он будет создан, а если существует и в нем что-то записано, то это что-то будет стерто, а вместо будет помещены наши новые данные, которые мы сейчас будем записывать, например, скажем с помощью оператора Print #. Записать его можно аналогично оператору считывания Line Input:

Print #F, Text1.Text

То есть, мы записываем содержимое нашего текстбокса (если там что-то есть. Считайте в текстбокс Text1 наш файл Справка.txt нажатием одной из кнопок Command1-Command3) в файл "Запись.txt". Затем закрываем файл и конец процедуре:

Close #F
End Sub

Ты теперь можешь открыть этот файл с помощью Виндусового Блокнота или в любом текстовом редакторе и посмотреть его. Или напиши в нашей программе свой код, чтоб считать этот файл в Text1 и сразу его посмотреть.
В принципе, если тебе это надо, оператор Print # может форматировать текст, т.е. менять отступы от края, вставлять пробелы, но только между переменными, а не конечно в середину, скажем, нашего текста. Для этого используется аргументы Spc (количество пробелов) для введения пробелов и Tab(номер столбца) для вывода данных по столбцам. Положим на форму еще текстбокс Text2 и кнопку Command5 и в ее процедуре напишем код для форматированной записи. Потом считаем наши записи в Text2 с помощью оператора Line Input. С этим, я думаю, ты разберешся сам. (Не забудь поставить свойства Text2:свойство Multiline поставим в True, а свойство ScrollBars в 2 - Вертикаль.

Private Sub Command5_Click()
F = FreeFile
Open App.Path & "\Запись.txt" For Output As #F
'печатаем тексты с 15-й позиции
Print #F, Spc(15); "Я вывожу этот текст"
Print #F, Spc(15); "очень красиво"
Print #F, Spc(15); "потому, что я настоящий"
Print #F, Spc(15); "ПРОГРАММИСТ"
'печатаем пустую строку
Print #F,
'печатаем тексты в 30-ем, 40-м и 50-м стобцах соответственно
Print #F, Tab(30); "тридцатый столбец"
Print #F, Tab(40); "сороковой столбец"
Print #F, Tab(50); "пятидесятый столбец"
'если оператор завершать точкой с запятой, то перехода на следующую строку не будет
Print #F, Spc(15); "Печатаю текст";
Print #F, Spc(15); "не очень хорошо"
' и наконец несколько текстов в одном операторе
Print #F, Spc(10); "10 пробелов"; Tab(30); "30-й столбец+ 10 пробелов в начале"; Spc(5); "Пять пробелов"
Close #F

'считаем записанный текст в Text2
Text2.Text = ""
MyText = ""
F = FreeFile
Open App.Path & "\Запись.txt" For Input As #F
Do Until EOF(F)
Line Input #F, MyText
Text2.Text = Text2.Text & MyText & vbCrLf
Loop
Close #F
End Sub

Теперь запишем чего-нибудь в файл с помощью оператора Write #, а потом, уже корректно считаем это оператором Input # о котором мы уже говорили в этой главе. С помощью оператора Write # удобно записывать данные твоей программы. Есть две особенности. Первое - данные разделяются разделителем (запятой), и второе - данные надо считывать в переменные того же типа, что и запись в файле ( то есть число - в число, дату в дату, стоку в строку и т.п., иначе возникнет ошибка) и представляются они в файле в достаточно своеобразном виде - каждый в своем формате. Сейчас разберем это на примере, я так думаю, будет понятнее. Положим еще одну кнопку Command6 на форму и в ней объявим переменные четырех разных типов.

Private Sub Command6_Click()
Dim Chislo As Long
  ' числовая
Dim Stroka As String
  ' строковая
Dim Logika As Boolean
  ' логическая
Dim DDate As Date'
  дата (не путайте с Data)

Надо заметить, что число выводится в файл как число, строка - как строка в кавычках, логическая переменная может принимать только два значения Да и Нет в формате #False# или #True#, а дата в виде #2004-07-20# , что соответствует 20 июля 2004 года. Дальше присваиваем переменным какие-нибудь значения в соответствующем формате

Chislo = 15
Stroka = "пятнадцать"
Logika = True
DDate = #7/20/2004#

' записываем данные в файл
' обратите внимание, я нарочно разбил оператор Write # на 2, а считываю одним,
'чтобы показать, что нет разницы, сколько операторов Write# или Input# вы будете использовать

F = FreeFile
Open App.Path & "\Запись.txt" For Output As #F
Write #F, Chislo; Stroka
Write #F, Logika; DDate
Close #F

'теперь считаем данные с помощью оператора Input#
F = FreeFile
Open App.Path & "\Запись.txt" For Input As #F
Input #F, Chislo, Stroka, Logika, DDate
'и выведем их в Text2
Text2.Text = Chislo & " " & Stroka & " " & Logika & " " & DDate
Close #F
End Sub

Сейчас, когда ты запустишь программу, ты увидишь, что в Text2 данные вывелись в очень достойном виде, но это потому, что мы используем для считывания оператор Input #. Если же ты откроешь файл " Запись" в блокноте или считаешь файл с помощью оператора Line Input, то увидишь несколько иную картину, вот такую:

15,"пятнадцать"
#TRUE#,#2004-07-20#

Поэтому делать текстовые файлы для чтения их в других приложения бессмысленно. Но использовать операторы Write# - Input# внутри программы для работы с собстенными данными очень удобно.

Добавление данных в текстовой файл.

Чтобы добавить данные в текстовой файл, не стирая имеющихся там, надо просто открыть файл с ключевым словом Append. Быстренько разберем это на примере. Для этого положим на форму последнюю кнопку Command7 и в ней напишем код:

Private Sub Command7_Click()
Text2.Text = ""
MyText = ""
F = FreeFile
'открываем файл для дабавления
Open App.Path & "\Запись.txt" For Append As #F
Print #F, Spc(15); "Я добавляю этот текст снова и снова"
Close #F

'считаем записанный текст в Text2
F = FreeFile
Open App.Path & "\Запись.txt" For Input As #F
Do Until EOF(F)
Line Input #F, MyText
Text2.Text = Text2.Text & MyText & vbCrLf
Loop
Close #F
End Sub

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

Copyright © 2004 4us


Visual Basic с нуля. Глава 8. Объекты CheckButton, OptionButton и Frame. Функция Dir. Объекты ComboBox и ListBox.

Глава 8. Объекты CheckButton, OptionButton и Frame. Операторы для переименования уничтожения и пр. файлов. Функция Dir. Объекты ComboBox и ListBox.
Скачать исходник программы "Тестер"
 Дата создания 29.11.2004 {Автор 4us}

Снова становимся на тенистый путь программиста . Для логического завершения первой части, я решил в этой главе подобрать хвосты. Во-первых, о чем мы не говорили и что лежит прямо при загрузке VB. Какие объекты мы не затронули (конечно только пока из тех, которые стандартно лежат в окне Панели инструментов, то есть слева, где наши объекты формы). Вот на рисуночке я обвел их кружочками. Хотите поговорим об этом? Ну и ладно. Все равно поговорим.

Рисунок 16.

До фига получилось. Ну ничего. Это вобщем-то простые объекты, но в один пример запихнуть их все, пожалуй, не удасться.

Создадим новый exe-проект и положим на форму, например три флажка (CheckBox'а). Ну и вот, по умолчанию в квадратике нет ничего и главное значение этого объекта .Value равно 0 - Неотмечено. Когда мы щелкаем по флажку, то появляется галочка, и значение .Value становится равным 1-Отмечено. Bот и все. Есть еще и третье значение 2 - Выключено, но установить его щелканьем мышки не удасться. Надо либо в окне свойств задать значение, либо в коде написать
Check1.Value=2
при этом квадратик станет сереньким, типа выключился, но если по нему щелкнуть, то он опять примет значение включенного.

Аналогично работает и и радиокнопка (OptionButton) за исключением следующего. Первое: у OptionButton только два значения и принимают они "False" - выключено, точки в кружочке нет, и "True" - включено, точка есть. А второе, если вы на форму положите пять Check'ов, то вы можете включить сколько угодно из них, хоть три, хоть все пять. А вот Option можно включить только один. Если щелкнешь по другому, то включенный тут же выключится.
Кроме того, и у Check и у Option есть еще одно свойство Style. По умолчанию оно равно 0 - Standart. Но если его поставить в состояние 1- Graphical, то бац, твоя фигня с галочкой превращается по виду в кнопку. Внешне от кнопки Command она отличается тем, что в нажатом состоянии она показана, как нажатая. Ну и само собой свойство Caption позволяет Вам обзывать эти объекты, как угодно.

Далее объект Фрейм (Frame). Это как-бы окно, в которое можно запихивать флажки, радиокнопки, да что угодно. Но чтоб объект попал во Frame, надо его туда класть сразу. Если ты его создашь в форме вне фрейма, а потом перетащишь его во фрейм, то ничего не выйдет. Объект просто ляжет сверху. Ну и передвигая фрейм, ты сразу двигаешь и запихнутые туда объекты, что удобно. А если уничтожишь фрейм, то погибнут и все встроенные в него объекты. Вообще фреймами удобно делить форму на зоны. Кроме того, радиокнопки одного фрейма не связаны с радиокнопками другого или с внешними (на форме). Т.е. ты в каждом фрейме можешь создавать свой блок кнопок.

Программка-тест.

Сейчас мы напишем программку для тестов, где все это попробуем, а заодно потренируемся работать с текстовыми файлами, о чем говорилось в прошлой главе. Смысл программки в том, что мы готовим тест, например, психологический и на вопрос предлагаем испытуемому несколько ответов. По результатам теста выводим характеристику испытуемому.
Во-первых сделаем одну форму Form1 для создания контрольных вопросов, которую испытуемому видеть ни к чему - это форма для руководителя проекта тестирования. А вторая - Form2 - предлагается испытуемому для проведения теста.
Создадим новый exe-проект и на Form1 положим Text1 для занесения вопроса (не забудь его свойство Multiline поставить в True), Text2, Text3 и Text4 - для ответов, Text5, Text6, и Text7 для баллов по каждому ответу, кнопку Command1 для записи этого в файл и кнопки Command2 и Command3 для навигации по файлам (типа вверх-вниз). Я не стал использовать ScrollBar, чтобы не затемнять смысл программы. Поскольку мы еще не все познали-изучили (массивы или там файлы с прямым доступом), то будем записывать каждый вопрос в отдельный файл. Поэтому один Label1 надо положить сразу, он будет показывать номер вопроса, который будет и именем txt-файлов. А лучше сразу положить еще Label2 и Label3. У тебя должно получиться что-то типа рис.17

Рисунок 17.

Теперь убираем во всех Text'ах свойство Text (просто стираем в окне свойств), затем в свойстве Caption лейблов Label2 и Label3 пишем Ответы и Баллы соответственно. В свойствах Caption у кнопки Command1 пишем Сохранить вопрос, у кнопки Command2 - Назад, у кнопки Command3 - Вперед и изменяем их размеры соответственно надписи. Теперь пишем код в окне кода.

Перво-наперво, конечно Option Explicit, если вы не смогли установить VB, чтоб он прописывал это автоматом (я рассказывал как). Потом там - же, в области (General) - (Declarations) объявляем следующие переменные:

Option Explicit
'Для хранения номера вопроса (числовое, потому, что будем
'складывать и вычитать) и одновременно имя файла (строковое).
' Вот единственный случай использования типа Variant
Dim NomVopros As Variant
'и для каждого нашего текстбокса
Dim Vopros As String 'Text1-строковое, здесь пишем вопрос
Dim Otvet1 As String 'Text2-строковое- пишем возможный ответ №1
Dim Otvet2 As String 'Text3-строковое- пишем возможный ответ №2
Dim Otvet3 As String 'Text4-строковое- пишем возможный ответ №3
Dim Ball1 As Long 'Text5 -числовое - баллы к ответу №1
Dim Ball2 As Long 'Text6-числовое - баллы к ответу №2
Dim Ball3 As Long 'Text7-числовое - баллы к ответу №3

Почему так? На фик нам эти переменные? Писали бы в файл и грузили бы сразу из TextBox'ов. А потому, во-первых, что из TextBox'ов все бы записывалось как текст (строка), а у нас переменные разных типов, а во-вторых, при считывании вообще возникла бы ошибка, потому что нельзя так. И вообще, лучше всегда использовать переменные, а не объекты. Далее объявим еще две переменные, одна просто для получения свободного номера файла

Dim F As Long

а вторая, булева, т.е. логическая (ты помнишь, она может принимать только два значения False и True). Мы ее будем использовать, как флаг, т.е. индикатор состояния чего, позже скажу

Dim Flag As Boolean

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

Функция DIR подробно.

Синтаксис ее такой:

Dir (Полный путь, атрибут)

Ну полный путь - это понятно, типа "c:\windows\system\хрень.dll". А атрибуты его это константы и могут принимать такие значения

Имя константы | Числовое значение константы | Тип файлов
vbNormal | 0 | обычные файлы
vbHidden | 2 | скрытые файлы
vbSystem | 4 | системные файлы
vbVolume | 8 | метка тома
vbDirectory | 16 | каталог (папка)

Если атрибут не ставить, то по умолчанию он равен 0 (обычный файл). Еще нам будет интересен атрибут = 16 (каталог, то бишь папка).

Причем все элементы функции Dir необязательны. Так как еe использовать?
Смысл ее работы в том, что она ищет файл, удовлетворяющий пути. Если она его находит, то возвращает его имя, если нет, то пустую строку (""). Используя это замечательное свойство, можно ее использовать следующим образом:

- Проверить, существует ли файл "c:\windows\system\хрень.dll"
If Dir("c:\windows\system\хрень.dll")<>"" Then Text1.Text="А я нашла файлик"
т.е. можно смело с этим файлом работать.

-Проверить, существует ли директория windows
If Dir("c:\windows",vbDirectory)<>"" Then Text1.Text="А я нашла папочку"
или (вместо константы vbDirectory можно поставить число 16
If Dir("c:\windows",16)<>"" Then Text1.Text="А я нашла папочку"
Это довольно-таки просто. А вот что бы найти все файлы в папке, узнать их имена, и т.п., надо написать такой код:
Для этого примера я предполагаю, что на форме есть кнопка Command1 и текстбокс Text1. Весь код пишется в процедуре кнопки.

Private Sub Command1_Click()

Объявим переменную, FileName как строковую. Мы будем присваивать ей имя файла (переменная локальная, работает только в этой процедуре)

Dim FileName As String

и очистим Text1 (на всякий случай)

Text1.Text = ""

Присвоим нашей переменной значение, которое вернет функция Dir. Обратите внимание, что имя файла состоит из звездочек. Это потому, что мы хотим получить имена всех существующих файлов, независимо от имени и расширения. Допустимо также использовать знак ? (например, если мы хотим получить имена всех файлов с расширение txt, имена которых одинаковы, кроме последнего знака (file1.txt, file2.txt... file9.txt) , можно написать "file?. txt"

FileName = Dir("D:\MyPicture\Disk_001\*.*")

Сейчас функция Dir сработала первый раз (по полному пути) и теперь знает, в какой директории мы ведем поиск. Далее проверяем, может в этой папке вообще файлов нет

If FileName <> "" Then

если наша переменная, которая должна была получить имя от функции Dir - не пустая, значит все в порядке, файлы есть. И теперь организовываем цикл Do...While, который будет выполнятся, пока функция Dir не вернет пустую строку (а это значит, что файлов, удовлетворяющих нашему пути "D:\MyPicture\Disk_001\*.*" нет.

Do While FileName <> ""

Теперь дописываем имя полученного файла в тектбокс Text1 (константу vbCrLf ставим, чтобы у нас каждое новое имя файла писалось с новой строки, если конечно свойство MultiLine текстбокса стоит в состоянии True)

Text1.Text = Text1.Text & FileName & vbCrLf

теперь нам в этом же цикле нужно снова запустить функцию Dir, на сей раз без аргументов, просто скобочки (а можно и вообще без них

FileName = Dir() ' или FileName = Dir

Теперь закроем цикл, закроем условие If и закроем процедуру Command1_Click

Loop
End If
End Sub

Вот и все. Можете проверить.
Еще раз поясняю работу Dir для выборки группы файлов. Первый вызов функции имеет полный путь до файла и маску имени файла ( с использованием * и ? ). При этом функция возвращает первое попавшееся имя файла, удовлетворяющее маске. Второй и последующие вызовы - функция не имеет аргументов и возвращает имена файлов, удовлетворяющих маске в произвольном порядке. После того, как все имена файлов возвращены и в директории нет больше файлов с именами, удовлетворяющими нашей маске, функция Dir возвращает значение "пустая строка" (""). Это сигнал к тому, что пора прекратить вызовы функции Dir. Если снова вызвать функцию Dir без аргументов, то это вызовет ошибку выполнения. Вызывать функцию можно теперь по-новой , с полным путем до файла.
Поскольку мы не использовали второй аргумент "атрибут", то по умолчанию он равен 0, т.е. выбираются файлы. Если мы поставим атрибут 16 - папка, то это вовсе не значит, что функция Dir будет выбирать для нас только папки. Она выберет и папки и файлы, входящие в указанную нами в пути директорию. Есть прием, который позволяет отделить файлы и оставить только папки. Он приведен в Хелпе для пятого Бейсика.
Кроме того, поскольку Функция Dir возвращает файлы в произвольном порядке, то для достойного вывода списка файлов на экран, их надо отсортировать хотя бы в алфавитном порядке. О сортировке мы будем говорить, когда будем рассматривать массивы.

Совет: Прежде чем открывать файл, проверьте, существует и он, с помощью функции Dir. Это позволит избежать ненужных ошибок.

Поскольку мы уже научились просматривать имена файлов, логично будет упомянуть другие операции с файлами:

Копирование, уничтожение, переименование и прочие действия с папками и файлами.

  • оператор Kill путь - уничтожает файл (или файлы, если используются подстановочные знаки * или ?) с диска (пример Kill "c:\mypicture\*.jpg")
  • оператор FileCopy "копируемый файл", "новое имя копируемого файла" - копирует файл. Лучше писать полный путь в обоих аргументах. (например: FileCopy "c:\referat.txt", "c:\allreferats\referat4.txt"). Открытый (например оператором Open) файл скопировать нельзя. Это вызовет ошибку выполнения.
  • оператор Name "староеимяфайла" As "новоеимяфайла" - переименовывает файл или каталог. При этом если указать разные пути, то файл перенесется под другим именем. А если пути одинаковые, а имена разные, то произойдет переименование файла. (пример: Name "c:\autoexe.bat" As "d:\myfile.bat")
  • оператор RmDir "путь" - уничтожает существующую пустую папку (пример RmDir "c:\Programs\Project"). При попытке удалить непустую папку возникает ошибка. Внимание, если не указать полный путь, оператор удалит папку из текущего каталога (пример RmDir "PAPKA").
  • оператор MkDir "путь" - создает новую папку (пример MkDir "c:\Programs\Project"). Если полный путь не указан, папка создается в текущем каталоге. (пример MkDir "Новый проект" ).
  • оператор СhDir "путь" - изменяет текущий каталог (а не диск) на указываемый в пути.
  • функция CurDir ("диск") - возвращает текущий диск. Аргумент диск необязательный.
  • оператор ChDrive "диск" - изменяет текущий диск. Аргумент диск - обязательный, в нем используется только первый символ.

В принципе, здесь все просто, поэтому специальной программой я иллюстрировать это не буду.

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

Shell ("путь до файла программы", "вид окна, в котором откроется программа")

Путь - это понятно, а второй аргумент - константа, которая может принимать следующие значения:

Имя константы | Числовое значение константы | В каком окно откроется программа
vbHide | 0 | скрытое окно, фокус
vbNormalFocus | 1 | свой размер, фокус
vbMinimizedFocus | 2 | в виде значка в контрольной панели, фокус
vbMaximizedFocus | 3 | в полный экран с фокусом
vbNormalNoFocus | 4 | свой размер без фокуса
vbMinimizedNoFocus | 6 | в виде значка в контрольной панель, без фокуса

Пример использования:ProgGO = Shell("C:\Program Files\BANDIT.EXE", 1) запускает программу "Однорукий бандит" с тем размером экрана, которая она имеет. Фокус будет на окне программы. В переменную, которую я назвал, например, ProgGO (ее надо объявить Dim ProgGo) возвращается идентификационный номер запущеной программы. А если программу запустить не удалось, возникает ошибка.

Извини за долгое оступление, но я посчитал своим долгом вывалить все это на тебя. Хоть и не сложно, но это понадобиться. Теперь, наконец, вернемся к нашей программе-тесту. Мы уже объявили все переменные, теперь сделаем собственную процедуру, которая будет грузить вопросы, ответы и баллы в форму. Назовем ее Loading().

Private Sub Loading()

Первым делом, конечно зададим имя формы, чтоб красиво было.

Form1.Caption = "Запись теста - только для администратора теста"

Поскольку мы к этой процедуре будем обращаться неоднократно и переменные могут хранить старые значения, мы очистим их:

Vopros = ""
Otvet1 = ""
Otvet2 = ""
Otvet3 = ""
Ball1 = 0
Ball2 = 0
Ball3 = 0

Теперь нам надо загрузить данные из файла вопросника. Для этого нам надо открыть файл с именем "номер вопроса.txt". Номер вопроса должен храниться в переменной NomVopros. Мы при загрузке формы присвоим ей значение "1", чтобы сначала попробовать загрузить файл с первым вопросом.
Прежде, чем открывать файл, надо проверить, существует ли он. Возможно это начало работы с тестом, и никаких вопросов просто не существует. Делаем это с помощью функции Dir, о которой мы так много говорили (причем ищем все в текущем каталоге - App.Path )

If Dir(App.Path & "\" & NomVopros & ".txt") <> "" Then

и если у нас файл существует (Dir - не пустая строка), тогда определяем свободный номер файла и открываем его

F = FreeFile
Open App.Path & "\" & NomVopros & ".txt" For Input As #F

Теперь с помощью оператора Input# считываем данные (я использовал три оператора только ради удобства чтения кода) и не забываем закрыть файл.

Input #F, Vopros
Input #F, Otvet1, Otvet2, Otvet3
Input #F, Ball1, Ball2, Ball3
Close #F

В другой ветви оператора If (когда файла не существует) мы присваиваем булевой переменной Flag значение True

Else
Flag = True
End If

Переменная Flag служит нам индикатором, существует ли текущий файл (Flag=False), или еще нет (Flag=True). Больше мы в этой ветви цикла If ничего не делаем, потому. что открывать нечего. Но, после цикла, чтобы наши данные отобразились в форме, мы Текстбоксам присваиваем соответствующие значения переменных, независимо от того, пусты они или нет. Пользователь должен видеть реальную картину.

Text1.Text = Vopros
Text2.Text = Otvet1
Text3.Text = Otvet2
Text4.Text = Otvet3
Text5.Text = Ball1
Text6.Text = Ball2
Text7.Text = Ball3

Конец процедуры

End Sub

Теперь переходим к процедуре Form_Load. Что ж мы делаем при загрузке нашей формы Form1?

Private Sub Form_Load()

Присваиваем значение переменной NomVopros единицу (мы же хотим начинать с первого вопроса) и устанавливаем значение нашего флага в значение False. Изначально мы предполагаем, что файл с номером вопроса 1 существует:

NomVopros = "1"
Flag = False

Выводим в Label1 для пользователя номер обрабатываемого вопроса

Label1.Caption = "Вопрос № " & NomVopros

запускаем нашу процедуру загрузки файла Loading и конец процедуре Form_Load

Loading
End Sub

Теперь создаем процедуру для кнопки Command1 - это просто запись в файл с помощью оператора Write#. Только перед записью мы нашим переменным присвоим текущие значения Текстбоксов

Private Sub
  Command1_Click()
Vopros = Text1.Text
Otvet1 = Text2.Text
Otvet2 = Text3.Text
Otvet3 = Text4.Text
Ball1 = Text5.Text
Ball2 = Text6.Text
Ball3 = Text7.Text

ну и открываем и записываем файл под именем "номер текущего вопроса.txt"

F = FreeFile
Open App.Path & "\" & NomVopros & ".txt" For Output As #F
Write #F, Vopros
Write #F, Otvet1, Otvet2, Otvet3
Write #F, Ball1, Ball2, Ball3
Close #F

Поскольку мы файл записали, значит он существует, устанавливаем наш флаг в положение False и конец процедуре.

Flag = False
End Sub

Остаются две кнопки Command2 и Command3, которые служат для передвижения по файлам (то бишь вопросам). Здесь я использую такой принцип: при нажатии кнопки вверх (Command2) имя файла, он же вопрос, уменьшается на единицу, а при нажатии кнопки вниз (Command3)- увеличивается на единицу. После этого происходит переход к процедуре Loading, которая загружает новый файл (вопрос). Однако здесь надо поставить определенные ограничения.

Для Command2:
Здесь все просто - мы проверяем, не меньше ли единицы наш номер билета. Если не меньше, то пожалуйста, уменьшаем номер на единицу и переходим к нашей процедуре Loading, а в противном случае ничего не делаем.

Private Sub Command2_Click()
If NomVopros > 1 Then
NomVopros = NomVopros - 1
Label1.Caption = "Вопрос № " & NomVopros
Loading
End If
End Sub

А вот с другой кнопкой Command3 дело обстоит несколько сложнее. Нам нужно, чтоб мы двигались вперед до тех пор, пока не дойдем до первого несуществующего файла (нам же нужно создавать новые впросы для тестирования) и на нем стоп! Для этого мы проверяем выполнение двух условий:
первое - хотим ли мы перейти к существующему файлу, или этот файл еще не создан? Это просто проверяется функцией Dir. Если мы просто переходим с последнего файла на несуществующий - это пожалуйста, а вот если мы уже на несуществующем и пытаемся двигаться дальше, то этого допустить никак нельзя, поэтому
второе - вот настал час переменной Flag. Так вот ее состояние таково, что показывает, на каком файле мы находимся перед переходом. Если Flag=False, значит мы стоим на нормальном файле и к несуществующему перейти вполне можно и мы увиличиваем на единицу номер файла. А вот если Flag=True, то значит мы уже достигли предела и двигаться дальше нельзя. Вот это код:

Private Sub Command3_Click()
If Dir(App.Path & "\" & NomVopros & ".txt") = "" And Flag = True Then
Exit Sub
Else
NomVopros = NomVopros + 1
Loading
End If
Label1.Caption = "Вопрос № " & NomVopros
End Sub

Здесь мы используем оператор Exit Sub, который позволит нам выйти из процедуры, не выполняя ее до конца. Хочу обратить твое внимание, что в коде программы в трех местах встречается одна и та же строка Label1.Caption = "Вопрос № " & NomVopros. Это недопустимая халатность. На работоспособности программы это, конечно не скажется, но с точки зрения программирования, каждая лишняя строчка (особенно если их тысячи) - это смерть разрабатчикам, т.е. нам. Попробуйте убрать ее и поставить в одном месте, где бы она "работала за троих". В исходнике это сделано. Далее для красоты ты можешь надписи на кнопках Command2 и Command3 заменить стрелочками. Я вот нарисовал их просто в Paint'e. Получилось два маленьких bmp-файла Up.bmp и Down.bmp. Чтобы поставит их в кнопку нужно свойство кнопки Style в состояние 1 - Graphical, очистить свойство Caption и в свойстве Picture нажать на кнопочку с тремя точечками. У тебя появиться окно "загрузить рисунок", где можно благополучно выбрать графический файлик. Это файлик сохранится в твоей программе и таскать его за собой больше не надо, но у меня в исходнике они лежат, как бы для примера.

Ну вот, с первой частью программы мы закончили. А теперь неплохо-бы сделать форму собственно для тестирования. Для этого в нашу программу надо добавить еще одну форму. Вверху общего окна VB надо нажать на кнопку со словом Проект и в выпавшем меню кликнуть по первой строке "Добавить форму". Тут же откроется окно "Добавить форму" и первая картинка называется "Форма". Выдели ее, если она не выделена и нажми кнопку в этом же окне "Открыть" (или там Open). И хрясть, перед вами новая форма Form2, ну точно такая же как и старая.

Сделаем все почти по аналогии с первой формой.

Кладем на Form2, Label1, Text1, Command1 и Frame1. После этого, сразу во Frame1, а не на форму кладем три радиокнопки Option1, Option2 и Option3. Вот и готово. Кнопка Command1 будет называться " Ответить". Теперь объявляем переменные, аналогичные первой форме

Option Explicit
Dim NomVopros As Variant
Dim Vopros As String
Dim Otvet1 As String
Dim Otvet2 As String
Dim Otvet3 As String
Dim Ball1 As Long
Dim Ball2 As Long
Dim Ball3 As Long
Dim F As Long

плюс еще SumAns для суммирования баллов

Dim SumAns As Long

Создаем процедуру Loading, почти такую же, как и в первой форме, только ответы выводим в названия радиокнопок, а баллы нам вообше выводить не надо. И устанавливаем первую радиокнопку в положение "включено", иначе, если все кнопки будут выключены, пользователь может нажать кнопку "Ответить" , не выбрав варианта ответа.

Private Sub Loading()
Option1.Value = True ' первая радиокнопка включена
Vopros = ""
Otvet1 = ""
Otvet2 = ""
Otvet3 = ""
Ball1 = 0
Ball2 = 0
Ball3 = 0
If Dir(App.Path & "\" & NomVopros & ".txt") <> "" Then
F = FreeFile
Open App.Path & "\" & NomVopros & ".txt" For Input As #F
Input #F, Vopros
Input #F, Otvet1, Otvet2, Otvet3
Input #F, Ball1, Ball2, Ball3
Close #F
Else
End If

Option1.Caption = Otvet1
Option2.Caption = Otvet2
Option3.Caption = Otvet3
Text1.Text = Vopros
Label1.Caption = "Вопрос № " & NomVopros
End Sub

В процедуре загрузки формы задаем название формы

Private Sub Form_Load()
Form2.Caption = "Тестирование"

обнуляем на всякий случай сумматор ответов

SumAns = 0

и далее как в первой форме

NomVopros = "1"
Loading
End Sub

Теперь код кнопки "Ответить"

Private Sub Command1_Click()

Проверяем, какая радиокнопка нажата, и в зависимости от этого увеличиваем сумматор на количество баллов ответа

If Option1.Value
  = True Then SumAns = SumAns + Ball1
If Option2.Value = True Then
  SumAns = SumAns + Ball2
If Option3.Value = True Then
  SumAns = SumAns + Ball3

увеличиваем номер вопроса на один и проверяем, существует ли такой файл

NomVopros = NomVopros + 1
If Dir(App.Path & "\" & NomVopros & ".txt") = "" Then

если не существует, заканчиваем тест, находим среднее арифметическое и в зависимости от него выдаем сообщение в Text1 с помощью вложенного условия

Text1.Text = "Тест окончен"
If SumAns / (NomVopros - 1) <= 1 Then
Text1.Text = Text1.Text & " Вы злой как собака"
ElseIf SumAns / (NomVopros - 1) > 1 And SumAns / (NomVopros - 1) <= 2 Then
Text1.Text = Text1.Text & " Бывают и добрее"
Else
Text1.Text = Text1.Text & " Вы сама доброта, таких не бывает"
End If

Если есть файл, переходим к процедуре загрузки Loading и конец процедуре

Else
Loading
End If
End Sub

Вот, практически и все. Только для редактирования вопросов надо иметь возможность тайно заходить на первую форму. Для этого я использовал два клика по TextBox'у

Private Sub Text1_DblClick()
Form1.Show
End Sub

И последнее. Поскольку первой (по умолчанию)грузиться должна Form2, войдем в меню Проект вверху окна VB и в выпавшем списке кликнем по последней строке "Проект1 Свойства". У нас откроется окно "Свойства проекта". На вкладке "Главное" в поле "Объект запуска" выберем Form2. Готово. Сохраняем проект. Исходник можно скачать вверху страницы.

Объекты ComboBox и ListBox.

Ну и в конце главы поговорим еще об объектах.
Два объекта ComboBox и ListBox очень похожи. Основные различия их в следующем: ComboBox выполнен в виде выпадающего списка и пользователь может добавлять в него строки, а ListBox похож на TextBox, если все строки не вмещаются, включаются полосы прокрутки. Основные методы у них следующие:
.AddItem - позволяет добавлять пункты (строки)
.RemoveItem - позволяет удалять строки
А основные свойства это:
.List(n) - позволяет ввести или вывести содержимое n-ой строки (пункта)
.ListCount - позволяет узнать количество строк (пунктов в объекте)
.ListIndex - позволяет вывести номер строки пункта, или перейти к строке с эти номером. Для ComboBox'a пустая строка имеет номер -1 (минус 1), а строки списка нумеруются от 0 и далее, поэтому количество строк всегда на 1 больше, чем номер последней строки. В ListBox'e нет строки -1 (минус 1).
.Text - позволяет вывести содержимое стоки, например в TextBox

Исходя из этого, и преполагая, что в нашем проекте на форме Form1 лежат Text1, Command1, Combo1, List1, то
вот пример кое-каких действий, которые мы можем совершить с ComboBox или ListBox

- добавить строку (пункт)
Combo1.AddItem "Нулевая строка" ' добавляет строку номером (ListIndex) 0 (потому-что первая)
Combo1.AddItem "Вася" ' добавляет строку номером (ListIndex) 1 (потому-что вторая)
List1.AddItem "Нулевая строка"
Другой путь - это войти в свойство .List в окне свойств, щелкнуть по свойству и выпавшем окне и дописывать строки в ручную, но мне представляется это не решением вопроса.

- удалить строку (пункт)
Combo1.RemoveItem 0 или ' удаляет строку под номером (ListIndex) 0
 List1.RemoveItem 0

- узнать (например вывести в TextBox) количество строк
Text1.Text = Combo1.ListCount
Text1.Text = List1.ListCount

- перейти к третьей строке
Combo1.ListIndex = 2
List1.ListIndex = 2

- вывести строку в TextBox
Text1.Text = Combo1.Text 'выводит строку, по
  которой щелкнули мыщью
Text1.Text = List1.Text
Text1.Text = Combo1.List(3) 'выводит четвертую строку
Text1.Text = List1.List(3)

- позволить пользователю дописать новую строку (только в ComboBox)
Это можно реализовать следующим образом. При загрузке пустая строка (ее индекс -1) пустая. В нее пользователь и пишет содержимое строки. Но оно не сохраниться. Поэтому, для сохранения новой строки можно использовать например, командную кнопку Command1. Тогда ее процедура будет выглядеть так

Private Sub Command1_Click()
Combo1.AddItem Combo1.Text
End Sub

Кроме того в окне свойств у этих объектов можно поставить свойство .Sorted в положение True. Тогда строки отсортируются в порядке возростания.
Ну вот про эти два объекта пока все.

Что касается графических объектов, то я упомяну их только для очистки совести. Нарисовать квадратик с помощью объекта Shape или линию с помощью объекта Line, по-моему может каждый и без особых инструкций.

Что касается объекта OLE, то это контейнер, который позволяет вставлять в форму различные объекты, например avi-файлы, картинки и т.п. Это достаточно капризный и кривой объект. В настоящий момент его подробное исследование нам совершенно ни к чему, есть задачи по-важнее.

Об объекте Data скажу следующее. Это один из объектов, который позволяет работать с существующей базой данных, создавать, удалять, сортировать и т.п. записи. Но изучение этого объекта довольно серьезная задача. Мы вернемся к ней позже. Вообще, перед этим хорошо-бы познакомится с Access'ом и сделать с помощью него хотя-бы одну простенькую базу.

Copyright © 2004 4us


Visual Basic с нуля. Глава 9. Создание меню. Контекстное (всплывающее) меню. Метод PopupMenu.

Глава 9. Создание меню. Контекстное (всплывающее) меню. Метод PopupMenu.
Скачать исходник программы "Меню"
 Дата создания 11.12.2004 {Автор 4us}

Конечно, можно обойтись и без меню. Я, например считаю, что меню надо использовать в сложных программах, чтобы избежать использования множества командных кнопок. Однако меню придает программе профессиональный, "не детский" вид, в отличие от множества насобаченых на форму кнопок. Для добавления меню имеется редактор Menu Editor. С его помощью и можно как раз добавлять, изменять, уничтожать меню.

Создай новый exe-проект. Вверху окна VB найди кнопку инструменты, и из выпадающего меню выберем Редактор Меню (рис.18). Обратит внимание, что вызов редактора меню доступен, когда ты нахожишься в окне формы - объект. Если ты редактируешь код программы или программа запущена, вызвать редактор меню нельзя.

Рисунок 18.
У нас в общем-то откроется окно меню редактора (Рис.19)

Рисунок 19.

В первой строке этого чуда "Название" (Caption) мы пишем название меню, то, что у на отобразиться на экране при запуске программы. А во второй строке, "Имя" (Name) имя меню, которое используется в программном коде. Заполнение обоих этих полей обязательно.
Для примера слепим меню, которое (должно же меню что-то делать), изменяет свойства графического объекта Shape, поскольку мы наплевали на него в прошлой главе. Поэтому, нажмем кнопку OK, отчего наше окно редактора меню закроется, положим на форму Form1 объект Shape1. По умолчанию он у нас будет в виде прямоугольника. Снова откроем редактор точно также, как и в первый раз.

Итак, в названии пишем "Фигура", а в имени - mnuShape. Почему mnu? Потому что так принято, и в коде программы потом будет легче отличать меню от прочих переменных.

Из нашего пункта меню Фигура должно выскакивать, например, подменю Прямоугольник и подменю Круг. Мы можем нажать кнопку Дальше и курсор в нижнем большом окне (окно списка меню) перескочит на следующую строчку, сейчас пустую, а можем просто мышь кликнуть по нужной строке и таким образом перейти к следующему пункту. Перейдем на вторую строку и напишем в названии Прямоугольник, а в имени - mnuPram. Перейдем на третью строчку и напишем соответственно Круг и mnuKrug. Поскольку названия и имена я придумывал на ходу, прошу простить за их несуразность.
Вот и здорово, но если мы запустим нашу программу, то увидим, что наши названия расположились рядком, и никаких подменю не получилось. Для того чтобы строчку Прямоугольник переместить в подменю, нужно щелкнуть по ней в окне списка меню (при этом она выделится) и нажать маленькую стрелочку вправо. Ты увидишь, что строка переместилась вправо, как бы стала на другой уровень. Нажимая кнопки вправо и влево мы создаем подменю второго, третьего и т.д. уровня или выводим из подменю на более высокий уровень. Теперь посмотри на эту таблицу.

Определение уровня подменю | Вид в редакторе меню | Вид в программе
Мы не нажимали стрелку вправо, все наши пункты меню расположились в главном меню, подменю нет. |  |
Мы нажали на стрелку вправо на строках "Прямоугольник" и "Круг" один раз, отчего они сдвинулись на 4 пикселя вправо и вошли в меню "Фигура", как строки подменю. |  |
Мы нажали на стрелку вправо на строках "Простой", "Скругленный", "Квадрат", а также "Круглый" и "Овальный" два раза, отчего они сдвинулись в право на 8 пикселей и вошли в подменю следующего уровня (в подменю "Прямоугольник" и подменю "Круг" соответственно). |  |

Сдвинем строчку Круг, как в таблице. Вот теперь у нас настоящее подменю. А кнопочками вверх и вниз мы можем изменять их положение по вертикали.

Теперь самостоятельно, так же, как в таблице, введем в подменю Прямоугольник подменю следующего уровня, куда войдет
Название Простой Имя mnuProstoi
Название Скругленный Имя mnuPramRound
Название Квадрат Имя mnuKvadrat
а в подменю Круг подменю следующего содержания (уровень тот же, что и у подменю Прямоугольник)
Название Круглый Имя mnuKrugli
Название Овальный Имя mnuOval

Кроме того еще введем паралельно меню Фигура меню Заливка (Самого первого уровня) c именем mnuPaint и в него поместим следующие подменю (их нужно передвинуть с помощью стрелки вправо на следующий уровень)
Название Залить Имя mnuZalit
Название Прозрачно Имя mnuProzrachno
Название Горизонтальная Имя mmuHorizont
Название Вертикальная Имя mnuVertical

В поле "Быстрая клавиша" из выпадающего списка мы можем назначить клавишу или комбинацию клавиш, для каждого пункта нашего меню.

Общий вид редактора меню должен получиться, как на рисунке 20.

Рисунок 20.

Если горячие клавиши были тобой назначены, они тоже показаны в окне списка меню (Ctrl+S, Ctrl+K и т.д.)

Что еще есть в редакторе меню. Ну понятно, что с помощью кнопок "Дальше", "Вставить", "Удалить" мы можем передвигаться в окне списка меню, вставлять и удалять строки. Если поставить галочку возле "Отмечено", то в меню данная строка тоже будет помечена галочкой (Это для переключателей) только эта галка так и будет светиться для нас это пока совершенно бесполезно. Галочка возле "Видимо" указывает на то, что эта стока видима, если ее убрать, пользователь не будет видеть эту строчку меню. Галочка возле "Включено" показывает, что эта строчка доступна пользователю. Если ее убрать, то эта строка будет показана тускло и кликать по ней пользователю будет бесполезно. Вообще-то эти свойства для пользы дела надо задавать программно, а не тыкать сейчас по ним мышью.

Дальше надо заставить наше меню работать. Нажми кнопку OK и выйди из редактора меню.
Внимание. Если кнопка OK не будет нажата, твои изменения в меню не сохранятся!
Закрой это чертов редактор и перейди к окну редактирования кода. Кликни по полю (General) и опаньки, все наши строчки там есть! Можно кликать и делать процедуру для любой строчки, то бишь пункта меню (рисунок 21).

Рисунок 21.
Но нам нужны только пунты самого последнего уровня, потому, что предыдущих уровней мы используем только для выбора.
Пишем процедуры, которые включим изменения свойств объекта Shape ( ну там заливку, и вид). Свойств объекта Shape на самом деле больше, но мы используем некоторые из них:

Private Sub
  mmuHorizont_Click()
Shape1.FillStyle = 2
End Sub

Private Sub mnuKrugli_Click()
Shape1.Shape = 3
End Sub

Private Sub mnuKvadrat_Click()
Shape1.Shape = 1
End Sub

Private Sub mnuOval_Click()
Shape1.Shape = 2
End Sub

Private Sub mnuPramRound_Click()
Shape1.Shape = 4
End Sub

Private Sub mnuProstoi_Click()
Shape1.Shape = 0
End Sub

Private Sub mnuProzrachno_Click()
Shape1.FillStyle = 1
End Sub

Private Sub mnuVertical_Click()
Shape1.FillStyle = 3
End Sub

Private Sub mnuZalit_Click()
Shape1.FillStyle = 0
End Sub

Можно запустить программу, и с помощью меню изменять вид объекта Shape.

Но мы еще с меню не закончили. Допусти нам захотелось, чтобы меню появлялось не в стандарном месте в верхней строке нашего окна, а допустим, где угодно, например около курсоры мыши. Давайте "Фигура" оставим как есть, а меню "Заливка" превратим в контекстное, т. е. всплывающее. Но для этого сначала познакомимся с методом PopupMenu.

Метод PopupMenu

С помощью этого метода мы можем создавать всплывающие меню в любом месте нашей формы (одновременно отображаться может только одно).
Синтаксис его таков

объект.PopupMenu имяменю, флаг, позицияX, позицияY, выделеннаястрока

Объект - необязательный параметр, если опущен, то принимается форма, на которой фокус
Имяменю - имя всплявающего меню. Обязательный параметр.
Флаг - константа или значение, указывающее расположение и поведение меню. Список констант приведен в таблице ниже. При использовании двух констант (по одной из каждой группы) необходимо между ними ставить логический оператор Or.
ПозицияX, позицияY - координаты X и Y, указывающие, где отобразиться окно. Если они опущены, то принимаются координаты мыши.
Выделеннаястрока - название элемента всплывающего меню, которое отобразится жирным шрифтом.

Таблица флагов, указывающих позицию и поведение контекстного меню.

Константа | Числовое значение константы | Описание
Размещение
vbPopupMenuLeftAlign | 0 | По умолчанию. Размещение по x левого края всплывающего меню
VbPopupMenuCenterAlign | 4 | Центрирование всплывающего меню по x.
VbPopupMenuRightAlign | 8 | Размещение по x правого края всплывающего меню.
Поведение
vbPopupMenuLeftButton | 0 | По умолчанию. Элемент на всплывающем меню реагирует только на щелчок левой кнопки мыши.
VbPopupMenuRightButton | 8 | Элемент на всплывающем меню реагирует на щелчки и левой и правой кнопки мыши.

Контекстное меню.
Теперь, опираясь на метод PopupMenu сделать контекстное меню раз плюнуть. Создадим процедуру, которая срабатывает на щелчок мыши по форме Form_Click и вставим в нее наш новый метод:

Private Sub Form_Click()
Form1.PopupMenu mnuPaint, , , , mnuZalit
End Sub

Как видишь, mnuPaint - это название нашего меню "Заливка", а mnuZalit - имя строки "Залить", оно будет выделяться жирным цветом. Остальные опции мы опускаем, хочешь, пробуй сам. Поскольку теперь меню "Заливка" в старом месте, в верхней строке формы нам не нужна, сделаем ее невидимой. Зайдем в Редактор Меню выделим в окне списка меню строку "Заливка" и поставим галочку у "Видимо". Ну, пока хватит. Если понадобиться исходник, то как всегда, его можно скачать вверху страницы.

Copyright © 2004 4us


Visual Basic с нуля. Глава 10. Массивы. Их границы. Сортировка.

Глава 10. Статические и динамические массивы. Границы массивов. Сортировка.
Скачать исходник программы ScanFiles
 Даты: создания 09.01.2005
дополнения 27.02.2005 {Автор 4us}

Массивы.

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

Массив - это сколько угодно значений, объединенных одним именем.

Массив, прежде чем использовать, надо объявить. Объявляется он совершенно также как и переменная. Если ты объявишь его локально в процедуре (здесь можно использовать ключевое слово Static), то он будет доступен только в этой процедуре и нигде больше, если в разделе (General)-(Daclarations) формы (c оператором Dim) - то во всей форме, а если тебе нужно объявить его глобально, так, чтобы он был доступен всему проекту, то в стандартном модуле с ключевым словом Public.
Кроме того , массивы бывают статическими и динамическими.

Статические массивы.

Статический массив - это когда ты заранее знаешь, сколько переменных туда нужно будет записать, т.е. если ты объявил массив на 10 членов, то уж туда никак не запихнешь одиннадцать. В форме он объявляется так:

Dim Chislo(9) As Long

Что здесь важно.
Dim - оператор, который точно также, как и при объявлении переменной, объявляет массив и выделяет для нее память.
Chislo - ну это просто имя, придумывается также как и для переменной и отвечает тем же требованиям.
(9) - количество элементов в массиве - их 10. Почему так. Дело в том, что элементы массива нумеруются с нуля, а не с единицы, поэтому у нас десять элементов массива c номерами 0, 1, 2, 3, 4, 5, 6, 7, 8 и 9. Если для каких-то целей такой порядок тебя не устраивает, то используется оператор Option Base 1, который прописывается в разделе Declarations и привязывает первый элемент ВСЕХ, повторяю ВСЕХ, массивов в программе к единице.
As Long - определение типа данных массива так же как тип обычной переменной. Почти всегда все элементы массива имеют один и тот же тип ( в данном случае длинное число Long). На крайняк, если это не так, или если ты не знаешь какого типа будут данные, можно задать тип Variant, но это нежелательно из-за того, что это занимает значительный объем памяти, особенно если массив большой.
Размерность - ее так сразу не заметно, но она здесь присутствует и равна единице, потому, что у на одна циферка (девятка), т.е. наш массив является одномерным - по сути список значений.
Теперь создадим массив для хранения результатов, ну скажем таблицы умножения на 8. Поскольку на ноль умножать бесперспективно, привязываем первый элемент массива к единице и объявляем переменную x для организации цикла For...Next.

Option Explicit
Option Base 1
Dim Chislo(10) As Long 'так как мы используем оператор Option Base, то элементы массива нумеруются с единицы
Dim x As Long

Положим на форму Text1 с установленными свойствами .MultiLine в True, а .ScrollBars - в 2-Вертикаль, а также кнопку Command1. В процедуре Form_Load напишем цикл, который будет присваивать каждому элементу массива значение, соответствующее его номеру, умноженному на 8.

Private Sub Form_Load()
Text1.Text = ""
For x = 1 To 10
Chislo(x) = x * 8
Next x
End Sub

Как видите, в цикле мы вместо номера элемента просто используем значение x, и таким образом перебираются все элементы массива. Теперь мы выведем значения элементов массива в Text1 в процедуре Command1.

Private Sub
  Command1_Click()
For x = 1 To 10
Text1.Text = Text1.Text & Chislo(x) & vbCrLf
Next x
End Sub

Таким образом наш одномерный массив представляет собой аналог таблицы с одной строчкой:

1 элемент | 2 элемент | 3 элемент | 4 элемент | 5 элемент | 6 элемент | 7 элемент | 8 элемент | 9 элемент | 10 элемент
8 | 16 | 34 | 32 | 40 | 48 | 56 | 64 | 72 | 80

Теперь переделаем его в двухмерный массив, чтобы в еще одну строчку записать множитель, на который умножается число 8. Переделаем наш код на такой:

Option Explicit
Option Base 1
Dim Chislo(10, 2) As Long
Dim x As Long

Private Sub Form_Load()
Text1.Text = ""
For x = 1 To 10
Chislo(x, 1) = x
Chislo(x, 2) = x * 8
Next x

Private Sub Command1_Click()
For x = 1 To 10
Text1.Text = Text1.Text & "8 x " & Chislo(x, 1) & "
  = " & Chislo(x, 2) & vbCrLf
Next x
End Sub

И наш массив будет представлять собой следующее

 | 1 элемент | 2 элемент | 3 элемент | 4 элемент | 5 элемент | 6 элемент | 7 элемент | 8 элемент | 9 элемент | 10 элемент
1 элемент | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10
2 элемент | 8 | 16 | 34 | 32 | 40 | 48 | 56 | 64 | 72 | 80

таким образом элемент массива Chislo (7,2) будет иметь значение 56.
Обратите внимание, что размерность массива определяет колиство циферек в объявлении. Массив Chislo (10,5) - тоже двухмерный, только строк в табличке было-бы не две, а пять. А объявление трехмерного массива выглядело бы так
Dim(10,5,2).Такую трехмерную таблицу нарисовать мне затруднительно. В принципе VB поддерживает до 64 размерностей массива, но это в голове трудно укладывается.
Все, как видите очень просто. Однако статические массивы используются довольно редко. Чаше мы не знаем сколько данных мы будем иметь и потом ведь хочется дописать при необходимости в массив новые данные. Для этого существуют

Динамические массивы.

Часто возникает ситуация, когда мы не знаем заранее, сколько элементов массива мы будем использовать заранее. Поэтому массив объявляется без размерности, например
Dim Mass () as String
Но, перед его непосредственным использованием, его надо переобъявить c указанием размерности. Делается это с помощью оператора Redim.

Redim Mass (5) as String

Вообще-то, у нас два пути использования динамического массива.
Первый путь, это года мы узнаем (просчитываем) , сколько элементов массива нам нужно, и после этого объявляем массив небходимого нам размера. Однако мне это путь не очень нравится, поскольку, нам приходится задавать два цикла: один - для просчета необходимого количества элементов, второй - для собственно присваивания массиву значений переменных.
Второй путь, это когда мы в одном цикле при нахождении каждого нового данного переобъявляем массив с увеличением количества его элементов. Но этот способ загружает компьютер и может занимать много времени на обработку, особенно если создается большой массив. Происходит это из-за перераспределения элементов массива в памяти всякий раз при его переобъявлении и добавлении нового члена. Но именно такой способ мы применим при разработке программки Scanfiles, которая будет сканировать файлы в выбранной директории, сортировать их по-возрастанию и выводить в Text1. Хочу сразу заметить, что при переобъявлении массива все записанные в него данные стираются - массив обнуляется. Чтобы этого не происходило, надо использовать ключевое слово Peserve:

Redim Preserve Mass (5) as String

Идем дальше. Поместим на форму объекты Dir1 и Text1. Начнем писать код. Обратите внимание, что процедуры Form_Load у нас не будет, так как под это событие обрабатывать нам нечего. Ну-с, фигачим

Option Explicit
'Установим начальную нумерацию массива с единицы, сейчас так удобнее
Option Base 1
'объявим переменные
Dim OurDir As String 'для директории, где будем искать файлы
Dim FileName As String 'для имен находимых файлов
Dim X As Long 'просто для цикла
'и, наконец, наш динамический массив, как строковый
Dim Files() As String

Кроме того, поскольку одни имена - это уж совсем скучно, то мы еще во вторую размерность массива выведем атрибуты файла. Атрибуты, это когда по файлу шлепаешь правой кнопкой мыши и в выпавшем меню выбираешь "Свойства". Тогда снизу окна этих свойств можно увидеть галочки рядом с загадочными словами "Только чтение", "Скрытый" и "Архивный". Именно эти свойства можно устанавливать или получать с помощью оператора SetAttr и функции GetAttr. Весь геморрой в том, что этот атрибут представляет из себя число, получаемое из суммы значений атрибутов, приведенных в таблице ниже, и чтобы понять, какой атрибут все-таки у файла, нужно "с помощью оператора And выполнить поразрядное сравнение значения". Друзья мои. Это цитату из Help5 я привел для того, чтобы можно было почувствовать разницу между "академическим" и "вольным" изложением проблемы. Короче, привожу таблицу этих атрибутов:

Константа | Значение | Описание
vbNormal | 0 | Обычный.
vbReadOnly | 1 | Только для чтения.
vbHidden | 2 | Скрытый.
vbSystem | 4 | Системный.
vbDirectory | 16 | Каталог или папка.
vbArchive | 32 | Архивный.

Из всей этой дребедни нам нужны три константы: vbArchive, vbReadOnly и vbHidden для этого самого "поразрядного сравнения". Делается это так:
Чтобы узнать, только для чтения ли этот файл:
Переменная = GetAttr("полный путь к файлу") And vbReadOnly
Если в Переменной не ноль, то файл - только для чтения. Аналогично для других констант.
Чтобы установить аттрибут, например "Архивный" для файла:
SetAttr "C:\Andrey\index.htm", vbReadOnly
при этом уже установленные атрибуты файла сбрасываются. Если надо установить атрибуты "только для чтения" и "архивный":
SetAttr "C:\Andrey\index.htm", vbReadOnly +vbArchive
Все просто, а по сути - издевательство. Про атрибуты - все. Более интересные данные о файлах мы получим, когда начнем использовать FSO. Но об этом не сегодня.
Итак, для хранения атрибута (на основании "поразрядного сравнения") продолжаем объявлять переменные (еще две)

Dim Attr As Long 'числовая, для атрибута файла
Dim AttributFile As String 'строковая, для записи атрибута в виде слов

Напишем маленькую процедурку для Dir1 на событие Change. Дело в том, что событие Change наступает при двойном клике, а я и хочу, чтобы директория для поиска файлов устанавливалась по двойному клику. В этой процедурке мы очистим Text1 и обнулим переменные, ну и главное, запишем в переменную OurDir полный путь к директории, после чего перейдем к другой поцедуре - ScanDir, где собственно и будем искать файлы и записывать их имена и атрибуты в наш массив Files.

Private Sub
  Dir1_Change()
Text1.Text = ""
FileName = ""
X = 0
OurDir = Dir1.Path
ScanDir 'переходим к процедуре сканирования файлов
End Sub

Далее в процедуре ScanDir все, с учетом комментариев, понятно:

Private Sub ScanDir()
FileName = Dir(OurDir & "\*.*", 0) 'присваиваем переменной значение функции Dir для всех файлов
ReDim Files(2, 1) 'переобъявляем массив с минимальной размерностью, иначе может возникать ошибка
Do While FileName <> "" 'запускаем цикл, до тех пор, пока Dir не вернет пустую строку
X = X + 1 'счетчик элементов массива
ReDim Preserve Files(2, X) 'переобъявляем массив по счетчику, сохраняя уже имеющиеся в нем данные
AttributFile = "" 'обнуляем переменные
Attr = 0
'проверяем файл на атрибут архивный
Attr = GetAttr(OurDir & "\" & FileName) And vbArchive
If Attr > 0 Then AttributFile = AttributFile & " Архивный"
'проверяем файл на атрибут только для чтения
Attr = GetAttr(OurDir & "\" & FileName) And vbReadOnly
If Attr > 0 Then AttributFile = AttributFile & " Только чтение"
'проверяем файл на атрибут скрытый для порядка, Бейсик все равно не видит такие файлы
Attr = GetAttr(OurDir & "\" & FileName) And vbHidden
If Attr > 0 Then AttributFile = AttributFile & " Скрытый"

Files(1, X) = FileName 'пишем в массив имя файла
Files(2, X) = AttributFile 'пишем в массив атрибут файла
FileName = Dir() 'запускаем функцию Dir без атрибутов
Loop 'и так в цикле, пока
  файлы в директории не закончатся

If X > 0 Then
Sort ' если хоть один файл найден,
'отправляемся к процедуре сортировки
Else
Text1.Text = "Файлов не найдено" 'в противном случае выводим сообщение.
End If
End Sub

Основная прелесть массивов в том, что с данными, записанными в них, можно что угодно делать: сортировать, разбивать на группы, осуществлять поиск, делать выборки и т.п. Сейчас мы наш массив отсортирум по возрастанию имен файлов. Скажу сразу, что алгоритмы сортировки разработаны и существуют давно. Приводимый в примере был разработан лично мною, а возможное совпадение его с другими алгоритмами совершенно случайно. Но прежде, чем перейти к сортировке, я хочу рассказать вот о чем.
Поскольку количество элементов нашего массива меняется, а для сортировки его с помощью циклов For...Next нам надо точно знать минимальный (нижняя граница) и максимальный (верхняя граница) доступные значения индекса массива, то я использую функции LBound для определения минимального индекса и UBound для определения максимального индекса указанной размерности. Синтаксис их такой

Переменная=LBound(Massive,1) 'возвращает в Переменную минимальный индекс массива Massive по размерности 1.
Переменная=UBound(Massive,1) 'возвращает в Переменную максимальный индекс массива Massive по размерности 1.

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

Private Sub Sort() 'процедура сортировки
Dim K As Long
Dim N As Long
Dim y As Long 'просто для цикла For...Next
Dim Peremen As String 'для временного храненения из массива имени файла
Dim Peremen2 As String 'и атрибута файла
Dim NomerPerem As String
Dim NomerPerem2 As String
'сортировка массива
K = LBound(Files, 2) 'присваиваем переменной К начальное значение массива - 1 (нижняя граница)
For y = LBound(Files, 2) To UBound(Files, 2) 'просматриваем все строки массива с нижней до верхней границы
Peremen = Files(1, y) 'присваиваем каждую строку в переменные
Peremen2 = Files(2, y)
'вложенный цикл
For N = y To UBound(Files, 2) 'просматриваем строки массива, начиная с той,
'значения которой храняться в переменных Peremen и Peremen2

If Files(1, N) < Peremen Then 'если значение в массиве меньше, чем в переменной
Peremen = Files(1, N) 'то присваиваем переменным Peremen и Peremen2 новые, меньшие значения
Peremen2 = Files(2, N)
K = N 'присваиваем номер найденного элемента массива переменной K
End If
Next N
'конец вложенного цикла
NomerPerem = Files(1, y) 'сохраняем в переменных старые значения строки массива
NomerPerem2 = Files(2, y)
Files(1, y) = Peremen ' и присваиваем этой строке массива новые
Files(2, y) = Peremen2
If K > 0 Then 'если K не ноль,
Files(1, K) = NomerPerem 'то строке К присваиваем старые значения из строки y
Files(2, K) = NomerPerem2
End If
Peremen = "" 'обнуляем переменные
Peremen2 = ""
K = 0
Next y
'конец сортировки
'просто выводим в Text1 отсортированные значения массива
For X = 1 To UBound(Files, 2)
Text1.Text = Text1.Text & Files(1, X) & Files(2, X) & vbCrLf
Next X
End Sub

Исходник программы можно, как всегда, скачать вверху страницы.

Можно ли присвоить один массив другому не по отдельному элементу (в цикле), а сразу.

Вопрос посетителя сайта Cubic: Можно ли присвоить один массив другому не по отдельному элементу (в цикле), а сразу?

Присвоение массивов.
Visual Basic 6.0 и версии выше дают возможность проводить операции присваивания с массивами точно также, как с переменными. Теперь нет необходимости создавать цикл For...Next для присваивания одного массива другому по каждому элементу. Достаточно написать такой оператор
NewMassive=OldMassive
и содержимое массива OldMassive присвоится массиву NewMassive.
Однако при этом следут учитывать, что для исключинения ошибок при таком присвоении, желательно соблюдать одинаковую размерность и тип массивов. Хотя при присвоении динамического массива динамическому массиву, массив в левой части оператора изменяется, как бы подстраивается под оператор в правой части. Однако при работе со статическими массивами возможна ошибка компиляции. Кроме того при присвоении, например массива типа Long типу Integer может возникнуть ошибка переполнения (Owerflow). В программе операция присвоения может выглядеть приблизиельно так (на форме должны быть кнопка Command1 и текстбокс Text1):

Option Explicit
Dim OldMassive() As Long
Dim NewMassive() As Long

Private Sub Command1_Click()
Dim x As Long

For x = 0 To 999 'просто
  заполнение массива цифрами
ReDim Preserve OldMassive(x)
OldMassive(x) = x
Next x

NewMassive = OldMassive 'присоение массивов

For x = 0 To UBound(NewMassive)
  ' считывание нового массива в Text1
Text1.Text = Text1.Text & NewMassive(x) & vbCrLf
Next x
End Sub

Я думаю, особых комментариев здесь не требуется.

Copyright © 2005 4us


Visual Basic с нуля. Глава 12. ActiveX: Progress Bar. Slider.

Глава 12. ActiveX: Индикатор хода выполнения Progress Bar. Скользящая шкала Slider.
Скачать исходник программы "Generator"
или "SliderPic"
 Дата создания 21.01.2005 {Автор 4us}

Что такое ActiveX?
Мой дорогой друг. Когда ты устанавливал на жесткий диск Visual Basic 6.0, то в твой компьютер скачалось много всякого добра, и в том числе куча файлов средств управления ActiveX. Эти файлы лежат в c:\Windows\System или c:\Windows\System32 и имеют расширение .ocx. Большинство этих файлов представляют собой отдельные средства ActiveX, но например, тот, что мы будем рассматривать сегодня (Microsoft Windows Common Controls 6.0) включает в себя аж девять средств управления ActiveX. Некоторые средства ActiveX перечислены на странице "Элементы ActiveX" на сайте http:/vbzero.narod.ru. Сегодня мы поговорим о некоторых.

Начнем с самых нужных и используемых. Одним из таких средств является индикатор хода выполнения - Progress Bar. Если ты достаточно наблюдателен,то наверняка заметил,что при выполнении длительных процессов (копирование файлов, загрузка программ и т.п.) Windows выводит на экран окошко, где синенькая полосочка (индикатор хода выполнения процесса), постепенно увеличиваясь в длине, показывает, какой объем процесса выполнен, и вообще скорость процесса. Индикатор хода выполнения позволяет юзеру не нервничать и утешаться тем, что процесс худо-бедно идет, что он успеет раза два покурить и что компьютер не завис.

Элемент ActiveX: ProgressBar.
Чтобы приступить к использованию Progress Bar, его надо сперва добавить в панель инструментов нашего нового exe-проекта (по-скольку это средство управления ActiveX храниться в файле mscomctl.ocx). Сделать это - раз плюнуть. Для этого надо создать exe-проект, затем в меню Проект надо щелкнуть по строке Компонеты (Рис.22)

Рисунок 22.
Затем, в открывшемся окне списка средств управления найди строку Microsoft Windows Common Controls 6.0 , щелкни в этой строке по полю флажка, поставив галочку и нажми кнопку OK на окне (Рис.23).

Рисунок 23.
У тебя в панели инструментов появилось сразу несколько объектов. И в том числе ProgressBar (Рис.24). Аналогично добавляются и другие средства ActiveX из других файлов.

Рисунок 24.
Теперь ты можешь перетащить его на форму и начать с ним работать. Он будет называться ProgressBar1. Основными свойствами, которыми мы будем пользоваться являются:
.Min - минимальное числовое значение, с которого шкала начнет работать
.Max - максимальное числовое значение, на котором шкала дойдет до конца.
.Value - текущее числовое значение шкалы
кроме того можно использовать
.Scrolling - меняет прерывистую шкалу (0-ccScrollingStandard) на непрерывную (1-ccScrillingSmooth)
.Orientation - установка в вертикальное (если приспичило) или горизонтальное положение
Кроме того, понятно, что имеются всякие стандартные свойства (типа высота-длина, видимый-невидимый и т.п), о которых и говорить-то нечего.

Вообще использовать ProgressBar имеет смысл только в циклах (любых). Ты задаешь начальное и конечное значение, а значение ProgressBar.Value меняется именно в цикле. Собственно именно оно и заставляет шкалу изменяться. Использование этого объекта рассмотри на примере программы Generator, которая иммитирует игрушки-гадалки. Знаешь такие китайские игрушки: загадаешь желание, крутанешь шарик, а он потом остановится и лампочка зажжется у слова типа "Никогда" или "Скоро". Так вот, у нас у аналогичных слов (они заданы в окне свойств, а не в коде) будет останавливаться шкала ProgressBar со свойством .Scrolling - 1-ccScrillingSmooth (неперывная).
Объявим две переменные: первая для хранения сгенерированного случайного числа, вторая - просто для цикла.

Dim MyValue As Long
Dim x As Long

Положим на форму командную кнопку Command1 и в ней напишем процедурку

Private Sub Command1_Click()

Здесь мы используем генератор случайных чисел. Мы уже использовали генерирование случайных чисел в Главе 3 при создании программы "Однорукий бандит", хоть и без оператора Randomize. Этот оператор просто генерирует число и передает его в качестве опорного в функцию Rnd, которая в свою очередь на основе этого числа генерирует случайное число от 0 до 1.

Randomize ' Инициализирует генератор случайных чисел.

Поскольку, функция Rnd возвращает число, меньшее единицы, мы умножаем его на 10000, а чтобы избежать нулевого значения, прибавляем единицу. Кроме того, получившийся результат округляем до целого числа с помощью функции Int. Эта функция просто отбрасывает дробную часть (а на самом деле не округляет). Синтаксис ее: Int(число). Аналогична ей функция Fix(число), которая работает так же, как и Int, но при округлении отрицательного числа приближает его к числу большему или равному, в то время как Int - к меньшему или равному.

MyValue = Int((10000 * Rnd) + 1) ' Возвращает случайное число от 1 до 10000.

Далее уже задаем начальное (1) и конечное (10000) значение ProgressBar1.

ProgressBar1.Min = 1
ProgressBar1.Max = 10000

Теперь организуем цикл от единицы до полученного нами случайного числа.
В нем при каждом его выполнении значение ProgressBar1.Value изменяется, что отображается на изменении шкалы.

For x = 1 To MyValue
ProgressBar1.Value = x
Next x
End Sub

Вот и вся программка. Да, еще хочу сказать, что число 10 000 выбрано для того, чтобы ProgressBar работал достаточно медленно. Скачать исходник можно вверху страницы.

При загрузке файла средств управления общего назначения в панели инструментов появились и другие объекты ActiveX. Обратим наши взоры на средство Slider, которое располагается через один и ниже ProgressBar'а, думаю, найдешь.

Элемент ActiveX: Slider.
По сути своей этот элемент очень похож на объект "полоса прокрутки ScrollBar", который мы разбирали в Главе 4. Главными отличиями его являются внешний вид: Slider представляет собой линейку с бегунком и отметками-маркерами (тиками), а также то, что его максимальное значение не ограничено, как у ScrollBar'а значением 32767. Элемент этот простой, я не буду повторять описание его свойств .Min, .Max, .Value и т.п. Если пропустил, посмотри выше на этой странице. Однако у Slider'а есть и другие свойства:

.LargeChange - величина, на которую изменится положение бегунка при щелчке мышью по слайдеру
.TickFrequency - определяет с какой частотой на слайдере будут располагаться маркеры (тики). Например, если у нас Slider1.Min=0, Slider1.Max=1000, а Slider1.TickFrequency=100, то на всем слайдере будут равномерно располагаться (1000-0)/100+1 = 11 тиков, первый из которых будет обозначать 0, второй - 100 и т.д., до последнего - 1000.

Как работать со слайдером, мы разберем примере программки SliderPic (скачать ее можно вверху страницы). Практического применения ей я не вижу, но она иллюстрирует, как с помощью слайдера можно изменять объекты. В нашем случае мы с помощью двух слайдеров - горизонтального и вертикального будем менять, соответственно, ширину и высоту Picture для того, чтобы в нее влезла картинка. Сразу оговорюсь, что поскольку единица измерения размеров объектов в VB принята почему-то в твипах, я все в твипах и оставлю. Если тебе охота перевести в другие единицы, например, в пиксели,- пожалуйста (в Главе 6 мы использовали методы ScaleX и ScaleY для перевода в из одного измерения в другое).
Итак, создадим exe-проект, положим на него Picture1 любого размера, но желательно позиционировать его ближе к верхнему левому углу формы (а то куда ж мы его расширять будем), и два слайдера -Slider1 (горизонтальный) и Slider2 (вертикальный). Чтобы Slider2 стал горизонтальным, надо его свойство .Orientation в окне свойств установить в 1-ccOrientationVertical.
Теперь пишем код.

Option Explicit

В процедуре загрузки формы привяжем начало каждого слайдера к положению Picture

Private Sub Form_Load()

Устанавливаем левую границу Slider1 вровень с левой границей Picture1

Slider1.Left = Picture1.Left - 150

Также устанавливаем верхнюю границу Slider2 вровень с верхней границей Picture1

Slider2.Top = Picture1.Top - 150

Как видишь, мы здесь используем свойства .Top и .Left, которые есть практически у всех объектов. Они показывают расстояние от края формы до границы объекта сверху (.Top) и снизу (.Left).
Поскольку бегунок слайдера в нулевой позиции отстоит от начала самого слайдера где-то на 150 твипов, я из значения положения картинки вычел это расстояние.
Далее загружаем картинку в Picture1. Прошу прощения, за ее пошлость, просто под руку попалась.

Picture1.Picture = LoadPicture(App.Path & "\Lopes.jpg")

Затем устанавливаем свойства для каждого слайдера. Значения .Max слайдеров я взял от фонаря, исходя из того на сколько можно расширить картинку, чтобы она в экран влезала.

Slider1.Min = 0
Slider1.Max = 10215
Slider1.LargeChange = 500
Slider1.Value = Picture1.Width
Slider1.TickFrequency = 500

Slider2.Min = 0
Slider2.Max = 6975
Slider2.LargeChange = 500
Slider2.Value = Picture1.Height
Slider2.TickFrequency = 500

End Sub

Для каждого слайдера создаем процедурку, где присваиваем значению ширины (или
  длины) Picture1 значение слайдера.

Private Sub Slider1_Click()
Picture1.Width = Slider1.Value
End Sub

Private Sub Slider2_Click()
Picture1.Height = Slider2.Value
End Sub

Вот, собственно, и все.


Visual Basic с нуля. Глава 13. Работа с принтером. Объект Printer. Его свойства и методы. Функция InStr.

Глава 13. Работа с принтером. Объект Printer. Его свойства и методы. Функция InStr.
Скачать исходник программы Printing
 Дата создания 29.01.2005 {Автор 4us}

Ну, друзья мои, мы дошли до 13-й чертовой главы. Не будем суеверными и попробуем разобраться, как вывести на печать результаты нашей программной деятельности. Не хочу тебя огорчать, но для этого нужен принтер, который подключен к твоему компьютеру и готов к работе. Если его нет, эту главу не читай.

Традиционно во всех учебных изданиях разбирается пример, как вывести на печать всю форму целиком. На фига нужно выводить всю форму, какая от этого нам польза, остается для меня загадкой на протяжении долгого времени и гнетет мое сознание. Однако, отдавая дань традициям, все-таки напомню, что форма выводится на печать с помощью метода PrintForm. То бишь, ты берешь любую готовую программу, добавляешь кнопку Command, называешь ее "Печать", и в код ее процедуры гордо пишешь:

Form1.PrintForm

Запускаешь программу, нажимаешь эту созданную кнопку и у тебя на печать выходит Form1 со всеми элементами-объектами, видимыми на момент печати. Ты некоторое время смотришь на это безумие, потом перед командой PrintForm делаешь невидимыми кнопки и в надежде на чудо, снова повторяшь процесс, но чуда не произошло. Кнопок теперь нет, но распечатка красивше от этого не стала. И понося на чем свет VB, Microsoft и его хозяина, выключаешь все и идешь пить пиво.

Вот про печать и все.

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

А теперь скажу, что подготовка для печати красивой страницы - достаточно кропотливый процесс. То есть, надо программно расчитывать месторасположение всех элементов страницы, причем в зависимости от формата листа и размера шрифта и всего остального. Зато тщательно подготовленная страница выглядит, как настоящий документ, очень достойно.

Основной объект, с которым мы будем работать - Printer. Несмотря на то, что это объект, ни самого объекта в окне инструментов или на форме, ни свойств в окне свойств мы не увидим. Все надо делать программно. Но этот объект имеет просто кучу свойств и методов, большинство из которых нам нужны. Постараемся разобраться подробнее. Хочу заметить, что не все принтеры поддерживают все свойства объекта Printer. В этом случае разные свойства могут вызывать одинаковое действие или же вообще вызывать ошибку. Я для написания примера использовал довольно-таки старый лазерный черно-белый принтер HP LaserJet 5L, и основываясь на его возможностях и готовил пример. Можно было взять цветной струйник, но струйники - дороги в эксплаутации и, как правило, документы печатаются на лазерных принтерах.
Итак, начнем со свойств, список важнейших из них приведен в таблице ниже:

Свойство | Характеристика | Синтаксис
.Copies | определяет число печатаемых копий | Printer.Copies=1 (одна копия)
.ColorMode | определяет цветную или монохромную (оттенки серого) печать vbPRCMMonochrome=1 (черно-белая) vbPRCMColor =2 (цветная). Монохромные принтеры это свойство игнорируют. | Printer.ColorMode=1 или Printer.ColorMode=vbPRCMMonochrome
.CurrentX и .CurrentY | устанавливают горизонтальную (CurrentX) или вертикальную (CurrentY) координаты для начала (продолжения) печати. Определяется по-умолчанию в твипах, либо в текущих единицах измерения | Printer.CurrentX=500 Printer.CurrentY=1000
.Duplex | возвращает или устанавливает значение, которое определяет, будет ли страница печататься с двух сторон, константы: vbPRDPSimplex =1, vbPRDPHorizontal= 2, vbPRDPVertical=3. | Printer.Duplex=1 (Одностороння печать с текущей ориентацией листа) Printer.Duplex=2 (Двухсторонняя печать горизонтальной страницы) Printer.Duplex=3 (Двухсторонняя печать вертикальной страницы)
.Font | определяет "жирность" текста, подчеркивание, перечеркивание, курсив и т.п. | Printer.Font.Bold = True (жирный) Printer.Font.Underline = True (подчеркивание)
.FontCount | возвращает число доступных шрифтов | Text1.Text=Printer.FontCount
.FontName | возвращает или устанавливает имя шрифта | Printer.FontName="Arial" (устанавливает шрифт Arial)
.Fonts | возвращает имена всех доступных шрифтов | Dim I As Long For I = 0 To Printer.FontCount -1 List1.AddItem Printer.Fonts (I) Next I
.FontSize | возвращает или устанавливает размер шрифта в пунктах (максимальное значение 2160). Задается после задания свойства FontName. При использовании шрифтов TrueType менее 8 пунктов, надо сначала задать свойство FontSize, затем FontName, а потом снова FontSize | Printer.FontSize=18
.Height и .Width | устанавливают физические размеры листа бумаги. Используются вместо установок PiperSize. Для Printer задается только в твипах. | Printer.Height=5000 (Высота) Printer.Widtht=3000 (Ширина)
.Orientation | возвращает или устанавливает значение, указывающее, в вертикальном или горизонтальном расположении печатаются документы. Принимает значение двух констант: vbPRORPortrait =1 (вертикаль), vbPRORLandscape=2 (горизонт) | Printer.Orientation=1 или Printer.Orientation=vbPRORPortrait
.Page | возвращает текущий номер страницы, начиная с единицы и шагом единица | Printer.Print Printer.Page (вывод на печать текущей страницы)
.PaperBin | возвращает или устанавливает значение, указывающее для принтера установленный по умолчанию лоток для подачи бумаги. Константы перечислены в библиотеке объектов Visual Basic (VB) в Object Browser. | Printer.PaperBin=1 (верхний лоток) Printer.PaperBin=2 (нижний лоток) Printer.PaperBin=3 (средний лоток) Printer.PaperBin=4 (ручная подача) Printer.PaperBin=7 (лоток по умолчанию) и т.д.
.PaperSize | возвращает или устанавливает значение, указывающее размер бумаги для текущего принтера. vbPRPSA4= 9 для листа A4, 210 x 297 мм vbPRPSUser=256 для пользовательского Константы перечислены в библиотеке объектов Visual Basic (VB) в Object Browser. | Printer.PaperSize=9 или Printer.PaperSize=vbPRPSA4 (установка листа A4)
.PrintQuality | возвращает или устанавливает значение, указывающее разрешающую способность принтера (отрицательная константа). Можно также задавать положительные значения в точках на дюйм (dpi). | Printer.PrintQuality=-1 (черновой- draft) Printer.PrintQuality=-2 (низкая- Low) Printer.PrintQuality=-3 (средняя- Medium) Printer.PrintQuality=-4 (высокая- High) Printer.PrintQuality=300 (разрешение 300 dpi)
.Zoom | возвращает или устанавливает процентное соотношение, по которому увеличивается или уменьшается масштаб вывода на печать. Число обозначает процент и по умолчанию равно 0, что соответствует стандартному размеру. | Printer.Zoom=50 (лист с уменьшением 1:2)

Теперь, как ни печально, надо переходить к методам, но их не так много. На мой взгляд важные приведены в таблице ниже:

Метод
Характеристика
Синтаксис
.EndDoc | завершает формирование листа для печати и начинает печать | Printer.EndDoc
.KillDoc | немедленно завершает печать сформированной страницы | Printer.KillDoc
.NewPage | завершает текущую страницу на объекте Printer и переходит на следующую | Printer.NewPage
.TextHeight и .TextWidth возвращают высоту (TextHeight ) и длину (TextWidth) текстовой строки, которая получится при ее выводе на печать или на форму с использованием текущего шрифта. В возвращаемое значение включаются и интервалы. Text1.Text=Printer.TextHeight("vbzero")
Text2.Text=Printer.TextWidth("vbzero")

Tеперь попробуем разобраться, как все это использовать на примере программки Printing. Чтобы у нас было, что выводить на печать, я набросал подобие программки, представляющую собой рабочее места продавца мобильных телефонов. Советую вам скачать ее вверху страницы, чтобы иметь перед глазами, а потом продолжать читать главу.
Создадим новый exe-проект. Первая ее часть представляет собой интерфейс для работы с базой мобильных телефонов: вывод на экран изображения и технических характеристик телефона, две кнопки навигации - вперед и назад и кнопка Печать. Изображение и теххарактеристики грузятся из файлов, имеющих имена 1.jpg и 1.txt, 2.jpg и 2.txt, и так далее, т.е., аналогично программке Tester, которую мы разбирали в Главе 8.

Итак стандартное начало:

Option Explicit
Option Base 1

Объявляем статический одномерный массив для двенадцати строк технических характеристик телефона:

Dim MassiveData(12) As String

Затем несколько необходимых переменных

Dim FileName As String 'для имени загружаемых файлов
Dim F As Long 'для номера свободного файла
Dim X As Long 'для переменной цикло For...Next
Dim QuantityFonts As Long 'для числа найденых шрифтов

Затем процедура загрузки формы. Здесь мы просто изначально устанавливаем имя загружаемого файла - "1" ("1.jpg" и "1.txt") и переходим к созданной нами процедуре загрузки этих файлов DataLoading().

Private Sub Form_Load()
FileName = "1"
DataLoading
End Sub

Private Sub DataLoading()

Загружаем изображение телефона в Image1

Image1.Picture = LoadPicture(App.Path & "\" & FileName & ".jpg")

Восстанавливаем положение Image1 от левой и верхней границы экрана соответственно

Image1.Left = 24
Image1.Top = 128

Центрируем положение картинки в зависимости от того, вертикальная она (Image1.Height > Image1.Width - высота больше ширины) или горизонтальная (Image1.Width > Image1.Height - ширина больше высоты)

If Image1.Width > Image1.Height Then Image1.Top = Image1.Top + (250 - Image1.Height) / 2
If Image1.Height > Image1.Width Then Image1.Left = Image1.Left + (250 - Image1.Width) / 2

Открываем текстовой файл с тем же именем, что и картинка и грузим 12 строк текста в 12 элементов массива

F = FreeFile
Open App.Path & "\" & FileName & ".txt" For Input As #F
For X = 1 To 12
Line Input #F, MassiveData(X)

Тут же выводим содержание массива в Label9. Обратите внимание, что Label9 представляет собой Control Array (по сути - это одномерный массив объекта), т.е. группу лейблов с именем Label9(индекс). По этому индексу мы можем перебирать Label9 в цикле. Делается это так: мы кладем на форму Label9, затем копируем его. VB спрашивает "Создавать Control Array?", ты отвечаешь "Да", после чего на форме появляется копия Label9 с именем Label9(1). Наш же просто лейбл Label9 становится Label9(0). Продолжаем этот процесс до получения нужного количества лейблов в Control Array. А потом, ненужные элементы надо уничтожить. У нас, поскольку мы используем оператор Option Base 1, и элементы массива начинаются с единицы - это Label9(0). Аналогично создается и Control Array и для Label8, куда присваиваются наименования технических характеристик телефона. Для облегчения кода я сделал это присвоение в окне свойств Label8.

Label9(X).Caption = MassiveData(X)
Next X
Close #F

End Sub

В кнопках Command1 и Command2 мы пишем код для смены имени загружаемого файла. Все это опять-таки похоже на программку Tester из Главы 8, и на этом я подробно останавливаться не буду.

Private Su b Command1_Click()
If Val(FileName) > 1 Then
FileName = Str(Val(FileName) - 1)
FileName = Right(FileName, Len(FileName) - 1)
DataLoading
End If
End Sub

Private Sub Command2_Click()
FileName = Str(Val(FileName) + 1)
FileName = Right(FileName, Len(FileName) - 1)
If Dir(App.Path & "\" & FileName & ".txt") <> "" Or Dir(App.Path & "\" & FileName & ".jpg") <> "" Then
DataLoading
Else
FileName = Str(Val(FileName) - 1)
FileName = Right(FileName, Len(FileName) - 1)
DataLoading
End If
End Sub

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

Private Sub Command3_Click()
Dim OrientTelefona As Single ' меняет отступ от картинки в зависимости горизонтальная она или вертикальная
Dim Fonts() As String 'одномерный динамический массив для хранения существующих шрифтов
Dim VertCoord As Single 'накапливает сдвиг по вертикали по мере создания строк в документе
Dim NumberFont As Long ' номер шрифта

обнуляем переменную, содержащую количество шрифтов на всякий случай

QuantityFonts = 0

В смысле определения шрифтов на текущем принтере я пошел по пути наименьшего сопротивления. Исходя из того, что на любом компьютере установлен хотя бы один из четырех общеупотребимых шрифтов, я проверяю их наличие и имена найденых шрифтов загружаю в динамический массив Fonts(). Для этого мы организуем цикл, где перебираем все существующие на текущем компьютере шрифты (их количество нам возвращает свойство .FontCount), и грузим их имена в массив Fonts(). При этом счетчик QuantityFonts отражает, сколько из четырех шрифтов найдено.

For NumberFont = 0 To Printer.FontCount - 1
If Printer.Fonts(NumberFont) = "Arial Cyr" Or Printer.Fonts(NumberFont) = "Times New Roman" _
Or Printer.Fonts(NumberFont) = "Courier New" Or Printer.Fonts(NumberFont) = "MS Sans Serif" Then
QuantityFonts = QuantityFonts + 1
ReDim Preserve Fonts(QuantityFonts)
Fonts(QuantityFonts) = Printer.Fonts(NumberFont)
End If
Next NumberFont

Затем, если хоть один шрифт найден, мы устанавливаем его из первого элемента массива

If QuantityFonts > 0 Then Printer.FontName = Fonts(1)

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

Printer.ScaleMode = vbCentimeters

Хочу сказать, что начинать формирование страницы методом Printer.NewPage не надо, иначе первой вылезет пустая страница. У тебя нет еще ни одной страницы, а ты хочешь создавать новую. Вот для второй и т.д. страницы - пожалуйста.
устанавливаем размер шрифта для первой строки

Printer.FontSize = 12

'устанавливаем подчеркивание
Printer.Font.Underline = True
'устананавливаем качество печати - среднее
Printer.PrintQuality = 3

Прижимаем вправо первую строчку с названием фирмы. Для установки положения начала вывода по-горизонтали используется свойство .CurrentX. Что-бы строчка прижалась вправо мы из общей ширины нашего документа Printer.ScaleWidth вычтем длину нашей строчки Printer.TextWidth(Label1.Caption), которую она займет при печати объявленным нами шрифтом:

Printer.CurrentX = Printer.ScaleWidth - Printer.TextWidth(Label1.Caption)

В переменной VertCoord мы будем накапливать сдвиг по вертикали. Сначала запишем в нее, сколько высоты заняла первая строка (вместе с отступами).

VertCoord = Printer.TextHeight(Label1.Caption)

Теперь встраиваем в страницу первую строчку. Никакой печати при этом не происходит. Собственно принтер начинаеn работать только тогда, когда VB посчитает, что формирование документа закончено. Это произойдет, если будет написан оператор Printer.EndDoc, либо если ты выйдешь из программы совсем.

Printer.Print Label1.Caption

'устанавливаем большой размер шрифта для названия модели телефона
Printer.FontSize = 20
'отменяем подчеркивание
Printer.Font.Underline = False
'зато ставим жирный
Printer.Font.Bold = True
'делаем отступ вниз на 1,5 см от первой строчки, меняя вертикальную координату
Printer.CurrentY = VertCoord + 1.5
'выравниваем вторую строчку по-середине
Printer.CurrentX = (Printer.ScaleWidth - Printer.TextWidth(Label9(1).Caption & "Лидер продаж - телефон ")) / 2
'добавляем, сколько высоты мы использовали на вторую строчку вместе с отступами
VertCoord = VertCoord + Printer.TextHeight(Label9(1).Caption) + 1.5
'встраиваем в страницу вторую строчку
Printer.Print "Лидер продаж - телефон " & Label9(1).Caption
'устанавливаем размер шрифта поменьше для заглавия технических характеристик телефона
Printer.FontSize = 14
'добавляем курсив
Printer.Font.Italic = True
'отступаем вниз еще на 2 см
Printer.CurrentY = VertCoord + 2
'добавляем высоту третьей строчки вместе с отступами
VertCoord = VertCoord + Printer.TextHeight(Label4.Caption) + 2
'оставляем 8 см слева под картинку, а остальное справа - под текст
'строчку центрируем на ее Printer.ScaleWidth = 8 см
Printer.CurrentX = (Printer.ScaleWidth - 8 - Printer.TextWidth(Label4.Caption)) / 2
'встраиваем в страницу третью строчку
Printer.Print Label4.Caption

Для печати изображения телефона берем текущую координату по вертикали VertCoord.
Печатаем картинку, центрируя ее на своей площади и меняя пустое расстояние перед ней, в зависимости от того, горизонтальная она (OrientTelefona = 0.5) или вертикальная (OrientTelefona = 2.25). Тогда картинка будет располагаться красиво, по центру выделенной ей площади.

If Image1.Height > Image1.Width Then
OrientTelefona = 2.25
Else
OrientTelefona = 0.5
End If

Встраиваем картинку в наш документ

Printer.PaintPicture Image1.Picture, Printer.ScaleWidth - ScaleX(Image1.Width, vbPixels, vbCentimeters) - OrientTelefona, VertCoord

'отменяем курсив и жирность
Printer.Font.Italic = False
Printer.Font.Bold = False
'устанавливаем размер шрифта еще меньше
Printer.FontSize = 12
'отступаем вниз от 3-ей строки на 1 см
Printer.CurrentY = VertCoord + 1
VertCoord = VertCoord + 1

Теперь, остальные строки - технические данные( с 2-ой по 11-ую) мы встроим в цикле (всего 10 строк) не меняя размеров, жирности и т.п. шрифта. Обратите внимание, что наименование характеристи в Label8(X) и сами характеристики в Label9(X) прописаны отдельно друг от друга и перед каждым установлены свои координаты начала печати Printer.CurrentX и Printer.CurrentY. Тогда печать осуществится красиво, двумя столбиками.

For X = 2 To 11
'отступаем слева по сантиметру
Printer.CurrentX = 1
Printer.CurrentY = VertCoord
Printer.Print Label8(X).Caption

Printer.CurrentX = 5
Printer.CurrentY = VertCoord
Printer.Print Label9(X).Caption
VertCoord = VertCoord + 0.6 'увеличение вертикальной координаты с каждой новой строчкой
Next X

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

VertCoord = VertCoord + 1
Printer.Line (1, VertCoord)-(17.5, VertCoord + 0.7), vbBlack, BF

Printer.CurrentY = VertCoord + 0.1
Printer.CurrentX = 6
'теперь пишем цену
Printer.Font.Bold = True
Printer.Print "Самая лучшая цена: " & Label9(12).Caption & " руб."
Printer.Font.Bold = False
А вот теперь даем команду о завершении формирования страницы и принтер начинает печать:
Printer.EndDoc
End Sub

Но не все хорошо в этом примере. Строка, которая выводит особенности телефона (Label9(8)) длинная и не входит в рамки страницы, а при печати все, что не влезает в страницу безжалостно отсекается и на печать не выводится. Та же проблема возникает при печати неформатированного текста из текстового файла. Попробуйте решить эту проблему самостоятельно. Возможно позже я по этому поводу чего-нибудь напишу, но не здесь, а в Части 3 - "Релизация некоторых задач"

Разбивать текст можно по пробелам. Возможно, ты захочешь использовать функцию InStr.

Функция InStr.

Эта функция используется при текстовом поиске. Она возвращает значение (если совпадение обнаружено)- позицию (номер символа), с которого искомая строка входит в строку, в которой выполняется поиск. Если поиск не удался или строка, в которой проводится поиск - пустая, возвращаемое значение - 0. Ее синтаксис следующий

Числовая переменная=InStr(номер символа, строка поиска, критерий поиска, способ сравнения)

номер символа - необязательный аргумент определяет с какого символа в строке (третьего, десятого и т.п.) надо начинать поиск. Если не задан, поиск начинается с первого символа.
строка поиска - строка, в которой ведется поиск
критерий поиска - искомое строковое выражение
способ сравнения - необязательно. Указывает способ сравнения строк (0-двоичное сравнение, 1 -посимвольное сравнение без учета регистра, 2 - только в Microsoft Access). Если аргумент опущен, способ сранения определяется параметром инструкции Option Compare.

Вот пример поиска фамилии Иванов по по критерию из четырех начальных букв "иван" в массиве A(4) без учета регистра.

Dim A(4) As
  String
Dim X As Long
A(1) = "Петров"
A(2) = "Сидоров"
A(3) = "Иванов"
A(4) = "Глюкоза"
For X = 1 To 4
If InStr(1, A(X), "иван", 1) > 0 Then
MsgBox "Найден " & A(X) & " в строке " & X
End If
Next X

Удачи!


Visual Basic с нуля. Глава 14. События мыши. Работа с мышью. Рисование фигур программными методами.

Глава 14. События мыши. Работа с мышью. Рисование фигур программными методами. Методы Line и Circle.
Скачать исходник программы Mouse
 Дата создания 08.02.2005 {Автор 4us}

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

События.

Прежде всего хочу сказать, что ранее, при создании процедуры объекта мы довольствовались тем событием, которое нам предоставлял VB по умолчанию. Например для формы - это загрузка Form1_Load(), для кнопки - щелчок мыши Command1_Click и так далее. Однако для почти каждого объекта есть довольно-таки большой список и других доступных событий, которые можно посмотреть в правом поле вверху окна кода. То есть, если мы создали процедуру для формы, реагирующую на загрузку формы Form_Load, то щелкнув по полю, где находится надпись Load, мы в выпавшем списке видим еще массу событий и с каждым мы можем создать свою процедуру, например Form_Click, или там Form_MouseMove.
По-сути событие - это машинное прерывание, вызванное пользователем, другой программой или системой или еще чем-нибудь. Таким образом, выполнение процедуры для объекта начинается когда наступает событие этой процедуры.
Почему мы программный код обычно начинаем с процедуры Form_Load? Потому-что при загрузке программы наша форма автоматически загружается, и хочешь-нехочешь, наступает событие Load, начинается выполнение процедуры Form_Load, в которой поэтому удобно выполнять предварительные действия по подготовке программы к работе: загрузка служебных файлов, установка свойств объектов, присвоение начальных значений переменным и т.п. А уж далее, либо из этой процедуры происходит переход к другим процедурам, либо программа ожидает наступления других событий для объектов, под которые должны быть написаны свои процедуры. Те же процедуры, которые мы писали сами, обзывали, как хотели, не будут выполнятся никогда, пока мы конкретно не вызовем их по имени. Эти процедуры ни с объектом, ни с событием никак не связаны.

Теперь перейдем к тем событиям, которые вызываются гнусным воздействием юзера на мышь.

Private Sub объект_MouseMove ([индекс As Integer,] клавиша As Integer, shift As Integer, x As Single, y As Single) | Возникает при перемешении мыши над объектом. По значению необязательной переменной индекс можно отследить, над каким элементом массива объектов Control Array (если массив существует) происходит перемещение. Переменная клавиша указывает состояние всех клавиш мыши, а не указывает на какую-нибудь одну, как при событиях _MouseDown или _MouseUp. Поэтому значения клавиш (1-левая, 2 - правая, 4 - средняя, 0 - никакая) складываются (1+2=3 нажаты левая и правая клавиши мыши одновременно). Переменная shift возвращает состояние кнопок shift, alt и ctrl в момент нажатия клавиш мыши (1- shift, 2- alt и 4 - ctrl). При удержании больше одной кнопки их значения складываются (ctrl-alt = 4+2=6). Значения переменных x и y возвращают текущие координаты курсора мыши (x - по-горизонтали, y - по-вертикали) в единицах измерения, заданных свойствами ScaleHeight, ScaleWidth.
Private Sub объект_MouseDown ([индекс As Integer,] клавиша As Integer, shift As Integer, x As Single, y As Single) и Private Sub объект_MouseUp ([индекс As Integer,] клавиша As Integer, shift As Integer, x As Single, y As Single) | Возникает при нажатии (_MouseDown) или отпускании (_MouseUp) клавиши мыши, при нахождении курсора мыши над объектом. При этом по значению необязательной переменной индекс можно отследить, над каким элементом массива объектов Control Array (если массив существует) нажата клавиша. Также позволяет отследить, какая клавиша мыши нажата (не показывает состояние всех клавиш одновременно, как событие _MouseMove), по состоянию переменной кнопка (1- левая клавиша, 2 - правая клавиша, 4 - средняя клавиша). Переменная shift возвращает состояние кнопок shift, alt и ctrl в момент нажатия клавиш мыши (1- shift, 2- alt и 4 - ctrl). При удержании больше одной кнопки их значения складываются (ctrl-alt = 4+2=6). Значения переменных x и y возвращают текущие координаты курсора мыши (x - по-горизонтали, y - по-вертикали) в единицах измерения, заданных свойствами ScaleHeight, ScaleWidth.
Private Sub объект_Click() | Возникает при щелчке левой или правой клавиши мыши, при нахождении курсора мыши над объектом. Объекты CheckBox, CommandButton, Listbox или OptionButton реагируют только на щелчок левой клавиши. Кроме того, если у нас существует массив объектов Control Array (см. Главу 13), то введя переменную индекс в объявление процедуры, можно определить, по какому элементу массива щелкнула мышь. В этом случае объявлять процедуру надо так:
Private Sub объект_Click (индекс As Integer). | Индекс и возвратит номер элемента массива. Вот пример кода, когда на форме имеется массив Control Array из трех текстбоксов Text1(0), Text1(1) и Text1(2) и текстбокс Text2. При щелчке мышью по одному из элементов Text1 в текстбокс Text2 будет выводится индекс элемента Control Array. Private Sub Text1_Click(Nomer As Integer) Text2.Text = "Щелкнуто по номеру" & " " & Nomer End Sub При возникновении этого события порождаются события MouseUp и MouseDown. Чтобы различать нажатия клавиш, надо использовать события MouseUp и MouseDown.
Private Sub объект_DblClick() | Возникает при двойном щелчке клавиши мыши, при нахождении курсора мыши над объектом.
Private Sub объект_DblClick (индекс As Integer). | Аналогичен событию _Click.

Тут надо помнить один очень важный момент. Порядок наступления событий различен для различных элементов. Нужно следить, чтобы события (время их наступления) не противоречили друг другу. Если написать две процедуры, одну на событие _Click, а вторую - на _DblClick в надежде, что они будут выполнятся в зависимости от наступившего события, то процедура _DblClick не будет выполнятся никогда, поскольку событие _Click наступит раньше, начнется выполнение ее процедуры и на _DblClick можно будет наплевать.

Рассмотрим самое мощное событие - _MouseMove на примере (поскольку остальные события используются аналогично) программки Mouse:
сделаем новый exe-проект и для наших опытов в верхней части формы создадим массив из пяти TextBox'ов Text1(0), Text1(1), Text1(2), Text1(3), Text1(4), и положим один PictureBox. (Рис. 25).

Рисунок 25.

А в нижней части формы расположим шесть лейблов. В первый будем записывать, с каким объектом работает мышь, во второй - состояние клавиш мыши, в третий - состояние кнопок SHIFT-CTRL-ALT, в четвертый и пятый - координаты мыши, а в шестой -номер элемента массива TextBox'ов, с которым работает мышь.

'Объявим пару переменных для записи пометок в лейблах
Option Explicit
Dim ValButton As String
Dim ValShift As String

'и создадим процедуру для нашей формы с событием _MouseMove
  и напишем код:
Private Sub Form_MouseMove(Button As
  Integer, Shift As Integer, X As
  Single, Y As Single)
Label1.Caption = "Мышь работает с формой"
'в зависимости от значения Button определяем, какая клавиша
  мыши нажата
If Button = 0 Then
ValButton = " Ни одной клавиши мыши не нажато"
ElseIf Button = 1 Then
ValButton = " Нажата левая клавиша мыши"
ElseIf Button = 2 Then
ValButton = " Нажата правая клавиша мыши"
ElseIf Button = 4 Then
ValButton = " Нажата средняя клавиша мыши"
ElseIf Button = 3 Then
ValButton = " Нажаты левая и правая клавиши мыши"
ElseIf Button = 7 Then
ValButton = " Нажаты все три клавиши мыши"
End If
Label2.Caption = "Значение Button= " & Button & ValButton

'в зависимости от значения Shift определяем, какая комбинация из кнопок SHIFT-CTRL-ALT нажата
If Shift = 0 Then
ValShift = " Ни одна из кнопок не нажата"
ElseIf Shift = 1 Then
ValShift = " Нажата кнопка SHIFT"
ElseIf Shift = 2 Then
ValShift = " Нажата кнопка CTRL"
ElseIf Shift = 4 Then
ValShift = " Нажата кнопка ALT"
ElseIf Shift = 3 Then
ValShift = " Нажаты кнопки SHIFT и CTRL"
ElseIf Shift = 6 Then
ValShift = " Нажаты кнопки ALT и CTRL"
ElseIf Shift = 5 Then
ValShift = " Нажаты кнопки SHIFT и ALT"
ElseIf Shift = 7 Then
ValShift = " Нажаты все три кнопки SHIFT-CTRL-ALT"
End If
Label3.Caption = "Общее состояние кнопок SHIFT-CTRL-ALT - " & Shift & " ;" & ValShift
'выводим координаты мыши
Label4.Caption = "Координата x= " & X & " твипов"
Label5.Caption = "Координата y= " & Y & " твипов"
Label6.Caption = "Массива из форм Control Array быть не может"
End Sub

Обратите внимание, что все кнопки и клавиши отслеживаются только тогда, когда мышь движется. Ждать результатов на неподвижной мыши бесполезно.
Теперь, если мы запустим программу, то увидим, что все нажатия кнопок и клавиш, а также изменение координат происходит только тогда, когда курсор мыши находтся над формой. Как только мы перемещаемся на любой объект, TextBox или PictureBox, видимых изменений не происходит. Поскольку мы написали процедуру для формы, над другими объектами она не работает. Кроме того, координаты возвращаются внутри именно того объекта, над которым находится мышь. Т.е., если мышь находится над PictureBox'ом, значит и координаты будут PictureBox'а, а не формы Form1.
Для того, чтобы наша программка работала над всеми объектами, создадим аналогичные процедуры для объектов PictureBox и массива TextBox'ов Text1:

Private Sub Picture1_MouseMove(Button
  As Integer, Shift As Integer,
  X As Single, Y As Single)
Label1.Caption = "Мышь работает с PictureBox"
If Button = 0 Then
ValButton = " Ни одной клавиши мыши не нажато"
ElseIf Button = 1 Then
ValButton = " Нажата левая клавиша мыши"
ElseIf Button = 2 Then
ValButton = " Нажата правая клавиша мыши"
ElseIf Button = 4 Then
ValButton = " Нажата средняя клавиша мыши"
ElseIf Button = 3 Then
ValButton = " Нажаты левая и правая клавиши мыши"
ElseIf Button = 7 Then
ValButton = " Нажаты все три клавиши мыши"
End If

Label2.Caption = "Значение Button= " & Button & ValButton

If Shift = 0 Then
ValShift = " Ни одна из кнопок не нажата"
ElseIf Shift = 1 Then
ValShift = " Нажата кнопка SHIFT"
ElseIf Shift = 2 Then
ValShift = " Нажата кнопка CTRL"
ElseIf Shift = 4 Then
ValShift = " Нажата кнопка ALT"
ElseIf Shift = 3 Then
ValShift = " Нажаты кнопки SHIFT и CTRL"
ElseIf Shift = 6 Then
ValShift = " Нажаты кнопки ALT и CTRL"
ElseIf Shift = 5 Then
ValShift = " Нажаты кнопки SHIFT и ALT"
ElseIf Shift = 7 Then
ValShift = " Нажаты все три кнопки SHIFT-CTRL-ALT"
End If
Label3.Caption = "Общее состояние кнопок SHIFT-CTRL-ALT - " &
  Shift & " ;" & ValShift
Label4.Caption = "Координата x= " & X & " твипов"
Label5.Caption = "Координата y= " & Y & " твипов"
Label6.Caption = "Массива из PictureBox'ов сейчас нет"
End Sub

Private Sub Text1_MouseMove(Index As
  Integer, Button As Integer, Shift As
  Integer, X As Single, Y As
  Single)
Label1.Caption = "Мышь работает с массивом TextBox'ов"
If Button = 0 Then
ValButton = " Ни одной клавиши мыши не нажато"
ElseIf Button = 1 Then
ValButton = " Нажата левая клавиша мыши"
ElseIf Button = 2 Then
ValButton = " Нажата правая клавиша мыши"
ElseIf Button = 4 Then
ValButton = " Нажата средняя клавиша мыши"
ElseIf Button = 3 Then
ValButton = " Нажаты левая и правая клавиши мыши"
ElseIf Button = 7 Then
ValButton = " Нажаты все три клавиши мыши"
End If

Label2.Caption = "Значение Button= " & Button & ValButton

If Shift = 0 Then
ValShift = " Ни одна из кнопок не нажата"
ElseIf Shift = 1 Then
ValShift = " Нажата кнопка SHIFT"
ElseIf Shift = 2 Then
ValShift = " Нажата кнопка CTRL"
ElseIf Shift = 4 Then
ValShift = " Нажата кнопка ALT"
ElseIf Shift = 3 Then
ValShift = " Нажаты кнопки SHIFT и CTRL"
ElseIf Shift = 6 Then
ValShift = " Нажаты кнопки ALT и CTRL"
ElseIf Shift = 5 Then
ValShift = " Нажаты кнопки SHIFT и ALT"
ElseIf Shift = 7 Then
ValShift = " Нажаты все три кнопки SHIFT-CTRL-ALT"
End If
Label3.Caption = "Общее состояние кнопок SHIFT-CTRL-ALT - " &
  Shift & " ;" & ValShift
Label4.Caption = "Координата x= " & X & " твипов"
Label5.Caption = "Координата y= " & Y & " твипов"
Label6.Caption = "Мышь находится над " & "Text1(" &
  Index & ")"
End Sub

Конечно, все это можно написать короче, используя одну собственную процедуру - ведь код этих процедур почти одинаков, но сейчас так будет яснее.
Теперь нажатия и координаты отслеживаются над всеми объектами, кроме лейблов. Для устранения этого, мы можем в их свойствах поставить значение свойства .Enabled в окне свойств в положение False. Тогда при нахождении курсора мыши над лейблами будут показываться нажатия кнопок и координаты мыши при событии _MouseMove, наступающем для формы. Однако текст в лейблах изменится на серенький.
Скачать исходник программы Mouse можно вверху страницы.

Рисование фигур программными методами.

Теперь я скажу, что рисовать такие фигуры, как круг, овал, прямоугольник, линия можно не только с помощью объекта Shape, но и программно, путем написания операторов. Это очень просто.

Прямоугольник, линия - метод Line.
Для рисования на объекте линии или прямоугольника существует метод Line. Синтаксис его такой

объект.Line [Step] (x1, y1) [Step] (x2, y2), [color], [B][F]
Где

объект | это объект на котором рисуется фигура. Если не задан, то используется объект на котором в этот момент установлен фокус.
Step | необязательное ключевое слово, устанавливает, что начальная точка прямоугольника - верхний левый угол, (или линии) задается не по абсолютными координатами x1, y1, а относительно текущих координат, определяемых значениями свойств CurrentX и CurrentY.
x1, y1 | координаты начальной точки прямоугольника (верхний левый угол) или линии (единицы измерения определяются свойством ScaleMode).
Step | необязательное ключевое слово, устанавливает, что конечная точка прямоугольника - нижний правый угол, (или линии) задается не по абсолютыми координатами x2, y2, а относительно координат начальной точки прямоугольника x1,y1.
x2, y2 | координаты конечной точки прямоугольника (нижний правый угол) или линии (единицы измерения определяются свойством ScaleMode).
color | определяет цвет линии или прямоугольника. Может задаваться как RGB - в формате шестнадцатиричного числа VB (&HFF0033), либо цветом, определяемым ключевыми словами Visual Basic (vbRed). Можно использовать функции RGB или QBColor. Если не задан, цвет устанавливается тот, что задан свойством .ForeColor.
B | определяет, что будет прямоугольник. Если его опустить, рисоваться будет линия.
F | определяет, что прямоугольник будет закрашен тем же цветом, что и его границы (заливка). Используется с опцией B.

Таким образом, чтобы нарисовать линию, например, в объекте Picture1, можно использовать такой код:

Private Sub Form_Load()
'задаем единицы измерения пиксели
Picture1.ScaleMode = vbPixels
'устанавливаем свойство AutoRedraw в True, а то нашего рисования видно не будет
Picture1.AutoRedraw = True
'ширину лини устанавливаем в 3 пикселя
Picture1.DrawWidth = 3
'ставим цвет в свойстве ForeColor в фиолетовый
Picture1.ForeColor = &HFF00FF
'рисум линию с начальной координатой X1=10, Y1=10 и конечной X2=150, Y2=150 пикселей
'поскольку цвет не задаем, линия будет фиолетовой по свойству ForeColor
Picture1.Line Step(10, 10)-(150, 150)
End Sub

Если мы в оператор Picture.Line добавим B, то у нас получится прямоугольник (в общем-то квадрат)
Picture1.Line (10, 10)-(150, 150), , B
Чтобы изменить цвет линий, например, на красный, независимо от .ForeColor:
Picture1.Line (10, 10)-(150, 150), vbRed, B
И наконец можем залить наш квадратик
Picture1.Line (10, 10)-(150, 150), vbRed, BF

Ключевое слово Step можно использовать, если нужно привязаться к постоянно меняющимся значениям свойств .CurrentX и .CurrentY. Так, например, мы можем нарисовать наш квадратик там, куда щелкнем мышкой.

Private Sub Form_Load()
Picture1.ScaleMode = vbPixels
Picture1.AutoRedraw = True
Picture1.DrawWidth = 3
Picture1.ForeColor = &HFF00FF
End Sub

Private Sub Picture1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
Picture1.Cls 'очищаем Picture1 перед каждым кликом
Picture1.CurrentX = X
Picture1.CurrentY = Y
Picture1.Line Step(0, 0)-Step(150, 150), vbRed, B
End Sub

Окружность, эллипс, дуга - метод Circle.
Для рисования на объекте окружности, эллипса или дуги применяется метод Circle. Его синтаксис:

объект.Circle [Step] (x, y), радиус, [color, start, end, aspect],
Где

объект | это объект на котором рисуется фигура. Если не задан, то используется объект на котором в этот момент установлен фокус.
Step | необязательное ключевое слово, устанавливает, что центр окружности задается не по абсолютным координатам x, y, а относительно текущих координат, определяемых значениями свойств CurrentX и CurrentY.
x, y | координаты центра окружности (единицы измерения определяются свойством ScaleMode)
радиус | радиус окружности, эллипса ил дуги (единицы измерения определяются свойством ScaleMode).
color | определяет цвет окружности, эллипса или дуги. Может задаваться как RGB - в формате шестнадцатиричного числа VB (&HFF0033), либо цветом, определяемым ключевыми словами Visual Basic (vbRed). Можно использовать функции RGB или QBColor. Если не задан, цвет устанавливается тот, что задан свойством .ForeColor.
start, end | при создании дуги задают начальное и конечное значение угла дуги в радианах (от -2pi до 2pi)
aspect | коэффициент сжатия эллипса. По умолчанию равен 1,0, т.е. сжатия нет, выводится окружность.

Ну-с, опять в нашем Picture1 рисуем окружность. Все происходит аналогично методу .Line

Private Sub Form_Load()
Picture1.ScaleMode = vbPixels
Picture1.AutoRedraw = True
Picture1.DrawWidth = 3
Picture1.ForeColor = &HFF00FF
Picture1.Circle (200, 120), 70
End Sub

Мы нарисовали фиолетную окружность с центром в точке X=200, Y=120 и и диаметром 70 пикселей.
Теперь превратим ее в эллипс синего (&HFF0000) цвета. Коэффициент aspect установим в 0,5, т.е сожмем окружность в 2 раза.
Picture1.Circle (200, 120), 70, &HFF0000, , , 0.5
Попробуем теперь нарисовать дугу окружности. Коэффициент эллипcности убираем (дуга эллипса - некрасиво). Цвет определим зеленый (&H8000&)
Picture1.Circle (200, 120), 70, &H8000&, 0, 1

А вот пример маленького кода, где мы можем использовать "мышиные" события и рисование фигур. Эта программка рисует окружность, причем радиус и положение задается нажатием клавиши мыши. Когда клавиша отпускается, окружность фиксируется.

Dim OldX As Single
Dim OldY As Single
Private Sub Form_Load()
Picture1.ScaleMode = vbPixels
Picture1.AutoRedraw = True
Picture1.DrawWidth = 3
Picture1.ForeColor = &H8000&
End Sub
Private Sub Picture1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
OldX = X
OldY = Y
End Sub

Private Sub Picture1_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
Picture1.CurrentX = X
Picture1.CurrentY = Y
If Button = 1 Then
Picture1.Cls
Picture1.Circle Step(OldX, OldY), Sqr((X - OldX) ^ 2 + (Y - OldY) ^ 2), &H8000&
Center = Sqr((X - OldX) ^ 2 + (Y - OldY) ^ 2)
End If
End Sub

Copyright © 2005 4us


Visual Basic с нуля. Глава 15. ActiveX: Объект общего диалога СommonDialog.

Глава 15. ActiveX: Объект общего диалога СommonDialog. Стандартные окна Open, Save as, Font, Color, и Print.
Скачать исходник программы StandWin
 Дата создания 23.02.2005 {Автор 4us}

Практически каждая программа должна дать возможность пользователю загружать и сохранять файлы. Конечно, для этих целей можно использовать объекты Drive, Dir и File, об их использовании мы уже говорили в Главе 6. Однако, если ты заметил, большинство программ используют стандартные виндусовые окна, придающие программному продукту солидный вид. Имеем такую возможность и мы. Использование стандартных диалоговых окон проще и удобнее с точки зрения написания кода. Кроме того они позволяют пользователю выбирать цвет, шрифт, вводить свои установки для принтера и использовать Help (последнее мы опустим, так это сейчас не актуально).

Стандартные окна общего диалога CommonDialog

В Главе 12 мы уже использовали два элемента ActiveX - ProgressBar и Slider, добавив в панель инструментов средство Microsoft Windows Common Controls 6.0. Сейчас то же самое надо сделать с элементом ActiveX - Microsoft Common Dialog Control 6.0. Если ты не помнишь, как это делать, посмотри Главу 12, там все подробно расписано. После того, как ты отметишь галочкой (Проект - Компоненты - Microsoft Common Dialog Control 6.0) этот компонент и щелкнешь OK, у тебя в панели инструментов появится новый значок "CommonDialog".
Этот компонент позволяет нам использовать отдельно стандартные окна Windows
- открытия файлов (Open) с помощью метода ShowOpen
- сохранения файлов (Save As) c помощью метода ShowSave
- менять установки принтера (Printer) c помощью метода ShowPrinter
- выбор шрифтов и стилей (Font) c помощью метода ShowFont
- выбор цвета из палитры (Color) c помощью метода ShowColor

Рассмотрим все окна на примере маленькой программки DialogWindows. Создадим новый exe-проект, положим на форму наш новый компонент CommonDialog1, текстбокс Text1 и пять командных кнопок Command, в каждой из которых будет процедура для своего стандартного окна. Мы будем работать с текстовыми файлами. Итак первое окно Open в процедуре кнопки Command1:

Диалоговое окно открытия файлов Open (метод ShowOpen)

Чтобы открыть окно для открытия файлов, мы в процедуре командной кнопки Command1 пишем одну единственную строчку:

CommonDialog1.ShowOpen

Раз и готово. Теперь, запуская программу и нажимая кнопку Command1, мы тем самым открываем диалоговое окно Open. И можем радостно шоркаться по дискам, папкам и файлам. Можно даже папку создать. Но, никакого файла это окно не откроет, сколько на OK не жми. Поскольку для работы окна нам надо написать программный код. Подумаем, какой программный код мы можем написать.
Во-первых, нам надо, чтобы окно называлось так как мы хотим, а не как хочет Bill Gates. Для того, чтобы поменять название диалогового окна на твое собственное, можно использовать свойство .DialogTitle приблизительно так:
CommonDialog1.DialogTitle = "Загрузить текстовой файл"
Во-вторых, сейчас наше диалоговое окно показывает все файлы. А поскольку юзер имеет привычку тыкать во все файлы без разбора, надо ему эту возможность ограничить. Чтобы юзер случайно не пытался загружать графический файл в текстовое поле или наоборот, надо заставить наше окно показывать только те файлы, которые может корректно загружать наша программа. Для этого у нашего объекта CommandDialog есть свойство .Filter, которое и позволяет нам показывать в диалоговом окне файлы, соответствующие шаблону. Поскольку работать мы будем с текстовыми файлами, то и открывать надо именно их. Текстовые файлы имеют раширение, к примеру, .txt, или dat. Поэтому перед открытием окна мы должны написать например такое:
CommonDialog1.Filter = "Текстовые файлы (*.txt)|*.txt"
Как видишь, опции фильтра - строковые данные, взятые в кавычки. Они разделены вертикальной черточкой. Слева от черты - то что выведется в окне, т.е. то, что будет видеть юзер в диалоговом окне, а справа - указан шаблон, по которому происходит фильтрация. Указать здесь можно, что угодно, вплоть до конкретного файла.
Теперь, допустим, мы хотим показать несколько типов файлов. Добавим, например, в наш фильтр файлы .dat. Тогда слева от вертикальной черты, дописываем как угодно информацию о новом формате, а справа, через точку с запятой дописываем новый шаблон:
CommonDialog1.Filter = "Текстовые файлы (*.txt) Файлы данных (*.dat)|*.txt;*.dat"
Теперь наше окно будет показывать оба типа файлов одновременно. И в выпадающем списке в диалоговом окне оба типа файлов будут на одной строчке.
А вот если мы хотим предоставить юзеру право выбора, какие файлы ему просматривать, .dat или .txt, и чтобы они находились на разных строчках в выпадающем окне, надо написать следующее:
CommonDialog1.Filter = "Текстовые файлы (*.txt)|*.txt|Файлы данных(*.dat)|*.dat"
Здесь мы пишем пояснение для первого типа файла, отделяем вертикальной чертой, пишем первый шаблон, снова отделяем вертикальной чертой, затем пишем пояснение для второго типа, отделяем вертикальной чертой, пишем второй шаблон. В это случае, наши типы файлов в диалоговом окне будут в выпадающем списке на разных строчках и пользователь сможет выбрать, какие файлы будет показывать диалоговое окно, .dat или .txt.
В-третьих, нам надо передать полный путь и имя файла в инструкцию Open (хочешь не хочешь, а файл придется открывать обычным оператором Open). Нет ничего проще. Eсть свойство .FileName, которое принимает значение полного пути + имя файла, то есть все, что нужно для оператора Open:
Open CommonDialog1.FileName For Input As #F
Далее организуем цикл Do...Loop (объявив необходимые переменные) и считываем тeкстовой файл в Text1. Вот что у нас должно приблизительно получиться в итоге:

Option Explicit
Private Sub Command1_Click()
Dim F As Long
Dim Stroka As String
Text1.Text = ""
CommonDialog1.Filter = "Текстовые файлы (*.txt) Файлы данных (*.dat)|*.txt;*.dat"
CommonDialog1.ShowOpen
F = FreeFile
Text2.Text = CommonDialog1.FileName
Open CommonDialog1.FileName For Input As #F
Do While Not EOF(F)
Line Input #F, Stroka
Text1.Text = Text1.Text & Stroka
Loop
Close #F
End Sub

Радостно потирая руки запускаем нашу фигню. Вроде все работает. Однако давайте внимательно проанализируем код и работу диалогового окна.
И в результате исследования мы прийдем к неутешительному выводу: наша программа корректно работает только в одном-единственном случае - когда файл выбран (поле File Name (Имя файла) - не пустое) и юзер нажал кнопку Open (то же самое, что и при двойном щелчке по имени файла). То есть, когда все случилось удачно, путь и имя файла есть, после закрытия окна они передаются в оператор Open и все происходит как по маслу.

Что же происходит в менее благополучных случаях:
- юзер файл не выбрал, ну забыл, (поле File Name (Имя файла) - пустое), но тем не менее жмакает кнопку Open. Тогда диалоговое окно просто не закрывается, а ждет чего-нибудь более рационального. Наша программа ни взад, ни вперед. Тогда юзер плюет на открытие файла, решает, что лучше написать в Text1 новый текст, чем грузить старый файл (тем более, что не задалось) и естественно жмет кнопку Cansel. А поскольку у нас нет имени файла, диалоговое окно передает в оператор Open пустую строку. Такой файл Open открыть не может. Возникает ошибка "Run-time error 75. Ошибка доступа пути или файла". Фатал эрор. Такого быть не должно.

Поэтому нам надо первым делом отследить нажатие пользователем кнопки Cansel. Для этого у нашего объекта CommonDialog есть свойство .CanselError. Его можно устанавливать и в окне свойств, но мы сделаем это программно. Работает оно так. Если значение свойства .CanselError равно True, то при щелчке мышью по кнопке Cansel в диалоговом окне всегда возникает ошибка. По умолчанию свойство .СanselError=False, т.е ошибка не возникает. То есть, иными словами, если мы поставим свойство .CanselError в состояние True, то показателем того, что юзер нажал кнопку Cansel является именно возникновение ошибки. Нам остается ее только отследить и грамотно обработать.
Без установки и обработки этой ошибки отследить нажатие кнопок Cansel и OK довольно затруднительно. Правда иногда в примерах или если программой пользуется сам разработчик, который заведомо не будет хулиганить и нажимать все кнопки правильно, для упрощения кода применяется такой прием. В большинстве случаев, при нормальном открытии файлов свойство .Flags возвращает значение 1024, а при нажатии кнопки Cansel - 0. Исходя из этой сомнительной посылки, можно взять операторы открытия файла в такой оператор If:

If CommonDialog1.Flags = 1024 Then
Операторы открытия и считывания файла
End If

Тогда ошибок открытия файла не возникнет и в принципе все будет работать, кроме отдельных случаев, типа, когда наш предприимчивый юзер не удовлетворился выбранным именем файла и начал редактировать это имя в поле File Name. В результате этого может получится так, что в поле File Name будет записано имя несуществующего файла. Тогда свойство .Flags хоть и возвратит число 1024, но оператор Open открыть несуществующий файл не сможет, и все равно возникнет ошибка. Да мало-ли чего может натворить юзер. Поэтому здесь мы этот рабочий прием использовать не будем.
Вообще свойство .Flags - очень большое и важное свойство. С его помощью можно управлять внешним видом окна, его поведением и собственно работой. Для управления работой разных диалоговых окон существует множество констант, каждая из которых задает окну какое-либо свойство. Эти константы можно посмотреть в меню VB - кнопка "Проводник объектов". Константы могут быть использованы либо в виде константы (что более понятно в коде), либо как число - десятичное или шестнадцатеричное. Если надо использовать несколько констант, то они записываются в одном операторе через логическое ИЛИ - Or. Например, чтобы диалоговое окно контролировало, выбрал ли юзер файл из существующих и одновременно контролировало правильность пути до файла, надо написать следующее:
CommonDialog1.Flags = cdlOFNFileMustExist Or cdlOFNPathMustExist
Все константы мы перебирать не будем, а будем говорить лишь о самых необходимых в нашем нелегком деле. Вот некоторые, применительно к окнам Open и Save As.
cdlCancel = 32755 (&H7FF3) - выбрана кнопка Cansel
cdlInvalidFileName = 20477 (&H4FFD) - имя файла неправильно
cdlOFNFileMustExist = 4096 (&H1000) - позволяет выбирать файлы только из существующих
cdlOFNNoChangeDir = 8 - устанавливает текущую директорию для последующих вызовов
cdlOFNOverwritePrompt = 2 - спрашивает пользователя, если файл уже существует
cdlOFNPathMustExist = 2048 (&H800) - юзер может вводить только правильные пути для файла

Теперь мы можем написать правильный код для окна открытия и считывания текстовых файлов.

Option Explicit
'Объявим переменную для номера открываемого файла
Dim F As Long
Private Sub Command1_Click()
'объявим переменную, в которую мы будем считавать строки из файла
Dim Stroka As String
'напишем оператор для отслеживания ошибки и в случае возникновения переходим по метке Cansel
On Error GoTo Cansel
'установим, чтобы возникала ошибка при щелчке по кнопке Cansel
CommonDialog1.CancelError = True
'установим фильтры для текстовых файлов
CommonDialog1.Filter = "Текстовые файлы (*.txt) Файлы данных (*.dat)|*.txt;*.dat"
'установим свойство .Flags, которое не позволяет открывать несуществующие файлы
CommonDialog1.Flags = cdlOFNFileMustExist
'напишем наше собственное название диалогового окна
CommonDialog1.DialogTitle = "Загрузить текстовой файл"
'а вот теперь окно можно и открыть
CommonDialog1.ShowOpen

'открываем файл и считываем его содержимое в Text1
F = FreeFile
Text1.Text = ""
Open CommonDialog1.FileName For Input As #F
Do While Not EOF(F)
Line Input #F, Stroka
Text1.Text = Text1.Text & Stroka
Loop
Close #F
'файл считан
'выход из процедуры до ее завершения, чтобы не обрабатывалась ошибка, если ее нет
Exit Sub

Cansel:
'анализируем номер ошибки. Если нажата Cansel, выходим из процедуры
If Err.Number = cdlCancel Then
Exit Sub
Else
'в противном случае, если возникла какая-то другая ошибка, выводим ее описание
MsgBox Err.Description
End If
' заканчиваем процедуру
End Sub

Ну вот, мы сделали это. А теперь с нашими наработками справиться с окном сохранения файлов очень просто.

Диалоговое окно сохранения файлов Save As (метод ShowSave)

В кнопке Command2 пишем код для сохранения текста в файле из Text1.
Код у нас будет совершенно такой же, как и для окна Open, ну конечно файл надо открывать для записи, а не для чтения. Наш прием для отслеживания кнопки Cansel мы будем использовать во всех окнах. Кроме того, поскольку мы записываем файл, то теперь нам нет необходимости выбирать файл из существующих. Зато есть необходимость предупреждать юзера, что он можеть записать новый файл, уничтожив старый (при совпадении их имен). Для предотвращения этого ужаса присвоим свойству .Flags такую константу
CommonDialog1.Flags = cdlOFNOverwritePrompt
Тогда при попытке записать файл под уже существующим именем, Windows будет выводить предупреждающее окно. И наш код будет выглядеть следующим образом:

Private Sub Command2_Click()
F = FreeFile
On Error GoTo Cansel
CommonDialog1.CancelError = True
CommonDialog1.Filter = "Текстовые файлы (*.txt) Файлы данных (*.dat)|*.txt;*.dat"
CommonDialog1.Flags = cdlOFNOverwritePrompt
CommonDialog1.DialogTitle = "Сохранить текстовой файл как..."
CommonDialog1.ShowSave
Open CommonDialog1.FileName For Output As #F
Print #F, Text1.Text
Close #F
Exit Sub
Cansel1:
If Err.Number = cdlCancel Then
Exit Sub
Else
MsgBox Err.Description
End If
End Sub

Диалоговое окно выбора цвета Color (метод ShowColor)

Ну с этим окном все предельно просто. Мы просто открываем окно (используя приемы описанные выше). У CommonDialog есть свойство .Color, которое и определяет выбранный пользователем цвет. Нам надо только его передать какому-нибудь объекту нашего проекта и готово. Например, чтобы изменить цвет фона нашей формы пишем:
Form1.BackColor = CommonDialog1.Color
Конечно и здесь есть кое-какие значения свойства .Flags. Их всего четыре:
cdlCCFullOpen = 2 'выводит полное диалоговое окно с настройкой цветов
cdlCCPreventFullOpen = 4 'выводит часть окна с цветами по-умолчанию и делает недоступной кнопку Define Custom Colors (определение настраиваемых цветов)
cdlCCHelpButton = 8 'делает видимой кнопку Help
cdlCCRGBInit = 1 'выводит часть окна с цветами по-умолчанию но кнопка Define Custom Colors (определение настраиваемых цветов) доступна для расширения окна

Напишем код, который будет менять цвет фона в Text1:

Private Sub Command3_Click()
CommonDialog1.Flags = cdlCCRGBInit
On Error GoTo Cansel
CommonDialog1.CancelError = True
CommonDialog1.ShowColor
Text1.BackColor = CommonDialog1.Color
Exit Sub
Cansel:
If Err.Number = cdlCancel Then
Exit Sub
Else
MsgBox Err.Description
End If
End Sub

Хочу заметить, что поменять название этого окна, как мы делали с окнами Open и Save As с помощью свойства .DialogTitle не удасться.

Диалоговое окно изменения вида шрифта Font (метод ShowFont)

Одним оператором CommonDialog1.ShowFont, как мы делали раньше, открыть окно шрифтов не удастся. Это лишь вызовет ошибку 24574 - "No font exist" - "Нет существующих шрифтов". Это происходит потому, что для окна необходимо определить, какие шрифты ему показывать - экранные, принтерные или оба. Это определяют параметры свойства .Flags:

CommonDialog1.Flags=cdlCFScreenFonts 'экранные шрифты
CommonDialog1.Flags=cdlCFPrinterFonts 'принтерные шрифты
CommonDialog1.Flags=cdlCFBoth 'оба вида шрифтов
Кроме того полезны для использования следующие константы свойства .Flags:
cdlCFForceFontExist= 65536 (&H10000) - вызов сообщения об ошибке при выборе несуществующего шрифта или стиля
cdCFLimitSize = 8192 (&H2000) - ограничивает размер показываемых шрифтов с размера, заданного свойством .Min по размер, заданный свойством .Max.
cdlCFEffects = 256 (&H100) - отображает дополнительные параметры форматирования - подчеркивание, перечеркивание цвет.

Заданные в диалоговом окне параметры должны быть обработаны программой. Для этого имя шрифта, его размер, стиль, цвет передаются из CommonDialog1 соответствующими свойствами в свойства соответствующего объекта. В нашем примере мы будем менять шрифт нашего текста в Text1:

Private Sub Command4_Click()
On Error GoTo Cansel
CommonDialog1.CancelError = True
'Установим флаги экранного шрифта, дополнительных параметров форматирования
' и включим ограничение размера шрифтов
CommonDialog1.Flags = cdlCFScreenFonts Or cdlCFEffects Or cdlCFLimitSize
'установим вывод шрифтов размером от 10 до 16
CommonDialog1.Min = 10
CommonDialog1.Max = 16
'открываем окно
CommonDialog1.ShowFont
'передаем свойства окна свойствам Text1
Text1.Font.Name = CommonDialog1.FontName
Text1.Font.Size = CommonDialog1.FontSize
Text1.Font.Bold = CommonDialog1.FontBold
Text1.Font.Italic = CommonDialog1.FontItalic
Text1.Font.Strikethrough = CommonDialog1.FontStrikethru
Text1.Font.Underline = CommonDialog1.FontUnderline
Text1.ForeColor = CommonDialog1.Color
Exit Sub
Cansel:
If Err.Number = cdlCancel Then
Exit Sub
Else
MsgBox Err.Description
End If
End Sub

Диалоговое окно установки параметров принтера и печати Print (метод ShowPrinter)

Это окно открывается командой :
CommonDialog1.ShowPrinter
С помощью этого окна юзер может выбрать принтер, номера печатаемых страниц, а также число копий.
Некоторые полезные константы для свойства .Flags:

cdlPDPageNums = 2 - устанавливает или возвращает статус радиокнопки Pages (Делает ее доступной при использовании свойств .Min и .Max)
cdlPDCollate = 16 (&H10) - устанавливает или возвращает статус галочки Collate
cdlPDNoSelection = 4 - выключает радиокнопку Selection
cdlPDSelection = 1 - устанавливает или возвращает статус радиокнопки Selection
cdlPDAllPages = 0 - устанавливает или возвращает статус радиокнопки All Pages
cdlPDPrintSetup = 64 (&H40) - выводит на экран SetUp принтера раньше, чем диалоговое окно

Чтобы управлять ориентацией листа (горизонтальная печать или вертикальная) , надо явно передать константу ориентации из диалогового окна окна в объект Printer.
Printer.Orientation = CommonDialog1.Orientation

Если свойству .Flags задать значение cdlPDPageNums, а затем установить свойства .Min и .Max (Минимальное и максимальное количество печатаемых страниц, то в диалоговом окне станет доступно поле Pages. По умодчанию в него можно задать номера страниц с помощью свойств .FromPage (начальная страница печати) и .ToPage (конечная страница печати):

CommonDialog1.Flags = cdlPDPageNums
CommonDialog1.Min = 0 ' минимальное количество страниц
CommonDialog1.Max = 10 ' максимальное количество страниц
CommonDialog1.FromPage = 1 'начальная страница печати
CommonDialog1.ToPage = 5 'конечная страница печати
Можно также по умолчанию задать количество копий с помощью свойства .Copies:
CommonDialog1.Copies = 3

Вот приблизительно, как может выглядеть код для печати содержимого нашего Text1:

Private Sub Command5_Click()
On Error GoTo Cansel
CommonDialog1.CancelError = True
CommonDialog1.Flags = cdlPDPageNums Or cdlPDCollate Or cdlPDNoSelection Or cdlPDAllPages
CommonDialog1.Copies = 2 'количество копий по умолчанию
CommonDialog1.Min = 0
CommonDialog1.Max = 10
CommonDialog1.FromPage = 1
CommonDialog1.ToPage = 2
CommonDialog1.ShowPrinter

Printer.Orientation = CommonDialog1.Orientation 'ориентация листа
Printer.Copies = CommonDialog1.Copies 'количество копий
Printer.Print Text1.Text
Printer.EndDoc
Exit Sub
Cansel:
If Err.Number = cdlCancel Then
Exit Sub
Else
MsgBox Err.Description
End If
End Sub

А в принципе, с работа с принтером - дело непростое, попробуй, исходя из этого поэкспериментировать со свойством .Flags и прочими делами.

Вместо методов .ShowOpen, .ShowSave и т. д. можно использовать свойство .Action. Это свойство не соответствует по своим признакам свойствам, так как заставляет объект выполнять действие (открывать диалоговое окно). Таким образом, метод .ShowOpen аналогичен свойству .Action:

оператору CommonDialog1.ShowOpen соответствует оператор CommonDialog1.Action = 1
оператору CommonDialog1.ShowSave соответствует оператор CommonDialog1.Action = 2
оператору CommonDialog1.ShowColor соответствует оператор CommonDialog1.Action = 3
оператору CommonDialog1.ShowFont соответствует оператор CommonDialog1.Action = 4
оператору CommonDialog1.ShowPrinter соответствует оператор CommonDialog1.Action = 5

Скачать исходник примера можно вверху страницы.


Visual Basic с нуля. Глава 16. Стандартный модуль. Процедуры Function и Sub.

Глава . Стандартный модуль. Процедуры общего назначения типов Function (функция), Sub (подпрограмма). Ключевое слово ByVal.
Скачать исходник программы Proc
 Дата создания 08.03.2005 {Автор 4us}

Что такое стандартный модуль.

Обычно программа, состоит из более, чем одной форм. Часто возникает необходимость использовать содержимое переменной, полученной в одной форме для обработки в другой. Однако переменные, объявленные в форме в разеле General доступны только внутри этой же формы (контейнера). Для того, чтобы нужные переменные, подпрограммы, функции были доступны во всей программе, во всех формах, их объявляют в стандартных модулях.
Стандартный модуль в физическом смысле представляет собой файл с расширением .bas. Чтобы добавить стандартный модуль в exe-проект, надо в меню Проект (Project) щелкнуть по команде Добавить Модуль (Add Module) - рис. 26.

Рисунок 26.
После этого появиться окно добавления модуля рис.27

Рисунок 27.
в котором надо щелкнуть по кнопке Open (открыть). После этого в окне проекта вы увидите новую папочку Модули, в которой появиться наш первый модуль Модуль1. Хотя его имя, конечно, можно поменять в нижнем окне свойств Рис 28.

Рисунок 28.
После этого ты можешь щелкнуть по Модуль1 в окне проекта и перед тобой откроется окно модуля, такое же как и окно кода формы. В отличие от формы, модуль не имеет вида Объект, в него нельзя встраивать никакие другие объекты, так как он контейнером не является. В него лишь можно записывать код абсолютно также, как и в форму. В чем же радость от модулей, поскольку мы вроде бы пока обходились и без них.
Ну во-первых, как уже было сказано выше, объявленные в модуле переменные могут быть доступны во всем проекте, во всех его формах. Для этого переменная объявляется в разделе General с ключевым словом Public. Например
Public RazmerMassiva As Long
Такие переменные называются глобальными переменными и срок их жизни такой же, как и у всего проекта (т.е. пока программа не закрыта). При этом в формах объявлять эту переменную уже не надо.
Напоминаю: если не указывать тип переменной, то по умолчанию она будет типом Variant, и соответственно памяти под нее будет выделено по-максимуму.
Во-вторых, раз написанный модуль можно подключать к другим проектам. Это особенно актуально, когда ты пишешь процедуру, которую можно будет использовать в дальнейшем. Поэтому такие процедуры оформляются в виде функций или подпрограмм в модулях и могут быть вызваны из любого места любой твоей программы. Такие подпрограммы называются процедурами общего назначения. Эти процедуры не привязаны ни к каким событиям и работают только по вызову из программы. С их помощью можно создавать собственные функции и подпрограммы, которые расширяют возможности языка VB (последнее предложение само выскочило, наверно из учебника какого-нибудь).
В-третьих, разбитый на модули код гораздо удобней отлаживать и читать, поскольку программа делиться на логические блоки, а это деляет код программы яснее и понятнее.

Итак, мы можем создать в модуле три вида процедур:

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

Процедуры типа Function - функция.

Синтаксис процедуры Function следующий:

Function имя функции (аргументы) As тип функции
операторы, выполняющие вычисления
End Function

Как видишь, синтаксис функции напоминает процедуры обработки событий, с которыми мы работали ранее. Начинается все ключевым словом Function, затем пишется имя функции, которое мы сами придумываем, далее следуют аргументы (необязательно), которые функция будет обрабатывать и которые передаются при вызове функции из программы. Затем можно определить тип возвращаемого функцией значения (по умолчанию - Variant). Далее в теле функции следуют операторы, выполняющие действия. И затем функция закрывается оператором End Function.
Смысл работы с функциями точно такой же, как и со встроенными функциями Visual Basic. Тебе надо вызвать функцию и передать в нее значения, с которыми она будет работать. Далее функция производит определенные операторами тела функции действия с этими значениями и сама принимает значение результата этих действий, которое далее используется в основном коде. Чтобы вызвать свою функцию, надо использовать оператор типа:

Text1.Text=Имя функции (значения, передаваемые в функцию)

Теперь, в качестве примера, давай создадим собственную функцию, которая будет возвращать количество слов в тексте.
Начнем новый Exe-проект. На форму положим Text1, куда мы запишем текст, который будет анализировать наша функция. Кроме того нам понадобиться Label1, куда мы выведем возврашаемое функцией значение и кнопка Command1, которой мы будем вызывать нашу функцию.
После того, как мы всю эту хрень запихнули на форму, добавим в наш проект стандартный модуль Module1 (как добавить - см. выше). Откроем его и начнем писать код. Сперва нам надо придумать имя для функции. Пусть это будет CountWords. Затем нам надо определить аргумент, с помощью которого мы передадим в функцию текстовую строку, которую наша функция будет анализировать. Пущай это будет Stroka. Поскольку у нас количество слов большим не будет, определим тип нашей функции как Long. Вот теперь можно написать нашу первую строчку функции.

Function CountWords(Stroka) As Long
'Далее мы объявляем три необходимых нам переменных. Ну x - это просто для цикла, где мы
'будем перебирать каждую буковку и искать пробелы. NumWords - это как раз переменная, в
'которую мы будем записывать количество слов. FlagSlovo - это флаг, благодаря которому мы
'отследим, было ли слово перед тем, как был найден пробел. Таким образом мы избежим
'неправильного подсчета слов из-за того, что слова могут быть написаны через несколько
'пробелов, а кроме того сама строка может начинаться с пробела.
Dim x As Long
Dim NumWords As Long
Dim FlagSlovo As Boolean
'Организуем цикл по количеству символов в строке ( Len(Stroka))
For x = 1 To Len(Stroka)
'Теперь, если значение символа - не пробел (код пробела - 32), присваиваем нашему флагу значение True
If Mid(Stroka, x, 1) <> Chr(32) Then FlagSlovo = True
'А далее анализируем: если у нас нашелся пробел и значение флага True, значит можно считать, что этим пробелом
'закончилось слово и мы увеличиваем наш счетчик слов NumWords на единицу и сбрасываем
' значение флага FlagSlovo на False
If Mid(Stroka, x, 1) = Chr(32) And FlagSlovo = True Then
NumWords = NumWords + 1
FlagSlovo = False
End If
Next x
'А далее, поскольку последнее слово может не заканчиваться пробелом, делаем проверку флага и если
' его значение True увеличиваем значение счетчика еще на единицу.
If FlagSlovo = True Then NumWords = NumWords + 1
'Далее мы тупо присваиваем значение счетчика нашей функции.
CountWords = NumWords
'Вот и все. Простая функция с одним аргументом готова.
End Function

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

Private Sub Form_Load()
Text1.Text = "Microsoft Visual Basic 6 Professional Edition " _
& "- это мощная система программирования, позволяющая быстро и" _
& " эффективно создавать приложения для Microsoft Windows и Microsoft Windows NT "
End Sub

Затем в процедуре кнопки Command1 напишем оператор вызова функции и присваивания его значения Label1:

Private Sub Command1_Click()
Label1.Caption = CountWords(Text1.Text)
End Sub

Теперь при нажатии кнопки Command1 мы в лейбле будем получать количество слов текстбокса. Конечно, как и в любой функции, можно вместо Text1.Text использовать переменную или просто написать любой текст.
Естествено, в функцию можно передать несколько аргументов. Исключительно для иллюстрации: мы можем находить, например, количество слов, начиная с n-ого символа. Для этого в модуле в объявление функции надо добавить аргумент, обозначающий номер символа (назовем его Position):

Function CountWords(Stroka, Position) As Long

и изменить наш цикл, который будет работать не с x=1, а x=Position

For x = Position To Len(Stroka)

А в форме, соответственно, вызов функции для подсчета слов , например, с 30-ой позиции, будет выглядеть

Label1.Caption = CountWords(Text1.Text, 30)

Это будет уже функция с двумя аргументами. Ну принцип, я думаю, ясен.

Процедуры типа Sub - подпрограмма.

Синтаксис процедуры Sub следующий:

Sub имя процедуры(аргументы)
операторы, выполняющие действия
End Sub

Здесь, также как и в функции, имя процедуры мы придумываем сами, аргументы - это данные, которые мы передаем в подпрограмму и с которыми она будет работать. Напишем подпрограмму, которая будет записывать в файл текст из Text1 нашей формы. Чтобы не путаться, добавим в наш проект еще один модуль Module2. Имя нашей процедуры будет AddFileText. В качестве аргументов мы должны передать в подпрограмму имя файла (NameF) и собственно текст (MyText). Пишем во втором модуле

Sub AddFileText(NameF As String, MyText As String)
Dim F As Long
F = FreeFile
Open App.Path & "\" & NameF For Append As #F
Print #F, MyText
Close #F
End Sub

Здесь все должно быть понятно - просто открываем файл с именем NameF и записываем содержимое переменной MyText.
Положим на нашу форму вторую кнопку Command2 и в ее процедуре напишем оператор вызова подпрограммы для записи в файл bbb.txt текста из Text1:

Private Sub Command2_Click()
AddFileText "bbb.txt", Text1.Text
End Sub

Теперь, при нажатии кнопки Command2, у нас в файл bbb.txt будет дописываться текст из текстбокса. В данном случае никаких изменений с передаваемыми значениями аргументов не происходило. И произойти не могло, потому, что мы передали конкретное значение, а не переменную. Такая передача данных называется передача по значению. Однако можно ведь использовать и переменные. Такой способ передачи - по ссылке, может привести к тому (не в нашем случае), что в процессе выполнения подпрограммы значение переменных изменится и в программу вернутся уже измененные значения. Иногда это даже нужно, а иногда это наоборот может привести к ошибке. Чтобы избежать изменения значения переменной в процедуре, используется ключевое слово ByVal. При его использовании VB сохраняет изначальное значение и после окончания процедуры возвращает первоначальное значение переменной, даже если оно было изменено в ходе работы подпрограммы. Синтаксис использования этого ключевого слова в подпрограмме таков:
Sub AddFileText(ByVal NameF As String, MyText As String)

Скачать исходник можно вверху страницы.

Справочно: Процедуры типа Property - свойство.
Помимо процедур типа Function и Sub существуют процедуры типа Property. С их помощью можно определять свойства класса. Поскольку использование классов - дело будущего, то на процедурах Property мы сегодня останавливаться не будем.


Visual Basic с нуля. Глава 17. Пользовательские типы данных. Файлы произвольного и двоичного доступа.

Глава 17. Пользовательские типы данных. Файлы прозвольного и двоичного доступа. Перехват кнопок клавиатуры.
Скачать исходник программы Base
 Дата создания 22.03.2005 {Автор 4us}

Пользовательские типы данных.

Иногда, при использовании некоторых операторов, API-функций, а также просто для собственных нужд необходимо создавать собственные типы данных. Часто их называют структурами. По своей сути структура - это как бы одномерный массив , который мы запихиваем в одну переменную. Но в него могут входить данные разных типов.
Создание собственного типа данных осуществляется с помощью инструкции Type, который используется в секции General кода формы.
Например, для какого-нибудь условного прайса нам нужно хранить следующие данные:
Name - наименование товара
Kolichestvo - количество товара
Cost - цена товара
С одной строны мы можем просто объявить эти три переменные с необходимым нам типом и использовать их каждую в отдельность по необходимости:

Dim Name As String
Dim Kolichestvo As Long
Dim Cost As Single

Но мы также можем объединить эти три переменные в единую структуру, создав тип данных, например, под названием Price. Однако, поскольку, инструкция Type предназначена для использования во всем проекте, то и пользовательский тип данных должен создаваться в стандартном модуле:
Type Price
Name As String
Kolichestvo As Long
Cost As Single
End Type
Хотя, в принципе, ее можно написать и в форме в разделе деклараций с ключевым словом Private:
(Private Type Price)
Теперь нам, там же, в стандартном модуле, надо объявить какую-нибудь глобальную переменную с нашим типом данных. Назовем ее Stroka:
Public Stroka as Price

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

Рисунок 29.
Внешне это напоминает, как будто мы работаем со свойствами переменной, но это не так.
Теперь, вооруженные этими знаниями, перейдем к файлам прямого доступа.

Файлы произвольного (прямого) доступа - база данных внутренними возможностями VB (тренируемся на кошках).

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

Name | Kolichestvo | Cost
Газонокосилка | 2 | 156.17
Сноповязалка | 5 | 312.76

Строка этой таблицы представляет собой запись, а столбец (например Name) - это поле. Таким образом в таблице показан файл с тремя полями, в котором храняться две записи (верхняя строка - просто название полей). При такой организации возможен доступ непосредственно к любой записи по ее номеру. При этом нет необходимости считывать весь файл. Кроме того, возможно считывать и записывать данные, один раз открыв файл и не закрывая его. Причем доступ может быть разрешен и другим пользователям локальной сети.
Файл нельзя читать текстовым редактором, это не текстовой файл.
Ранее, в бейсиках, с помощью таких файлов можно было организовать что-то типа базы данных. С появлением версии Visual Basic 3.0 этот подход утратил свою актуальность, поскольку появилась возможность доступа из Visual Basic к различным "настоящим" базам данных.
Однако с помощью таких файлов можно хранить служебную информацию собственной программы, а кроме того иногда просто нецелесообразно организовывать доступ к серьезным базам типа Access, из-за небольших объемов данных или простоты структуры БД. Кроме того, работа с такими файлами поможет разобратся с принципами организации базы данных и окажет помощь в дальнейшем, когда мы будем рассматривать связь Visual Basic и Access.

Итак. Как и при работе с файлами последовательного доступа, файл прямого доступа надо открыть. Делается это также с помощью все того же многострадального оператора Open. Вот его синтаксис:

Open Имя_ файла For Random Access Тип_доступа Права_доступа As # Номер_файла Len=Длина_записи
где:

Имя_ файла - это, естественно, просто любое имя открываемого файла. Если файла с таким именем не существует, VB его создаст (пустым).
For Random - тип доступа - произвольный
Access -ключевое слово, указывающее, что далее идет тип доступа
Тип_доступа - позволяет установить доступ к файлу: Read - для чтения, Write - для записи, Read Write - для чтения и записи, и наконец если не указывать тип доступа, то по умолчанию - для чтения и записи.
Права_доступа - определяет права доступа к файлу различных пользователей одновременно (Shared - открыто для чтения и записи всем, кому не лень, Lock Read - никто другой не может читать открытый файл, Lock Write - никто другой не может записывать в файл, Lock Read Write - никто другой не может ни писать ни считывать данные файла)
As # Номер_файла - это просто число - идентификатор, как и в файлах последовательного доступа
Len=Длина_записи - определяет длину записи. Поскольку записи не могут быть произвольной или разной длины, необходимо раз и навсегда определить длину записи для данного набора. Здесь есть проблема, так как не всегда известна длина набора данных. Тем не менее если это значение меньше реальной длины записи, то возникнет ошибка, если же задать длину записи с "запасом", то файл будет занимать большое дисковое пространство.

Таким образом, чтобы открыть в текущей директории файл price.bzd произвольного доступа для чтения и записи с неограниченным для пользователей доступом под номером 1 и длиной записи 100 байт, нужен такой оператор:

Open App.Path & "\price.bzd" For Random Access Read Write Shared As #1 Len = 100
или в более коротком виде:
Open App.Path & "\price.bzd" For Random As #1 Len = 100

Теперь, после того, как файл открыт, мы можем производить в него запись и считывание.
Для записи в файл используют оператор Put, а для считывания - оператор Get, синтаксис которых совершенно идентичен:
Put #Номер_файла, Номер_записи, Переменная
Get #Номер_файла, Номер_записи, Переменная
где:

#Номер_файла - это понятно, что тот номер, под которым мы его открыли (у нас #1)
Номер_записи - номер записи, в которую мы записываем данные (в соответствующие поля). Первой записи соответствует номер 1, второй - 2 и т.д. Если номер записи опущен, то записывается запись, на которой стоит указатель после выполнения последнего оператора Put или Get, или та, на которую установлен указатель с помощью функции Seek (см. ниже).
Переменная - это данные, которые мы записываем в файл. При наличии обычной переменной мы сможем сохранить в запись одно-единственное поле. Чтобы сохранить или считать несколько полей в записи, необходимо использовать пользовательский тип данных, о чем мы говорили в начале главы.

Попробуем создать что-то, похожее на простенькую базу данных, внешне похожую на Виндусовый Access. Мы реализуем некоторые возможности БД, такие как создание записи, считывание, удаление записи, поиск записи по содержимому поля. И по ходу разработки познакомимся с сопутствующими этому делу функциями, инструкциями и методами. Прошу учесть, это всего лишь пример, хотя и полностью рабочий. Мы сделаем базу наличия товара на складе на одной-единственной таблице в запись которой будут входит четыре поля: ID для хранения номера записи (чисто иллюстративно), Name - для наименования продукции, Kolichestvo - для количества единиц товара и Cost - для цены товара. Вычисляемые значения, такие как общая сумма товара полями не являются и как в настоящей базе, в файле не хранятся. Они будут вычисляться всякий раз при выводе данных на экран. Именно так происходит и в Accesse.

Итак создадим новый exe-проект. Положим на форму Form1 следующие объекты (рис. 30)

Рисунок 30.

В таблице слева направо:
- первый столбец - массив кнопок Command1 (Control Array). Как их создавать я не раз уже говорил (например смотри Главу 13). Мы будем выводить на экран по 10 записей, поэтому и массивы объектов у нас содержат по 10 элементов (с Command1(0) по Command1(9). Эти кнопки предназначены для удаления соответствующей записи.
-второй столбец - массив лейблов Label12(0)-Label12(9). Они служат только для показа номера записи.
-третий столбец - массив текстбоксов Text1(0)-Text1(9). Сюда будет выводиться содержимое поля Name (наименование товара).
-четвертый столбец - массив текстбоксов Text2(0)-Text2(9). Сюда будет выводиться содержимое поля Kolichestvo (количество товара).
-пятый столбец - массив текстбоксов Text3(0)-Text3(9). Сюда будет выводиться содержимое поля Cost (цена единицы товара).
-шестой столбец - массив лейблов Label1(0)-Label1(9). Здесь будет вычисляемое значение суммы по наименованию товара (Количество умножить на Цену).

Как и положено в настоящей базе, у нас будет и итог по столбцам: Label2 вычисляет количество единиц товара, а Label3 - общую сумму за весь товар. Кроме того у нас будет вертикальный скроллбар Vscroll1 для просмотра нашей таблицы, а также для поиска по наименованию кнопка Command2 и текстбокс Text4. Ну есть еще лейблы общего заголовка и заголовков столбцов и надписи Итого, но они только для красоты и могут быть любыми.

Сразу создадим стандартный модуль Module1 (или Модуль1) - это уж у кого какой VB, и зададим там пользовательский тип Price для переменной Stroka, которую мы там же и объявим:

Option Explicit
'создаем пользовательский тип
Type Price
ID As Long
Name As String * 28
Kolichestvo As Integer
Cost As Single
End Type
'объявляем переменную Stroka с типом Price
Public Stroka As Price

На фига я, спрашивается Name умножил на 28. А это я ничего не умножил, а задал длину строковой переменной, в которую будет записываться наименование товара. Все, что длинее, будет усекаться справа до 28 символов, а строки короче будут дополняться пробелами опять-таки справа до 28 символов. Почему 28? А потому, что я от фонаря на форму положил текстбокс Text1, и в него влезло только 28 буковок. Длины полей числовых переменных определяются их типом и свободное место справа тоже заполняется пробелами.
Обратите внимание на тип данных в пользовательской структуре. Поскольку я предполагаю, что количество товара - целое число и количество записей не будет чрезмерным, то Kolichestvo имеет тип Integer. А вот Cost - цена, может быть дробной, поэтому Single. Я не использовал денежное представление числа, хотя можно было бы.
Ну вот теперь о модуле можно забыть и полностью отдаться форме.
Закрываем модуль и пишем в форме. Сперва, конечно, объявим несколько переменных:

Option Explicit
Dim BeginStroka As Long 'номер записи, с которой начинается вывод десяти строк на экран
Dim LastStroka As Long 'номер следующей за последней записью в
'файле (куда будет записываться новая запись)
Dim OldIndex As Integer 'значение старого индекса массива объектов (Control array) до обновления
Dim IndexForSub As Integer 'промежуточная переменная для передачи значения индекса
'элемента массива из одной процедуры в другую
Dim ItogProm As Single ' промежуточный итог от умножения поля Kolichestvo на Cost

Далее продолжим процедурой загрузки формы:

Private Sub Form_Load()
OldIndex = 0 'обнуляем переменную и текстбокс
Text4.Text = ""
Command2.Caption = "Поиск" 'назначаем кнопке название
Open App.Path & "\price.bzd" For Random As #1 Len = Len(Stroka) 'открываем наш долгожданный файл
' произвольного доступа. В качестве длины нашей записи используем оператор Len(Stroka). Однако при
'использовании различных типов данных возможна ситуация, когда длина записи больше фактической
'длины записи как минимум на 2 байта. Это вызвано тем, что VB прописывает собственные
' дополнительные служебные байты. Но в нашем случае все работает и так.
Do While Not EOF(1) 'Запускаем цикл чтения записей до конца файла.
Get #1, , Stroka ' Поскольку номер записи мы не указали, указатель записи с каждым проходом цикла
'сдвигатся на один и оператор читает следующую запись.
Loop
LastStroka = Seek(1) - 1'узнем количество записей в файле c помощью инструкции SEEK (см. ниже)

'Задаем свойства для Vscroll, чтобы у нас корректно выводилось на экран и менее 10 записей
VScroll1.Min = 1
If LastStroka > 10 Then
VScroll1.Max = LastStroka 'максимальное значение соответствует последней+1 записи
VScroll1.LargeChange = 10
Else
VScroll1.Max = LastStroka 'максимальное значение соответствует последней+1 записи
VScroll1.LargeChange = 1
End If

VScroll1.SmallChange = 1
VScroll1.Value = 1
BeginStroka = VScroll1.Value 'присваиваем переменной начала вывода значение VScroll
SummaCount 'переходим к процедуре вычисления итоговых значений
End Sub

А теперь разберем функцию Seek

Функция Seek.

Ее синтаксис следующий:
Seek(Номер_файла)

Эту функцию можно использовать для файлов, открытых в режиме Random (как в нашем случае) и Binary.
Для файлов, открытых в режиме Random. С помощью этой функции мы можем узнать текущее положение указателя чтения записи внутри файла. То есть в нашем примере , после считывания всего файла указатель устанавливается на последней записи. А использовав эту функцию, мы узнаем, какая запись будет считана (обработана) следующей. Но поскольку следующей записи пока не существует, это будет номер создания новой записи.
Для файлов, открытых в режиме Binary. Поскольку режимы Random и Binary аналогичны, то и функция Seek здесь работает аналогично, за исключением того, что возвращает она не значение номера записи, а номер байта, с которого начнется выполнение следующей операции (считывания или записи). Файлы, открытые в режиме Binary мы разберем в конце этой главы.
Заметь, что кроме функции Seek существует еще инструкция Seek#

Инструкция Seek#.

Синтаксис: Seek #Номер_файла, позиция
Номер_файла - номер открытого файла
позиция - номер записи или байта, на который установится указатель

В отличие от своей одноименной функции, инструкция Seek задает положение указателя чтения или записи, т.е. номер записи в режиме Random или номер байта в режиме Binary, с которых начнется выполнение операции. Причем, если в операторах Put или Get задан номер записи, то инструкция Seek игнорируется.
Установка указателя за конец файла приводит к увеличению размера файла.

Теперь вернемся к коду нашей программы. В конце процедуры Form_Load у нас переход к процедуре SummaCount, где вычисляется итоговые значения по столбцам и обновляется переменная LastStroka . Но чтоб туды перейти, ее надо создать. В ней мы высчитываем итоговые значения по полю Kolichestvo и по сумме. Поля по сумме у нас нет - это вычисляемое значение, поэтому мы суммируем произведения Kolichestvo * Cost по каждой записи. Кроме того Здесь же мы вычисляем новое количество записей в файле. Обратите внимание, что, мы, раз открыв файл, больше его не закрываем, а постоянно обращаемся к нему и для записи и для считывания.

Private Sub SummaCount()
Dim ItogAll As Single
Dim ItogKol As Single
Seek #1, 1 'устанавливаем указатель на первую запись инструкцией Seek#
Do While Not EOF(1) ' Cчитываем весь файл до конца.
Get #1, , Stroka ' Читаем в цикле каждую запись.
ItogAll = ItogAll + (Stroka.Kolichestvo * Stroka.Cost)
Label3.Caption = ItogAll 'выводим итог по сумме
ItogKol = ItogKol + Stroka.Kolichestvo
Label2.Caption = ItogKol 'выводим итог по количеству товара
Loop
LastStroka = Seek(1) - 1 'обновляем количество записей
' переходим к процедуре вывода данных на экран
ShowRecords
End Sub

Из этой процедуры мы переходим к процедуре ShowRecords, которая, наконец, выводит наши поля на экран. Здесь такие нюансы. Поскольку, при записи в файл незаполненные байты полей заполняются пробелами, то при выводе содержимого поля в текстбокс данные справа будут завершаться ненужными пробелами. Чтобы избавиться от них мы будем использовать функцию RTrim.

Функции Trim, LTrim и RTrim.
Эти функции удаляют пробелы из начала строки - слева (LTrim), с конца строки - справа (RTrim) или с обоих концов строки сразу (Trim). Синтаксис этих функций (как и использование) предельно простой:
LTrim(строка)
RTrim(строка)
Trim(строка)
где строка - любое строковое выражение

Теперь вернемся к нашим баранам, т.е., виноват, к процедуре ShowRecords. Давайте, наконец, ее создадим:

Private Sub ShowRecords()
Dim X As Long
'включаем обработку ошибок, цель ее в том, чтобы предотвратить ситуацию,
'когда при наличии менее, чем 10 записей в файле
' или если файла вообще не существует, оператор Get# в цикле For... Next будет пытаться
' считать несуществующие записи.
On Error Resume Next
For X = 0 To 9
'считываем запись, номер которой складывается из индекса текстбокса и номера строки,
' которая выводится на экран первой в текстбокс с индексом 0.
Get #1, X + BeginStroka, Stroka
'выводим содержимое полей в текстбоксы и лейбл, удаляя ненужные пробелы справа
Text1(X) = RTrim(Stroka.Name)
Text2(X) = RTrim(Stroka.Kolichestvo)
Text3(X) = RTrim(Stroka.Cost)
Label12(X).Caption = RTrim(Stroka.ID)
'а для последней, еще не существующей записи вместо нуля выведем для красоты стрелку
'в Caption кнопки Command1 (которая вообще-то предназначена для удаления записи,
' а в Label12 - стрелочку. У нас будет, ну прямо как в Access'е
If Label12(X).Caption = 0 Then
Label12(X).Caption = "new"
Command1(X).Caption = "->"
End If
'кроме того в Label1 выведем результат умножения Количества на Цену
ItogProm = Stroka.Kolichestvo * Stroka.Cost
Label1(X).Caption = ItogProm
'нижеследующие условия нужны для того, чтобы сделать невидимыми текстбоксы, лейблы
'и кнопки, которые будут после окончания файла. Таким образом, пользователь не сможет
' при последней записи, например, 20 ввести данные в запись 26.
If LastStroka - BeginStroka < 10 And X + BeginStroka > LastStroka Then
Text1(X).Visible = False
Text2(X).Visible = False
Text3(X).Visible = False
Label1(X).Visible = False
Label12(X).Visible = False
Command1(X).Visible = False
Else
Text1(X).Visible = True
Text2(X).Visible = True
Text3(X).Visible = True
Label1(X).Visible = True
Label12(X).Visible = True
Command1(X).Visible = True
End If
Next X
'отключаем обработку ошибок
On Error GoTo 0
End Sub

Чегой-то мы устанавливали свойства VScroll1, даже использовали ее значение, а самой процедуры нет. Быстренько ее пишем:

Private Sub VScroll1_Change()
BeginStroka = VScroll1.Value
ShowRecords
End Sub

Из нее мы вызываем процедуру ShowRecords, чтобы обновить записи на форме.

Ну вот, теперь нам нужна процедура, которая бы записывала данные в файл. Написать ее довольно просто, но нам нужно событие, которое мы бы могли использовать для инициализации сохранения записи на файле. Тут возможны самые разнообразные решения, зависит от степени извращенности. Я использовал событие _GotFocus, которое возникает при получении объектом фокуса. Надо заметить, что объекты, для которых пишется процедура с событием _GotFocus должны быть видимы (Visible=True) и не заблокированы (Enabled=True). Все эти условия удовлетворяют нашим текстбоксам. Поэтому для каждого из них пишем собственную процедуру:

Private Sub Text1_GotFocus(Index As Integer)
IndexForSub = Index
MakeRecord
End Sub

Private Sub Text2_GotFocus(Index As Integer)
IndexForSub = Index
MakeRecord
End Sub

Private Sub Text3_GotFocus(Index As Integer)
IndexForSub = Index
MakeRecord
End Sub

Переменная IndexForSub служит для хранения индекса текстбокса, получившего фокус, так как Index доступен в пределах только своей процедуры. И из каждой процедуры мы вызываем процедуру MakeRecord. Она-то и будет записывать нашу запись в файл (тавтология: масло маслянное, я думал об этом с начала главы, но не смог ничего придумать).
Поскольку фокус получает новая запись, а записывать-то надо старую, то номер старой записи храниться в переменной OldIndex. Изначально мы ей прсвоили значение 1 в процедуре Form_Load. Эта запись и запишется первой. А в конце процедуры записи MakeRecord мы присвоим OldRecord значение IndexForSub, которая теперь станет номером старой, т.е. текущей записи. Только не спрашивайте, зачем такой гемморой, и почему я не использовал событие _LostFocus, которое возникает при утрате объектом фокуса. Ты можешь попробовать это сам, типа потренируйся. Прелесть программирования и заключается в том, что гланды можно удалить и через жопу и другими разными способами. Тем более, что мне захотелось показать, как применять переменную, которая постоянно обновляет свое значение (OldIndex). Это я отмазался. На самом деле просто так захотелось.

Cамое время все-таки написать (ударение на последнем слоге - замучился искать синоним) нашу процедуру MakeRecord.

Private Sub MakeRecord()
Dim ItogProm As Single
'При условии, что хотя бы один текстбокс что-нибудь, да содержит
If Len(Text1(OldIndex)) > 0 Or Val(Text2(OldIndex)) <> 0 Or Val(Text3(OldIndex)) <> 0 Then
'присваиваем пользовательской переменной их значения
Stroka.Name = Text1(OldIndex)
Stroka.Kolichestvo = Val(Text2(OldIndex))
Stroka.Cost = Val(Text3(OldIndex))
Stroka.ID = OldIndex + BeginStroka
'сразу выводим номер записи, на случай, если это новая запись
Label12(OldIndex).Caption = RTrim(Stroka.ID)
'а также удаляем стрелочку из Caption кнопки
Command1(OldIndex).Caption = ""
'собственно записываем запись
Put #1, OldIndex + BeginStroka, Stroka
End If
'получаем номер новой старой записи
OldIndex = IndexForSub
'и отправляемся считать итоги, а оттуда - вывод полей по-новой
SummaCount
End Sub

В основном ядро программы сделано.
Далее, поскольку мы налепили целый массив кнопок Command1 для уничтожения записи, надо стало-быть ваять под это дело процедуру. Для уничтожения записи я использовал такой метод: мы просто переписываем наш файл в новый под именем Copyprice.bzd без той записи, которую мы хотим удалить, затем закрываем текущий файл и удаляем его. А новую копию переименовываем в старый и открываем его по-новой. Все это реализовано в процедуре Command1_Click:

Private Sub Command1_Click(Index As Integer)
Dim X As Integer
Dim Z As Integer
'открываем файл-копию
Open App.Path & "\Copyprice.bzd" For Random As #2 Len = Len(Stroka)
For X = 1 To LastStroka - 1
'считываем запись с основного файла
Get #1, X, Stroka
'проверяем, чтобы не перезаписать уничтожаемую запись
If Index + BeginStroka <> X Then
Z = Z + 1
Stroka.ID = Z
'записываем запись во вспомогательный файл
Put #2, Z, Stroka
End If
Next X
'закрываем все файлы
Close
'убиваем основной файл
Kill App.Path & "\price.bzd"
'переименовываем вспомогательный в основной
Name App.Path & "\Copyprice.bzd" As App.Path & "\price.bzd"
'открываем его, как основной
Open App.Path & "\price.bzd" For Random As #1 Len = Len(Stroka)
' вперед на обсчет суммы и далее по коду
SummaCount
End Sub

Аналогично можно сделать и сортировку записей в файле. Но так в настоящих базах не делают. Там содержимое файла остается неизменным, меняется только вывод его на экран. Мы этого здесь делать вообще не будем. Но процедуру поиска записи по полю Наименование под кнопкой Command2 все-таки создадим:

Private Sub Command2_Click()
Dim X As Long
Dim FoundStroka As Boolean
FoundStroka = False 'флаг - если не найдено, значение не меняется,если найдено - становится True
'проверяем не пусто ли поле с критерием поиска, а то всяко бывает
If Text4.Text <> "" Then
For X = 1 To LastStroka - 1
'Считываем записи
Get #1, X, Stroka
'находим первое вхождение
If InStr(RTrim(Stroka.Name), Text4.Text) Then
'в соответствии с номером строки меняем VScroll, чтобы искомая запись стала первой
BeginStroka = X
VScroll1.Value = X
'Устанавливаем флаг
FoundStroka = True
End If
Next X
'выводим сообщение, если ничерта не найдено
If FoundStroka = False Then MsgBox "Результат поиска", , "Ничего не найдено"
'устанавливаем фокус на найденную строку (первую)
Text1(0).SetFocus
'переписываем текстбоксы
ShowRecords
End If
End Sub

Здесь мы для задания фокуса Text1(0) использовали метод SetFoсus. Он передает фокус заданному элементу управления, в нашем случае текстбоксу. Фокус можно передавать только видимому элементу - это как-бы понятно. Нельзя использовать этот метод внутри процедуры Form_Load, так как до ее окончания все элементы формы не видны. Чтобы сделать их видимыми до окончания процедуры Form_Load, можно использовать метод Show.

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

События и перехват кнопок клавиатуры

Private Sub Form_KeyDown(код_клавиши As Integer, shift As Integer) Private Sub объект_KeyDown([индекс As Integer,]код_клавиши As Integer, shift As Integer) Private Sub Form_KeyUp(код_клавиши As Integer, shift As Integer) Private Sub объект_KeyUp([индекс As Integer,]код_клавиши As Integer, shift As Integer) | Возникает при нажатии кнопки клавиатуры (KeyDown) и при отпускании (KeyUp), если объект получил фокус. Индекс определяет элемент массива , если имеется массив объектов. Код клавиши возвращает код клавиши, которые могут определятся константами VB. Переменная shift возвращает состояние кнопок shift, alt и ctrl в момент нажатия кнопок клавиатуры (1- shift(vbShiftMask ), 2- alt (vbCtrlMask) и 4 - ctrl (VbAltMask )). При удержании больше одной кнопки их значения складываются (ctrl-alt = 4+2=6). Свойство не вызывается для: - клавиши ENTER, если на форме есть элемент управления CommandButton, свойство Default которого имеет значение True; - клавиши ESC, если на форме есть элемент управления CommandButton, свойство Cancel которого имеет значение True; ·- клавиши TAB. События различают регистр при использовании кода_клавиши совместно с shift. Значение shift сравнивается с битовой маской с использованием оператора And: ( ShiftDown = (Shift And vbShiftMask) > 0)
Private Sub Form_KeyPress(код_ascii As Integer) Private Sub объектt_KeyPress([индекс As Integer,]код_ascii As Integer) | Возникает при нажатии и отпускании кнопки клавиатуры, если объект получил фокус. Индекс определяет элемент массива , если имеется массив объектов. Код_ascii - стандартный ANSI код клавиши. Событие KeyPress может получать любые печатаемые символы, символы клавиши CTRL в сочетании с символом стандартного алфавита или один из специальных символов, а также клавиши ENTER или BACKSPACE. Распознает верхний и нижний регистры каждого символа как различные коды клавиш. Если присвоено значение True свойства KeyPreview, то форма получает событие раньше, чем элементы управления формы.

Теперь вернемся к нашей программе. После ввода данных в текстбокс и нажатии клавиши ENTER хорошо бы, чтобы курсор , то бишь фокус передавался следующему текстбоксу. Тогда это вызовет событие LostFocus И наша запись обновится. И выглядеть это будет красиво. Для этих целей мы можем использовать, в принципе, любое событие из перечисленных в таблице. Возьмем, например _KeyDown и для каждого техтбокса напишем по короткой процедурке:

Private Sub Text1_KeyDown(Index As Integer, KeyCode As Integer, Shift As Integer)
If KeyCode = 13 Then
IndexForSub = Index
Text2(Index).SetFocus
MakeRecord
End If
End Sub

Private Sub Text2_KeyDown(Index As Integer, KeyCode As Integer, Shift As Integer)
If KeyCode = 13 Then
IndexForSub = Index
Text3(Index).SetFocus
MakeRecord
End If
End Sub

Private Sub Text3_KeyDown(Index As Integer, KeyCode As Integer, Shift As Integer)
If KeyCode = 13 Then
Text1(Index + 1).SetFocus
IndexForSub = Index
MakeRecord
End If
End Sub

Код клавиши ENTER - 13. Мы отслеживаем ее и передаем фокус следующему текстбоксу. При этом не забываем, где нужно, запоминать индекс массива элемента объектов.
На этом код весь. Песня кончилася. Всю эту хрень ты можешь скачать вверху страницы. Еще раз повторяю, что это лишь пример, а не полнофункциональная программа.
А сейчас, напоследок нам надо разобрать пример работы с файлом двоичного доступа.

Работа с файлами двоичного доступа.

Работа с файлами, открытыми для двоичного доступа практически такая же, как и с файлами произвольного доступа. Разница заключается в том, что мы считываем не запись, а отдельный байт. При этом файл открывается в режиме Binary. Синтаксис оператора Open cледующий:

Open Имя_ файла For Binary Access Тип_доступа Права_доступа As # Номер_файла

где все тоже самое, см. файл произвольного доступа в начале главы. Зато здесь нет необходимости парится с пользовательскими типами данных и главное с длиной записи. Поскольку ничего этого нет. Байт мы читаем прямо в переменную.
С помощью такого доступа мы можем залезть в любой файл, считать байты, которые нам нужны, записать в файл чего хотим и после этого файл скорее всего работать не будет. Потому что, прежде чем лезть писать (снова ударение на последнем слоге), надо знать структуру файла данного формата, будь то .exe или там jpg, но тем не менее разберем маленький примерчик. Кстати, структуры графических файлов BMP, GIF и JPG(JFIF) и кое-какие издевательства над ними с помощью именно бинарного чтения файлов будут опубликованы в Части 3 "Реализация некоторых задач". А еще кстати, если кому интересно, в конец exe-файла можно дописать чего угодно и он останется работоспособным.

Но продолжим.
Считывание и запись осуществляется с помощью все тех же операторов Get и Put.
Практически это так.
Давайте создадим exe-проектик, положим на него Text1 и Command1. В процедуре кнопки и будем писать код. Переменная, в которую мы будем считывать байт назовем Bait. Обратите внимание, что объявляться она должна с типом Byte. Итак, чтобы нам считать пятый байт из файла save.Bmp и его содержимое отобразить в Text1, нам надо написать следующее:

Private Sub Command1_Click()
Dim Bait As Byte
Text1.Text = ""
Open App.Path & "\save.Bmp" For Binary As #1
Get #1, 5, Bait
Text1.Text = Bait
Close #1
End Sub

Однако в Text1 большинство символов не отобразится, поэтому лучше использовать не содержимое Bait, а его ASCII-код. Для этого можно использовать функцию AscB(строка_байтов). Она похожа на функцию Asc, но предназначена специально для байт в строке:

Text1.Text =AscB(Bait)

Для того, чтобы считать несколько байтов, можно использовать цикл. Считаем 10 байт с 12 по 21-й:

Private Sub Command1_Click()
Dim x As Integer
Dim Bait As Byte
Text1.Text = ""
Open App.Path & "\save.Bmp" For Binary As #1
For x = 12 To 21
Get #1, x, Bait
Text1.Text = Text1.Text & "*" & AscB(Bait)
Next x
Close #1
End Sub

Иным способом это можно сделать, исходя из того, что число считываемых байт равняется числу символов, уже содержащихся в строке. Поэтому, если установить указатель с помощью функции Seek# на 12 байт и передать в переменную 10 пробелов, мы должны получить тот же результат. К сожалению, в этом случае функция AscB считает лишь первый байт:
В этом случае переменную Bait надо объявлять как String (строковая).

Private Sub Command1_Click()
Dim x As Integer
Dim Bait As String
Text1.Text = ""
Open App.Path & "\save.Bmp" For Binary As #1
Seek #1, 12
Bait = String(10, " ")
Get #1, , Bait
Text1.Text = AscB(Bait)
Close #1
End Sub

И в конце, если мы положим на форму кнопку Command2, то в ее процедуре можем попробовать написать код, который будет записывать в первые 10 байтов нашего несчастного файла букву "Ш" или ее код - 216:

Private Sub Command2_Click()
Dim x As Integer
Dim Bait As String
Text1.Text = ""
Open App.Path & "\save.Bmp" For Binary As #1
Bait = Chr(216) 'Ш
For x = 1 To 10
Put #1, , Bait
Text1.Text = Text1.Text & "*" & (Bait)
Next x
Close #1
End Sub

При очень большом желании исходник последнего варианта можно скачать здесь.


Visual Basic с нуля. Глава 18. Метод Drag & Drop (перетаскивание) внутри приложения.

Глава 18. Технология Drag&Drop (перетаскивание) в пределах формы. События перетаскивания. Автоматическое перетаскивание. Метод Drag.
Скачать исходник примера DragDrop
 Дата создания 03.04.2005 {Автор 4us}

При работе с приложениями Widows часто используется такой прием, когда юзер может нажать над объектом левую кнопку мыши, и не отпуская ее переместить мышь в другое место. При этом образ объекта следует за курсором мыши. При отпускании кнопки объект перемещается в новое место (если это предусмотрено программистом). Такая технология называется Drag & Drop - перетаскивание (или перетащил и оставил).

Использование этой технологии, по-моему является роскошью, однако придает программе профессиональный вид, а иногда даже делает интерфейс более удобным для пользователя.
Реализовать это довольно просто. Но прежде всего надо уяснить себе следующее:
Объект может быть взят только оттуда и перетащен только туда, как это определил программист. Иными словами, для реализации этой технологии должен быть объект-источник Drag и объект-приемник Drop и с каждым объектом, чтобы он поддерживал Drag&Drop, необходимо поработать отдельно.

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

Private Sub Объект_DragDrop([индекс As Integer,]source As Control, x As Single, y As Single) | Происходит при завершении операции drag&drop, т.е. перетаскиваемый объект находится в зоне объекта-приемника и кнопка мыши отпускается. Индекс определяет элемент массива , если имеется массив объектов. source -аргумент, определяющий перетаскиваемый элемент (содержимое). К нему могут добавляться методы и свойства (Sourse.Tag, Sourse.Visible и т.п.). Если используется несколько объектов для перетаскивания, то для определения типа элемента-приемника используется ключевое слово TypeOf с оператором If, а для однозначного определения объекта используется свойство Tag. x, y - текущие координаты мыши на форме или объекте. Всегда измеряются в единицах объекта-приемника. Событие используется для создания процедуры объекта-приемника после окончания перетаскивания для операторов, описывающих действия результата пертаскивания.
Private Sub Объект_DragOver([индекс As Integer,]source As Control, x As Single, y As Single, state As Integer) | Происходит во время выполнения операции drag&drop, т. е. при нажатой кнопки мыши. Индекс определяет элемент массива , если имеется массив объектов. source -аргумент, определяющий перетаскиваемый элемент (содержимое). К нему могут добавляться методы и свойства (Sourse.Tag, Sourse.Visible и т.п.). x, y - текущие координаты мыши на форме или объекте. Всегда измеряются в единицах объекта-приемника. state - показывает мгновенное состояние перетаскиваемого объекта по отношению к области объекта-приемника (область цели) и принимает значения: 0 (vbEnter)= Вход (элемент управления–источник входит в область цели). 1 (vbLeave)= Выход (элемент управления–источник покидает область цели). 2 (vbOver)= Над (элемент управления источник перемещается в пределах области цели из одного положения в другое). С помощью этого события создаются процедуры для визуализации операции перетаскивания (изменения курсора, подсвечивание объекта).

Теперь можно начинать чего-нибудь потаскать. Начнем с малого, а именно с автоматического перетаскивания.

Автоматическое перетаскивание.

Автоматическое перетаскивание - наиболее простая и наиболее худшая реализация технологии Drag&Drop.
Делается это элементарно. У многих объектов имеется свойство DragMode. Оно определяет режим перетаскивания для объекта и может принимать два значения: .DragMode=0 , т. e. вручную. и .DragMode=1 , т.е. автоматически. По умолчанию оно в проекте всегда установлено состояние 0, то бишь ручное, и это правильно. Но мы идем идиотским путем (а его нужно пройти обязательно), поэтому установим свойство DragMode=1. Это можно сделать в окне свойств, а можно программно.
Давайте создадим новый exe-прокт и посмотрим, как это все работает. Положим на форму Text1, Text2 и Text3 со свойствами Multiline=True.
Сейчас мы попытаемся организовать перетаскивание содержимого Text1 и Tex2 в Text3. И это нам удастся. Пишем код. Начнем со стандартного (хоть тут и нет переменных)

Option Explicit

В загрузке формы установим автоматический режим свойства DragMode для Text1 и Text2 и заполним их текстом, а Text3 очистим:

Private Sub Form_Load()
Text1.DragMode = 1
Text2.DragMode = 1
Text1.Text = "Это какой-то текст, записанный в Text1"
Text2.Text = "А это другой текст, записанный в Text2"
Text3.Text = ""

Да, чуть не забыл. Нам дана крутая возможность во время перетаскивания менять изображение курсора мышки. Обалдеть можно. Хотя, на самом деле, это важно. Пользователь должен контролировать состояние перетаскивания. Ну и на этом спасибо. Курсор меняется с помощью свойства .DragIcon. Это свойство определяет значок, который будет использоваться в качестве указателя мыши при операции перетаскивания. Ты можешь в окне свойств установить свойству DragIcon какую-нибудь иконку - файл с расширением .ico. Мы для ясности сделаем это программно.

Text1.DragIcon = LoadPicture(App.Path & "\Move.ico")
Text2.DragIcon = LoadPicture(App.Path & "\Move.ico")

Но при программной загрузке картинок их всегда надо таскать с проектом, а при загрузке картинок в окне свойств они становятся частью проектов и отдельно их хранить не надо.
Все. Процедура Form_Load закончена.

End Sub

Теперь логика следующая. Мы будем использовать для создания процедуры событие DragDrop, а поскольку оно возникает по окончании процесса перетаскивания, то процедуру надо создавать в Text3. Ведь именно там конечный пункт нашего процесса.

Private Sub Text3_DragDrop(Source As Control, X As Single, Y As Single)

Координаты мыши X и Y нас сейчас мало интересуют, все равно VB сам определит, перетащили мы объект на Text3 или нет. А вот аргумент Source мы используем. С его помощью и ключевого слова TypeOf мы проведем проверку типа объекта (то что мы перетаскиваем Text в Text, а не Text в Picture) и присобачив к нему свойство .Text изменим содержимое Text3. Это и будет результат перетаскивания.

If TypeOf Source Is TextBox Then
Text3.Text = Source.Text
End If
End Sub

Теперь ты можешь опробовать результат, запустив проект и поперетаскивать в Text3 и Text1 и Text2. Если ты не смог написать эти пятнадцать строк кода самостоятельно, то скачать исходник примера можешь здесь.
Откровенно говоря в данном случае ключевое слово TypeOf притянуто за уши, так как других объектов на форме нет.
Можно вместо этого проверять конкретно каждый элемент и присваивать значение того, которого захотим. Для идентификации элемента есть простенькое свойство Tag. Смысл его в том, что по нему с помощью аргумента Source мы можем однозначно идентифицировать объект-источник. В отличие от других свойств, значение свойства Tag не используется языком Visual Basic, но его можно использовать для идентификации объектов. Этому свойству мы можем присвоить любое имя-идентификатор, главное, чтоб они не были одинаковыми. Для этого в процедуру Form_Load допишем две строчки:

Text1.Tag = "Text один"
Text2.Tag = "002"

Теперь наша процедура Text3_DragDrop будет выглядеть следующим образом:

Private Sub Text3_DragDrop(Source As Control, X As Single, Y As Single)
If Source.Tag = "Text один" Or Source.Tag = "002" Then
Text3.Text = Source.Text
End If
End Sub

Опять же, если чего не получилось, скачать эту фигню можно здесь.

Теперь рассмотрим результаты нашей деятельности. Юзер кайфует, радостно перетаскивая текст из одного объекта в другой. Это несомненно хорошо. Потом юзеру надоедает это делать и он с удивлением обнаруживает тот факт, что отредактировать-то текст он не может ни в Text1, ни в Text2. Это несомненно плохо. А дело в том, что инициируемые юзером события мыши или клавиатуры KeyDown, KeyPress,KeyUp, MouseDown, MouseMove и MouseUp распознаваться НЕ БУДУТ!
Видя такое дело, нам целесообразно перейти к более сложному, но контролируему делу - ручному перетаскиванию.

Метод Drag - ручное перетаскивание.

Этот метод начинает, завершает или отменяет операцию перетаскивания любого из элементов управления кроме Line, Menu, Shape, Timer и CommonDialog. Он позволит нам не блокировать объект для редактирования, использовать всевозможные иконки, чтоб юзер видел, что творит при перетаскивании и вообще правильный метод.
Еще раз напоминаю очень важную вещь, если ты не понял из предыдущего: результат перетаскивания над перемещаемым объектом (копирование, удаление, вставка и т.д.) определяется в объекте-приемнике в процедуре обработки события DragDrop.
Итак, для того, чтобы начать чего-нибудь перетаскивать, надо перейти в режим перетаскивания. Для этого обычно используется процедура события MouseDown для объекта-источника. В ней мы включаем метод Drag. Для включения-выключения метода и отмены перетаскивания используются следующие константы, их объявлять не надо:
vbCancel =0 'отменяет операцию перетаскивания
vbBeginDrag =1 'включает метод Drop
vbEndDrag =2 'выключает метод Drop

Давай-ка лучше сделаем новый exe-проект и попробуем все на практике. Наш новый проект мало чем отличается от старого по внешнему виду, но не по коду. На форме у нас будут Text1, Text2, Text3 и еще кнопка Command1. Смысл проекта в том, чтобы перетаскивать содержимое Text1 в Text3, а Text2 запрещен для перетаскивания. И для прикола, потаскаем по форме командную кнопку Command1. Итак, начнем.
Объявим переменную Flag как булеву. На фига я ее ввожу? Ну не знаю, чтобы позволить юзеру беспрепятственно редактировать текст в объекте-источнике, т.е в Text1. Т.е. изначально Flag=False и при первом щелчке метод Drag в процедуре Text1_MouseDown не включается. Юзер спокойно может редактировать текст. Но в конце процедуры Flag меняет свое значение на противоположный (Flag = Not Flag) и при повторном нажатии кнопки мыши уже включается метод Drag.

Option Explicit
Dim Flag As Boolean

В процедуре загрузки формы зададим какое-нибудь содержимое TextBox'ам (можем и не задавать и вообще наплевать на нее):

Private Sub Form_Load()
Text1.Text = "Это какой-то текст, записанный в Text1"
Text2.Text = "А я вообще не знаю, что здесь делаю"
Text3.Text = "А это другой текст, записанный в Text3"
End Sub

Далее в событии MouseDown для объекта-источника Text1 включим метод Drag для левой кнопки мыши. И меняем состояние переменной Flag:

Private Sub Text1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
'проверяем, если нажата левая кнопка мыши и это повторное нажатие то
If Button = vbLeftButton And Flag = True Then
'включаем метод Drag
Text1.Drag vbBeginDrag
'загружаем общую иконку для курсора в виде молнии. Она будет обозначать,
'что объект готов к перетаскиванию
Text1.DragIcon = LoadPicture(App.Path & "\Move.ico")
End If
'меняем значение флага на противоположное
Flag = Not Flag
End Sub

В объекте-приемнике по событию DragDrop производим результат перетаскивания, т.е. присваиваем значение Text3=Text1. При этом проверяем тип объекта-источника:

Private Sub Text3_DragDrop(Source As Control, X As Single, Y As Single)
'проверяем, соответствует ли тип объекта-источника TextBox'у
If TypeOf Source Is TextBox Then
'если да, то
Text3.Text = Source.Text
'и для солидности передадим фокус в объект-приемник (Text3)
Text3.SetFocus
End If
End Sub

Поскольку наглядность перетаскивания для юзера чрезвычайно важна, загрузим иконки в соответствии с положением мыши для объекта-источника (Text1):

Private Sub Text1_DragOver(Source As Control, X As Single, Y As Single, State As Integer)
'если мышь над объектом-источником грузим обычную иконку (молния)
If State = vbEnter Then
Text1.DragIcon = LoadPicture(App.Path & "\Move.ico")
'если покидает объект, то критический треугольник
ElseIf State = vbLeave Then
Text1.DragIcon = LoadPicture(App.Path & "\No.ico")
End If
End Sub

и для объекта-приемника. Причем, обратите внимание, что в процедуре Text3 объекта-приемника мы определяем иконки для объекта-источника (Text1). В противном случае возникнет путаница.

Private Sub Text3_DragOver(Source As Control, X As Single, Y As Single, State As Integer)
'если мышь над объектом-приемником, то листик с кнопкой - готовность принять объект
If State = vbEnter Then
Text1.DragIcon = LoadPicture(App.Path & "\Yes.ico")
'если вне объекта-приемника - то критический треугольник
ElseIf State = vbLeave Then
Text1.DragIcon = LoadPicture(App.Path & "\No.ico")
End If
End Sub

Вот вобщем-то и все. В заключении я хочу показать, что использование проверки типа объекта-источника важна. На этой же форме включим метод Drag для командной кнопки Command1.

Private Sub Command1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
If Button = vbLeftButton Then
Command1.Drag vbBeginDrag
End If
End Sub

и напишем процедуру ее таскания по форме

Private Sub Form_DragDrop(Source As Control, X As Single, Y As Single)
If TypeOf Source Is CommandButton Then
Source.Move X, Y
End If
End Sub

Если бы мы не проверяли тип объекта с помощью ключевого слова TypeOf, то при попытке перетащить кнопку в Text3 получили бы ошибку и фатальное завершение программы. Можешь попробовать. А при наличии проверки наши методы не мешают друг другу и все проходит успешно.

В этой главе мы разобрали технологию Drag&Drop для перетаскивания в пределах одного нашего приложения. Скачать исходник примера можно вверху страницы.
В следующей главе мы попробуем исследовать эту технологию для обмена данных между разными приложениями.


Visual Basic с нуля. Глава 19 Технология Drag&Drop между приложениями.

Глава 19. Технология Drag&Drop (перетаскивание) между приложенями. Автоматическое перетаскивание. Метод OLEDrag. События перетаскивания. Объект DataObject.
Скачать исходник примера OLEDrag
 Дата создания 25.04.2005 {Автор 4us}

Итак, в прошлой главе мы проанализировали технологию Drag&Drop при перетаскивании под управлением событий. Это хорошо при работе внутри нашего проекта. Если же нам необходимо осуществить перетаскивание из окна совсем другого приложения, то навряд-ли мы дождемся наступления какого-нибудь события для нашей формы (или ее объектов), если будем щелкать по чужому окну. Поэтому сейчас мы проанализируем перетаскивание с помощью OLE технологий. Принцип перетаскивания здесь тот же, что и при перетаскивании с использованием событий - есть объект-приемник и объект-источник. Один и тот же объект может быть и приемником и источником (в случае если он используется и для приема и для передачи).
Как и в прошлой главе, начнем с чего по-проще - с автоматического перетаскивания.

Автоматическое перетаскивание.

Очень простая и удобная технология. С его помощью мы просто перемещаем содержимое одного объекта в другой объект. Для включения автоматического перетаскивания мы имеем два свойства:

.OLEDropMode - активизирует объект в качестве приемника
 | может принимать значения:
 | 0 (vbOLEDropNone) - Нет
 | 1 (vbOLEDropManual) - Вручную
 | 2(vbOLEDropAutomatic) - Авто
.OLEDragMode - активизирует объект в качестве передатчика
 | может принимать значения:
 | 0 (vbOLEDragManual) - Вручную
 | 1 (vbOLEDragAutomatic) - Авто

Эти свойства можно задавать как в окне свойств, так и программно, в ходе выполнения программы.

Но, к сожалению, не все элементы управления (объекты) поддерживают автоматическое перетаскивание. Некоторые элементы поддерживают оба свойства - и .OLEDropMode и .OLEDragMode, а некоторые - только OLEDragMode. А ряд элементов вообще нельзя использовать для автоматического перетаскивания. Вот элементы и поддерживаемые ими свойства.

Элемент управления с режимом авто (OLE перетаскивание) | .OLEDropMode | OLEDragMode
ComboBox | НЕТ | ДА
DBComboBox | НЕТ | ДА
DBGrid | ДА | ДА
DBListBox | НЕТ | ДА
DirecoryListBox | НЕТ | ДА
FileListBox | НЕТ | ДА
Image | ДА | ДА
ListBox | НЕТ | ДА
ListView | НЕТ | ДА
MaskedEdit | ДА | ДА
PictureBox | ДА | ДА
RichTextBox | ДА | ДА
TextBox | ДА | ДА
TreeView | НЕТ | ДА

Создадим новый exe-проект. Положим на форму PictureBox1 и Image1. Установим программно свойства перетаскивания в автоматический режим:

Private Sub Form_Load()
Picture1.OLEDropMode = vbOLEDropAutomatic 'автоматический
  прием
Picture1.OLEDragMode = vbOLEDragAutomatic 'автоматическая
  передача
Image1.OLEDropMode = vbOLEDropAutomatic
Image1.OLEDragMode = vbOLEDragAutomatic
End Sub

Это, собственно, все. Запустим проект. Если теперь мы откроем какую-нибудь картинку в приложении, поддерживающем технологию OLE-перетаскивания (например Photoshop, но при этом должен быть включен инструмент "Двигать" ("Move")), то запросто можем перетащить картинку из него в наш проект и обратно.
Также можно перетаскивать и внутри нашего проекта из Picture1 в Image1 и наоборот. При этом обратите внимание на то, что при перетаскивании нажатой кнопкой мыши картинка в объекте-передатчике уничтожается, т.е. происходит перемещение. Чтобы происходило копирование (картинка сохранялась) перетаскивание надо осуществлять с нажатой кнопкой Ctrl.
При автоматической реализации OLE-технологии Drag&Drop мы не обрабатываем результаты перетаскивания. Картинка (или другие данные) должны быть загружены в объект-передатчик. Т.е. мы не можем, например, перетащить в Image1 иконку графического файла .jpg из проводника Windows в надежде, что этот файл там неожиданно откроется. Поскольку мы программно этого не описали, никаких сюрпризов ждать не приходится.
Для более гибкого (и соответственно более сложного) управления перетаскиванием можно попробовать ручное перетаскивание.

Метод OLEDrag - ручное перетаскивание.

Ручное управление перетаскиванием позволяет программировать различные дополнительные действия. Кроме того с его помощью можно работать с такими элементами управления, не поддерживающими автоматическое перетаскивание, как CommandButton, Label, CheckBox, OptionButton, Frame и DriveListBox.

Итак, приступим. Помимо рассмотренных нами уже свойств .OLEDropMode и .OLEDragMode мы имеем еще целый ряд событий и их нам, как говорится, надо:
Private Sub объект_OLEDragDrop(data As DataObject, effect As Long, button As Integer, shift As Integer, x As Single, y As Single)

Возникает при перетаскивании объекта-источника в объект-приемник.
data - объект DataObject содержит форматы (и возможно данные для этих форматов), которые обеспечивает источник. Если в объекте никаких данных не содержится, то они предоставляются при вызове метода GetData. Методы SetData и Clear использованы быть не могут.
effect - число long, устанавливаемое приемником, для опознавания действия, которое выполнено (например удалять или копировать содержимое источника). Может принимать значения следующих констант:
0 (vbDropEffectNone) - приемник не может принять данные
1 (vbDropEffectCopy) - передача копии (исходник источника сохраняется)
2 (vbDropEffectMove) - перемещение данных (исходник источника удаляется)
button - соответствует состоянию кнопки мыши. 1 - левая кнопка, 2 - правая и 4 - средняя.
shift - указывает состояние кнопок shift, alt и ctrl (1- shift, 2- alt и 4 - ctrl). При удержании больше одной кнопки их значения складываются (ctrl-alt = 4+2=6).
Значения переменных x и y возвращают текущие координаты курсора мыши (x - по-горизонтали, y - по-вертикали) в единицах измерения, заданных свойствами ScaleHeight, ScaleWidth.

Private Sub объект_OLEDragOver(data As DataObject, effect As Long, button As Integer, shift As Integer, x As Single, y As Single, state As Integer)

 Возникает при перетаскивании одного компонента над другим.
data - объект DataObject содержит форматы (и возможно данные для этих форматов), которые обеспечивает источник. Если в объекте никаких данных не содержится, то они предоставляются при вызове метода GetData. Методы SetData и Clear использованы быть не могут.
effect - число long, устанавливаемое приемником, для опознавания действия, которое выполнено (например удалять или копировать содержимое источника). Может принимать значения следующих констант:
0 (vbDropEffectNone) - приемник не может принять данные
1 (vbDropEffectCopy) - передача копии (исходник источника сохраняется)
2 (vbDropEffectMove) - перемещение данных (исходник источника удаляется)
-2147483648 (&H80000000) (VbDropEffectScroll) - происходит или подготавливается прокрутка в приемнике (только при выполнении пользовательской прокрутки в приемнике).
button - соответствует состоянию кнопки мыши. 1 - левая кнопка, 2 - правая и 4 - средняя.
shift - указывает состояние кнопок shift, alt и ctrl (1- shift, 2- alt и 4 - ctrl). При удержании больше одной кнопки их значения складываются (ctrl-alt = 4+2=6).
Значения переменных x и y возвращают текущие координаты курсора мыши (x - по-горизонтали, y - по-вертикали) в единицах измерения, заданных свойствами ScaleHeight, ScaleWidth.
state - показывает состояние перетаскиваемого объекта по отношению к форме. Может принимать следующие значения:
0 (vbEnter) - источник в пределах приемника
1 (vbLeave) - источник вне окрестности приемника
2 (vbOver) - источник перемещается в приемнике из одного положения в другое
Private Sub объект_OLEGiveFeedback (effect As Long, defaultcursors As Boolean)
Происходит после каждого события _OLEDragOver. Позволяет показывать пользователю (например с помощью изменения курсора мыши) ход перетаскивания.
effect - число long, устанавливаемое в событии _OLEDragOver, и определяет действие, которое может быть педпринято, если пользователь опускает объект на компонент. Используется для визуализации перетаскивания.
Может принимать значения следующих констант:
0 (vbDropEffectNone) - приемник не может принять данные
1 (vbDropEffectCopy) - передача копии (исходник источника сохраняется)
2 (vbDropEffectMove) - перемещение данных (исходник источника удаляется)
-2147483648 (&H80000000) (VbDropEffectScroll) - происходит или подготавливается прокрутка в приемнике (только при выполнении пользовательской прокрутки в приемнике).
defaultcursors - значение True, если использовать курсор мыши по умолчанию и False, если не использовать. (Курсор может быть установлен свойством .MousePointer объекта Screen)
Private Sub объект_OLEStartDrag (data As DataObject, allowedeffects As Long)

Происходит, когда выполняется метод OLEDrag или когда объект инициирует операцию перемещения, если свойство OLEDragMode установлено в значение 1 - авто.
data - объект DataObject содержит форматы (и возможно данные для этих форматов), которые обеспечивает источник. Если в объекте никаких данных не содержится, то они предоставляются при вызове метода GetData. Методы SetData и Clear использованы быть не могут.
allowedeffect - число long, содержит результаты, поддерживаемые приемником. Значения данного параметра устанавливаются программистом. Может принимать значения следующих констант:
0 (vbDropEffectNone) - приемник не может принять данные
1 (vbDropEffectCopy) - передача копии (источник сохраняется)
2 (vbDropEffectMove) - перемещение данных (источник удаляется)
Private Sub объект_OLESetData(data As DataObject, dataformat As Integer)
         Происходит на источнике, когда приемник выполняет метод GetData на объекте DataObject, но данные указанного формата еще не загружены.
data - объект DataObject, в который необходимо поместить запрашиваемые данные. Компонент вызывает метод SetData, чтобы загрузить запрашиваемый формат.
dataformat - целое число, уточняющее формат данных. Используется для указания источнику, что загружать в объект DataObject.
Private Sub объект_OLECompleteDrag ([effect As Long]) Возникает, когда источник опускается на получатель, информируя источник, что операция перетаскивания выполнена или отменена.
effect - число long, устанавливаемое источником, позволяющее определить действие, которое выполнено. Может принимать значения следующих констант:
0 (vbDropEffectNone) - приемник не может принять данные
1 (vbDropEffectCopy) - передача копии (источник сохраняется)
2 (vbDropEffectMove) - перемещение данных (источник удаляется)

Прочитав этот полубезумный бред, мы не будем впадать в истерику. Известна аксиома - "Visual Basic - это не просто просто, а очень просто". Исходя из этой аксиомы, морща лоб от напряжения и пуская слюни от усердия, мы попытаемся сейчас реализовать технологию перетаскивания на раз-два.
Изначально будем считать, что у нас есть наше приложение и некое чужое. Вот между ними мы и будем таскать объекты, как китайские кули.

Итак, указаные выше события возникают каждое на своем объекте:

на стороне объекта-источника | на стороне объекта-приемника
OLEStartDrop | OLEDragDrop
OLESetData | OLEDragOver
OLEGetFeedback |
OLECompleteDrag |

Вроде как на стороне приемника их меньше, вот и начнем с конца. Создадим новый exe-проект и положим на него Picture1 и Text1. В них мы должны перетащить картинку и текст соответственно из неких сторонних приложений. Таким образом, на нашем проекте имеется только объекты-приемники, а формирование на стороне объекта-источника мы оставим на совести чужого приложения. Сперва мы должны инициализировать наши Picture1 и Text1 как объекты-приемники. Для этого мы используем то же свойство .OLEDropMode, что и при автоматическом перетаскивании, только значение его должно быть в этот раз vbOLEDropManual, т.е. ручное. Сделаем это в процедуре загрузки формы (хотя можно и в окне свойств проекта):

Private Sub Form_Load()
Picture1.OLEDropMode = vbOLEDropManual
Text1.OLEDropMode = vbOLEDropManual
End Sub

Зная, что при окончании перетаскивания на стороне приемника возникает событие _OLEDragDrop, используем его для загрузки картинки в Picture1. Но как же нам передать в нашу процедуру, что за картинка у нас, собственно, перетаскивается? А данные об этом хранятся в объекте DataObject. Прежде всего, этот объект позволяет нам проверить, того ли формата данные мы пытаемся загрузить в приемник (Picture1), так как, например, текст в PictureBox загружать не очень хорошо. Для этого объект DataObject имеет метод GetFormat. Его синтаксис следующий:
объект.GetFormat (format)
где
объект - это наш объект DataObject
format - это константа, указывающая на формат объекта и может принимать следующие значения:

Константа | Значение константы | Описание формата
vbCFText | 1 | Текст (.txt-фйлы)
vbCFBitmap | 2 | Растр (.bmp, .jpg-файлы)
vbCFMetafile | 3 | Метафайл (.wmf-файлы)
vbCFEMetafile | 14 | Улучшеный метафайл (.emf)
vbCFDIB | 8 | Независимые от устройств растровые рисунки (DIB)
vbCFPalette | 9 | Цветовая палитра
vbCFFiles | 15 | Список файлов
vbCFRTF | -16639 | Формат RTF (.rtf файлы)

Поскольку мы работаем с растровыми файлами, то можно использовать константу vbCFBitmap, однако некоторые чужие приложения могут имет другой формат, поэтому для картинок предпочтительнее использовать формат vbCFDIB, хотя это тоже не гарантия, что формат перетаскиваемого объекта будет соответствовать поддерживаемому VB.
Таким образом проверка формата объекта будет выглядеть так:
If Data.GetFormat(vbCFDIB) Then ....

Теперь далее. Если наш формат правильный, то мы можем загрузить картинку, используя метод GetData для все того же объекта DataObject. Метод GetData возвращает данные объекта DataObject в установленном формате. Обратный ему метод SetData устанавливает данные в нужном формате объекта DataObject.
Синтаксис этих методов аналогичен синтаксису метода GetFormat:
объект.GetData (format)
объект.SetData данные,format
где
объект - это наш объект DataObject
данные - это то, что мы будем перетаскивать.
format - это константа, указывающая на формат объекта и может принимать те же значения, что и для метода GetFormat.

Теперь, собственно, загрузка нашей картинки будет выглядеть так:
Picture1.Picture = Data.GetData(vbCFDIB)
Кроме того, мы можем использовать параметр effect процедуры _OLEDragDrop для определения характеристики перетаскивания: копирования, переноса или ничего.
Effect = vbDropEffectCopy 'например для копирования
Но надо сказать, что поскольку пакет данных для перетаскивания готовился в объекте-источнике в неизвестном нам приложении, то толку от использования этого параметра будет мало. Т.е. вообще не будет. Этот параметр сообщает в объект-источник, какое действие было выполнено. Но объект источник-то - в чужом приложении и ему в большей степени наплевать на наш параметр. Этот параметр сработает, если только второе приложение - тоже создавалось нами и для его объекта-источника определены соответствующие процедуры. Но мы все-таки оставим этот оператор для, так сказать, чистоты эксперимента. В результате наша процедура будет такая:

Private Sub Picture1_OLEDragDrop(Data As DataObject, Effect As Long, Button As Integer, Shift As Integer, X As Single, Y As Single)
If Data.GetFormat(vbCFDIB) Then
Picture1.Picture = Data.GetData(vbCFDIB)
Effect = vbDropEffectCopy
End If
End Sub

Как видишь все на самом деле просто. У нас еще осталось неиспользованое событие _OLEDragOver. С его помощью и с помощью параметра Effect мы можем управлять курсором мыши. В зависимости от параметра Effect курсор мыши может иметь следующий вид:

Effect = vbDropEffectNone
Effect = vbDropEffectCopy
Effect = vbDropEffectMove

Для курсора копирования мы можем записать такую процедурку:

Private Sub Picture1_OLEDragOver(Data As DataObject, Effect As Long, Button As Integer, Shift As Integer, X As Single, Y As Single, State As Integer)
Effect = vbDropEffectCopy
End Sub

Надо сказать, что все прочие параметры события _OLEDragOver имеют контрольный характер и нас не интересуют. Хотя, отслеживая значение параметра State мы можем подгружать изображение нужных нам курсоров (если есть желание), как мы делали в прошлой, 18-й главе.
Теперь можно перетаскивать картинку из того же Photoshopa в наш проект. Но, например, перетащить из ACDSee (из окна превьюшек) не удастся из-за несовместимости форматов.
А сейчас давай напишем две аналогичные процедурки для перетаскивания текста в Text1. Суть их совершенно та же. В качестве формата объекта возьмем константу vbCFText.

Private Sub Text1_OLEDragDrop(Data As DataObject, Effect As Long, Button As Integer, Shift As Integer, X As Single, Y As Single)
If Data.GetFormat(vbCFText) Then
Text1.SelText = Data.GetData(vbCFText)
Effect = vbDropEffectCopy
End If
End Sub

Private Sub Text1_OLEDragOver(Data As DataObject, Effect As Long, Button As Integer, Shift As Integer, X As Single, Y As Single, State As Integer)
Effect = vbDropEffectCopy
End Sub

Теперь, если мы выделим в чужом окне, например, хоть окне кода того же Basic'а кусочек строки, то его можно будет перетянуть в Text1. Единственное, что я хочу заметить, мы в качестве свойства Text1 использовали незнакомое нам свойство .SelText. Мы могли бы использовать и свойство .Text, но тогда вставляемый текст просто заменял бы уже существующий. А с использованием свойства .SelText мы можем вставлять фрагмент текста в уже существующий текст в любое место. Подробней это и сопутствующие ему свойства рассмотрены в следующей 20-й главе.

Итак, как видите, ничего сложного, больше пугающих таблиц со свойствами и константами, чем строк кода.
А теперь также радостно попробуем создать на нашей форме объекты-источники и перетащить наши данные в чужое приложение. Сперва мы изменим процедуру Form_Load. В ней мы добавим две строчки для инициализации Picture1 и Text1 как объекты-источники и заполним их данными и будет это выглядеть так:

Private Sub Form_Load()
Picture1.OLEDropMode = vbOLEDropManual
Picture1.OLEDragMode = vbOLEDragManual
Text1.OLEDropMode = vbOLEDropManual
Text1.OLEDragMode = vbOLEDropManual
Picture1 = LoadPicture(App.Path & "\images_07.jpg")
Text1 = "Visual Basic - это не просто просто, а очень просто"
End Sub

Обратите внимание, что я не писал свойство .Text у Text1 и свойство .Picture у объекта Picture1. Дело в том, что это главные свойства этих объектов и понимаются VB по умолчанию (как, например, .Capture у Label и т.д.) и писать их нет никакой необходимости. Так же я буду поступать и далее.

Напишем необходимые процедуры для источника перетаскивания - Text1. Для того, чтобы начать перетаскивание, используется метод OLEDrag. Он не имеет аргументов и просто начинает перетаскивание. Его удобно поставить в процедуру _MouseMove или _MouseDown, которые позволят отследить нажатие левой кнопки мыши (ведь при нажатой левой кнопке мыши мы и осуществляем перетаскивание):

Private Sub Text1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
If Button = vbLeftButton Then 'если нажата левая кнопка мыши
Text1.OLEDrag 'включаем перетаскивание
End If
End Sub

При запуске метода OLEDrag возникает событие _OLEStartDrag, которое позволяет нам сформировать данные для объекта-приемника. Данные эти надо поместить в объект DataObject с помощью метода SetData. Так же, с помощью параметра AllowedEffects мы можем определить порядок перетаскивания (копирование или перемещение):

Private Sub Text1_OLEStartDrag(Data As DataObject, AllowedEffects As Long)
AllowedEffects = vbDropEffectCopy Or vbDropEffectMove
Data.Clear 'очистка DataObject
Data.SetData Text1, vbCFText 'заполнение DataObject данными
End Sub

Обратите внимание, что если нам надо включить в DataObject несколько форматов, это достигается повторным вызовом метода .SetData каждый раз с новым форматом. Поэтому, чтобы гарантировать, что у нас нет ранее вызванных форматов, перед первым вызовом объект DataObject очищается с помощью метода Clear.

Мы можем использовать тот вид курсора, который нам захочется. Сделать это можно в процедуре события _OLEGiveFeedback, в зависимости от значения его параметра Effect. Для этого используется объект Screen и его свойства .MousePointer и .MouseIcon:

Private Sub Text1_OLEGiveFeedback(Effect As Long, DefaultCursors As Boolean)
'убираем курсор по-умолчанию
DefaultCursors = False
'включаем пользовательский курсор
Screen.MousePointer = vbCustom
'в зависимости от параметра Effect подгружаем нужные нам иконки
If Effect = vbDropEffectNone Then
Screen.MouseIcon = LoadPicture(App.Path & "\No.ico")
ElseIf vbDropEffectMove And Effect Then
Screen.MouseIcon = LoadPicture(App.Path & "\Move.ico")
ElseIf vbDropEffectCopy And Effect Then
Screen.MouseIcon = LoadPicture(App.Path & "\Yes.ico")
End If
End Sub

После окончания перетаскивания для объекта-источника возникает событие _OLECompleteDrag. В этой процедуре мы можем очиститьText1, если перетаскивание было перемещением, а не копированием и кроме того, надо обязательно вернуть курсор к виду, который у него был до начала перетаскивания.

Private Sub Text1_OLECompleteDrag(Effect As Long)
'проверка состояния Effect
If vbDropEffectMove And Effect Then Text1 = ""
'восстановление курсора по-умолчанию
Screen.MousePointer = vbDefault
End Sub

Обратите внимание, что проверка константы vbDropEffectMove осуществляется не так, как можно было бы предположить, исходя из здравого смысла, а именно: If Effect = vbDropEffectMove..., а маскируется с помощью логической операции And. В полном виде это выглядело бы так:
If Effect and vbDropEffectMove = vbDropEffectMove, мы в программе использовали сокращенный аналог. Делается это для совместимости с будущими версиями VB, хотя с появлением VB Net это, наверное, неактуально.
Вот в основном и все. Теперь ты можешь перетащить текст из Text1 в другое приложение, например Word. Процедуры для перетаскивания из Picture1 абсолютно аналогичны и приведены в коде примера "OLEDrag.zip", который можно скачать вверху страницы.


Visual Basic с нуля. Глава 20. Объекты Screen и Clipboard.

Глава 20. Объект Screen. Определение разрешения экрана. Объект Clipboard. Работа с системным буфером обмена.
Скачать исходник примера Clipboard
 Дата создания 02.06.2005 {Автор 4us}

В Visual Basic существуют объекты, не отображаемые на форме, но использование которых несложно и может принести некоторую пользу. В прошлых главах мы говорили об объектах Printer, Err и DataObject. Давайте поговорим еще о некоторых.

Объект Screen.

По-сути, объект Screen представляет собой весь экран или иначе Десктоп (DeskTop). Как и другие объекты, объект Screen имеет определенные свойства. Так, например, почему-то считается, что для определения разрешения экрана надо использовать API-функции. Но это прекрасно можно сделать и внутренними возможностями VB. У объекта Screen есть свойства .Height и .Width, которые и возвращают высоту и ширину экрана в твипах. Эти самые твипы легко преобразовать в пиксели, если использовать два других свойства объекта Screen - .TwipsPerPixelX (которое возвращает количество твипов в пикселе по-горизонтали) и .TwipsPerPixelY (которое возвращает количество твипов в пикселе по-вертикали). Таким образом, чтобы определить разрешение экрана, мы пишем приблизительно следующий код:

Private Sub Command1_Click()
'Объявить переменные
Dim VisotaScr As Long 'для высоты
Dim ShirinaScr As Long 'для ширины
VisotaScr = Screen.Height / Screen.TwipsPerPixelX 'узнаем высоту экрана и преобразовываем ее в пиксели
ShirinaScr = Screen.Width / Screen.TwipsPerPixelY 'узнаем ширину экрана и преобразовываем ее в пиксели
Text1 = VisotaScr & " x " & ShirinaScr 'выводим результат в Text1
End Sub

Еще одно свойство .ActiveControl содержит ссылку на элемент управления (и его свойства), имеющий фокус. Смысл использования этого свойства следующий. Например, у тебя на формe три текстбокса. И ты хочешь активный текстбокс очистить. Вот в этом случае и можно использовать свойство .ActiveControl, например

Screen.ActiveControl.Text=""

т. е. очищается тот объект ( в нашем случае TextBox) , на котором в данный момент находится фокус. Это, конечно, несколько упрощено, по-скольку, во-первых, необходимо проверить, а текстбокс ли это? Может у тебя на форме есть другие элементы, например, PictureBox. Во-вторых, реально это свойство можно использовать только в меню, так как меню не получает фокус и активным элементом остается выбранный объект. При использовании, например, свойства .ActiveControl в процедуре CommandButton, при нажатии на кнопку Command1 фокус перейдет с нужного нам элемента на командную кнопку и мы не получим желаемого результата. Ниже, в этой главе, мы на примере работы с объектом Clipboard будем использовать это свойство применительно к меню.

Аналогично свойству .ActiveControl и свойство и .ActiveForm, но содержит ссылку на активную форму. Тогда, чтобы добраться до какого-либо элемента можно применить, например, такой оператор:
Screen.ActiveForm.ActiveControl.Picture

Надо сказать, что объект Screen имеет еще свойства MouseIcon и MousePointer, которые позволяют менять вид курсора, но их использование совершенно аналогично этим свойствам для формы, которые мы анализировали в главе 5. Поэтому я не стану занимать этой дребеденью наше драгоценное время и предлагаю перейти к более нужному и интересному.

Свойства SelLength, SelStart, SelText.

Сейчас мы коротенько поговорим о довольно-таки нужных текстовых свойствах, тем более, что мы будем их использовать далее при работе с объектом Clipboard. Мы уже использовали свойство SelText в прошлой главе, но на этом не остановимся. Если мы выделим кусочек текста в Text, то мы можем получить (или установить) об этом выделенном фрагменте некоторую полезную информацию как раз с помощью этих свойств:

.SelLength — возвращает или устанавливает число выделенных символов. (синтаксис объект.SelLength [= число выбранных символов])
.SelStart — возвращает или устанавливает начало выделенного текста, а если текст не выбран, то указывает положение места вставки. (синтаксис объект.SelStart [= положение начала вставки])
.SelText — возвращает или устанавливает строку, в которой содержится выделенный текст. Если никаких символов не выбрано, то представляет строку нулевой длины ("").(синтаксис объект.SelText [= текст выделения]).

Наиболее часто эти свойства как раз и используются с объектом Clipboard для операций вставки и вырезания. Причем установка .SelLenght меньше нуля приводит к ошибке выполнения. Изменение .SelStart устанавливает .SelLength в значение ноль. Если установить .SelLength больше, чем длина текста, то оно автоматически станет равной длине текста. Установка .SelText в новое значение устанавливает .SelLength в ноль.
Ну вот, теперь можно двигать дальше.

Объект Clipboard.

С системным буфером обмена данных Clipboard мы сталкиваемся постоянно. Именно в него помещаются данные приложения при использовании операций Cut (Вырезать) и Copy (Копировать) и из него берутся при операции Paste (Вставить) в меню Edit, которое есть в практически любой виндусовой программе. Буфер обмена представляет своего рода промежуточное место для хранения данных, которое позволяет, не устанавливая связь между приложениями, тем не менее, переносить данные одного типа из одной программы в другую.
Буфер обмена для всего Windows один. Забрав в буфер данные одного приложения, а затем другого, мы обнаружим, что в буфере хранятся лишь данные последней операции Cut или Copy. Это вызвано тем, что в большинстве случаев, чтобы в буфере не хранилось данных разных приложений, перед загрузкой данных в буфер программа-донор обычно производит его очистку. Посмотреть содержимое буфера можно с помощью утилиты CLIPBRD.EXE, которая обычно имеется в директории C:\Windows\System32 или аналогичной (в зависимости от Windows, установленной на твоем компьютере). В буфер обмена можно забрать и текст и картинку, потому что он поддерживает разные форматы данных.
Из VB доступ к буферу организуется с помощью объекта Clipboard. Методы объекта практически те же, что и разобранные нами в главе 19 с объектом DataObject, однако повторенье - мать ученья (банальность). Они представлены в нижеследующей таблице:

Методы объекта Clipboard
Clipboard.SetText data, format | Помещает текстовые данные в буфер обмена, где data - строка данных, помещаемая в Clipboard | Значение format для текстовых данных может принимать следующие значения: vbCFLink (&HBF00) - Диалоговая информация DDE vbCFRTF (&HBF01) - Текст в формате RTF(Rich Text Format) vbCFText (1) - Текст (Установка по умолчанию)
Clipboard.GetText (format) | Возвращает текстовые данные из буфера обмена. Необязательный. Если ожидаемого формата в буфере нет, то возвращается пустая строка.
Clipboard.SetData data, format | Помещает графические данные в буфер обмена, где data - графическое изображение, помещаемое в Clipboard. Устанавливается функцией LoadPicture либо свойствами Picture. | Значение format для графических данных может принимать следующие значения: vbCFBitmap (2) - Растр (.bmp-файлы) vbCFMetafile (3) - Метафайл (.wmf-файлы) vbCFDIB (8) - Независимые от устройств растровые рисунки (DIB) vbCFPalette (9) - Цветовая палитра vbCFEMetafile (14) - Расширенный метафайл (.emf-файлы) vbCFFiles (15) - Список имен файлов (Microsoft Windows Explorer)
Clipboard.GetData (format) | Возвращает графические данные из буфера обмена. Если значение format опущено, то подбирается подходящий формат. Если графический формат не соответствует ожидаемому, то ничего не возвращается.
Clipboard.GetFormat (format) | Возвращает логические True или False, в зависимости от содержащихся в буфере данных. Для проверки более чем одного метод вызывается повторно. | Значение format может принимать следующие значения: vbCFLink (&HBF00) - Диалоговая информация DDE vbCFText (1) - Текст vbCFBitmap (2) - Растр (.bmp-файлы) vbCFMetafile (3) - Метафайл (.wmf-файлы) vbCFDIB (8) - Независимые от устройств растровые рисунки (DIB) vbCFPalette (9) - Цветовая палитра vbCFEMetafile (14) - Расширенный метафайл (.emf-файлы) vbCFFiles (15) - Список имен файлов (Microsoft Windows Explorer)
Clipboard.Clear | Очищает содержание системного буфера обмена. |

Использовать эти методы очень просто. Для примера создадим новый exe-проект и положим на него два TextBox'а и два PictureBox'а. В качестве управления нашими процессами с помощью Редактора меню создадим простое меню Edit. Подробно о создании меню написано в Главе 9.
Имя нашего меню будет mnuEdit, а строками
mnuCut - Вырезать
mnuCopy - Копировать
mnuPaste - Вставить
mnuDelate - Удалить
Вообще-то у TextBox'а есть свое контекстное меню, но мы сделаем свое общее.

Ну сперва в процедуре Form_Load загрузим в Text1 и Picture1 данные, чтобы нам было с чем работать. В примере я использовал маленькую картинку P00027.jpg. (Кстати поставь свойства TextBox'ов Multiline=True)

Option Explicit 'хоть у нас и не будет ни одной переменной, но дело принципа
Private Sub Form_Load()
Text1 = "Проще Visual Basic ничего нету"
Picture1 = LoadPicture(App.Path & "\P00027.jpg")
End Sub

Начнем с процедуры для первого пункта подменю "Вырезать" - mnuCut, которая должна копировать данные в буфер и затем очищать объект (Text или Picture):

Private Sub mnuCut_Click()

Сперва нам надо очистить буфер, чтобы в нем не осталась другая информация. Используя метод .Clear это предельно просто.

Clipboard.Clear

Далее мы будем использовать свойство .ActiveControl объекта Screen. С его помощью мы легко затащим в буфер содержимое объекта, которое в настоящий момент активно, т.е. по которой мы щелкнули мышью. Но сперва нам надо определить, а какой это объект, ведь для текста используется метод .SetText, а для графики - .SetData. Здесь мы снова, как и в Главе 18 используем ключевое слово TypeOf, которое поможет определить тип элемента управления, а значит и тип данных.

'итак, если это текст
If TypeOf Screen.ActiveControl Is TextBox Then
'забираем выделенный текст в буфер
Clipboard.SetText Screen.ActiveControl.SelText
'а затем удаляем этот выделенный текст из TextBox'а
Screen.ActiveControl.SelText = ""
'а теперь пишем ветвь, если это картинка
ElseIf TypeOf Screen.ActiveControl Is PictureBox Then
'забираем картинку из активного элемента в буфер
Clipboard.SetData Screen.ActiveControl.Picture
'и удаляем картинку
Screen.ActiveControl.Picture = LoadPicture()
End If
End Sub

Готово, наши данные в буфере обмена. Если у тебя открыт CLIPBRD.EXE, то ты можешь видеть, что творится в буфере. Теперь напишем процедуру для копирования mnuCopy. Единственное, чем она отличается от вырезания, так это тем, что мы не очищаем Text или Picture от копируемых данных.

Private Sub mnuCopy_Click()
'Очищаем буфер
Clipboard.Clear
'проверяем тип данных
'если если это текст
If TypeOf Screen.ActiveControl Is TextBox Then
'забор текста в буфер
Clipboard.SetText Screen.ActiveControl.SelText
'если это картинка
ElseIf TypeOf Screen.ActiveControl Is PictureBox Then
'забор картинки в буфер
Clipboard.SetData Screen.ActiveControl.Picture
End If
End Sub

Теперь нам хорошо-бы из буфера вставить данные в элемент управления. Для этого пишем процедуру Вставить - mnuPaste.

Private Sub mnuPaste_Click()
'Ну сперва, конечно, проверяем тип, куда мы хотим запихнуть содержимое буфера и если это текст
If TypeOf Screen.ActiveControl Is TextBox Then
'но все равно сразу не пихаем. А вдруг даные были забраны из PictureBox, поэтому мы еще и проверяем
'с помощью метода GetFormat, а текстовой ли тип в буфере
If Clipboard.GetFormat(vbCFText) Then
'вот если да, то тогда уж вставляем без сомнения
Screen.ActiveControl.SelText = Clipboard.GetText
End If
'а теперь проверка для графических данных
ElseIf TypeOf Screen.ActiveControl Is PictureBox Then
'и здесь мы проверяем тип данных буфера
If Clipboard.GetFormat(vbCFDIB) Then
'и если совпадает, то вставляем
Screen.ActiveControl.Picture = Clipboard.GetData
End If
End If
End Sub

Ну и для пущего сходства с приложениями Windows напишем еще процедурку для удаления данных - mnuDelate. Здесь все предельно просто, Мы ведь в буфер ничего на забираем, а проверяем наш активный элемент и очищаем его тем или иным методом. Кстати, пардон, за ошибку в слове mnuDelate (я просто не люблю это слово), но уж исправлять поздно, она в исходнике примера.

Private Sub mnuDelate_Click()
'проверяем тип данных
'если это текст
If TypeOf Screen.ActiveControl Is TextBox Then
'удаление выделенного текста
Screen.ActiveControl.SelText = ""
ElseIf TypeOf Screen.ActiveControl Is PictureBox Then
'удаление картинки
Screen.ActiveControl.Picture = LoadPicture()
End If
End Sub

Казалось бы, вот оно счастье, рядом, но фик, дело не закончено. Если ты обратил внимание на фирменные программы, то не всегда все строки меню редактирования доступны юзеру. Например, если нет выделения в тексте, то и нечего копировать или вырезать. Или наоборот, в буфере нет данных? Поэтому в определенные моменты доступ к определенным строкам меню надо перекрыть! Для решения этой проблемы надо написать небольшую, но ответственную процедуру. И используем мы для этого самый верхний, казалось бы совершенно ненужный пункт меню mnuEdit. Именно по клику юзера по этой строчке нам будет очень удобно проверять, что можно делать из наших процедур, а что нельзя.

Private Sub mnuEdit_Click()

Сначала обязательно проверяем, на каком элементе фокус.

If TypeOf Screen.ActiveControl Is TextBox Then

В случае, если из текста ничего не выделено, мы должны сделать недоступными строки "Вырезать", "Копировать" и "Удалить". Для этого надо ее свойству .Enabled присвоить значение False. Если ничего не выделено, значит длина выделения (свойство .SelLength) равна нулю. Это можно было бы записать приблизительно так:

If Screen.ActiveControl.SelLength=0 Then mnuCut.Enabled = False

Однако, для краткости записи мы можем напрямую присвоить свойству .Enabled значение длины выделения .SelLength, исходя из того, что значение ноль всегда соответствует для логических типов False, а любое положительное - True. В итоге то мы получаем более короткую, но по смыслу абсолютно такую же запись. Кроме того, при этом, нам не надо каждый раз в начале процедуры восстанавливать доступность строк, так как длина строки больше нуля сама переведет .Enabled в состояние True.

mnuCut.Enabled = Screen.ActiveControl.SelLength

аналогично две другие строки

mnuCopy.Enabled = Screen.ActiveControl.SelLength
mnuDelate.Enabled = Screen.ActiveControl.SelLength

Далее мы должны запретить Вставить, если формат данных в буфере не соответствует требуемому. Здесь даже еще проще. Метод .GetFormat возвращает True, если формат соответствет проверяемому и False - если нет. Именно эти значения мы напрямую и присваиваем для свойства .Enabled строки mnuPaste.

mnuPaste.Enabled = Clipboard.GetFormat(vbCFText)

Абсолютно также мы поступаем с веткой для графических данных

ElseIf TypeOf Screen.ActiveControl Is PictureBox Then
mnuCut.Enabled = Screen.ActiveControl.Picture
mnuCopy.Enabled = Screen.ActiveControl.Picture
mnuDelate.Enabled = Screen.ActiveControl.Picture
mnuPaste.Enabled = Clipboard.GetFormat(vbCFDIB)
End If
End Sub

Ну вот мы и сделали это. Исходник примера можно скачать вверху страницы.


Visual Basic с нуля. Перехват и обработка ошибок.

Глава 11. Перехват и обработка ошибок. Операторы On Error и Resume. Объект Err. Оператор GoTo.
Скачать исходник программы Error
 Дата создания 11.01.2005 {Автор 4us}

Как бы правильно не был бы написан код программы и как бы безошибочно, казалось, программа бы не работала, всегда есть возможность для возникновения непредвиденных ситуаций, когда VB не может выполнить корректно какой-нибудь оператор, что приводит к аварийному завершению программы (фатальный крах). При этом возможна еще и потеря данных, если в это время происходило, например, копирование файла или передача информации. В большинстве своем это ошибки вызваны внешними событиями, например пустой дисковод, когда твоя программа обращается к дискете, отсутствие того файла, который ты пытаешься открыть для, например, чтения, а также ошибки переполнения (когда не хватает памяти для рисунков и ли печати), сетевые сбои, проблемы с буфером обмена и много чего еще. Скажу сразу, что предусмотреть все возможные ситуации практически невозможно, особенно если твоим продуктом будет пользоваться сторонний юзер. Юзера крайне изобретательны. Я не раз сталкивался с возниковением таких ситуаций, спровоцированных юзером, которые у меня, например, в голове не укладываются, и которые потом смоделировать практически невозможно. Видимо по какому-то озарению свыше, юзер может добиться, таких катастрофических результатов, которых я достигнуть при всей внутренней мобилизации сил своего хилого организма не в состоянии. Если поставить перед собой цель создать программу, всегда работающую корректно, можно раздуть код этой программы до безумных размеров и все равно полного успеха не добиться. Microsoft, например, не добился.

Хотя и не все ошибки VB может перехватывать (их полный список можно найти здесь), однако избегать стандартных ошибок, обрабатывать явно возможные ошибки мы просто обязаны. Мне представляется, что для исключения фатальных сбоев надо придерживаться следующей стратегии.

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

Во-вторых, надо стараться избегать возникновения ошибки программно, тогда ее не придется отслеживать и обрабатывать. Ярким примером такой ситуации может служить программа Scandisk, которую мы рассматривали в прошлой, 10-ой главе. Когда мы тыкали мышью в Dir1 по директориям, все было нормально, при этом выполнялся следующий оператор:
FileName = Dir(OurDir & "\*.*", 0) 'присваиваем переменной значение функции Dir для всех файлов
Если же мы кликали по верхней строке с именем не директории, а диска, возникала ошибка 52 "Неправильное имя файла, или номер". А дело то всего лишь в слеже, который мы поставили в операторе. Дело в том, что функция Dir возвращает имя диска со слежом в конце строки, а имя директории - нет. Если мы его уберем и оператор будет выглядеть так
FileName = Dir(OurDir & "*.*", 0) 'присваиваем переменной значение функции Dir для всех файлов
вот тогда на диске оператор сработает правильно, зато перестанет читать директории. Можно конечно обработать эту ошибку, о чем мы будем говорить ниже, по ее номеру, но тогда в трех нижних операторах
Attr = GetAttr(OurDir & "/" & FileName)...
той же причиной будет также вызвана ошибка, но с другим номером - 75 "Ошибка доступа пути или файла". Тогда придется обрабатывать и ее. Это тоже вариант. Но по-моему логичнее сразу записать в переменную Ourdir правильный путь при любом раскладе. И надо-то для этого всего лишь убрать слеж из четырех операторов, где возникает ошибка, а в процедуру Dir1_Change добавить проверку, которая добавляет слеж при его отсутствии:
If Right(OurDir, 1) <> "\" Then OurDir = OurDir & "\" 'добавленный оператор для устранения ошибки
вот и все дела. Полный исправленный код, если что не понятно, можно скачать здесь.

В-третьих, при обращении к внешним источникам, в том числе к файлам, для получения или вывода информации надо предполагать, что возможно они будут недоступны. Кроме того, надо подумать, какие еще фатальные ситуации могут возникнуть. Вот тут уже надо обрабатывать ошибки конкретно.
Давайте создадим новый exe-проект. По мере необходимости на форму будем класть командные кнопки и в их процедуре отлавливать ошибки. Возьмем пример самой распространенной ошибки - нет того файла, который нам как раз крайне необходим. Положим на форму кнопку Command1 (она будет называться Resume Next) и в ее процедуре напишем операторы открытия файла "aaa.txt" для последовательного чтения.

Option Explicit

Private Sub Command1_Click()
Open "aaa.txt" For Input As #1
Close #1
End Sub

Поскольку такого файла никто не создавал, то при запуске программы и нажатии кнопки Command1 возникнет ошибка 53, о чем нам любезно сообщит Windows: "Run-time error '53': Файл не найден". Программа аварийно завершится.
Итак, что же нам может предложить VB для предотвращения фатальных ошибок.

Операторы ON ERROR и RESUME (Resume Next).

Перво-наперво, ошибку надо отловить. Сделать это можно с помощью оператора On Error. Однако сам по себе этот оператор работать не будет, это и логично, с отловленной ошибкой ведь надо что-то делать. Самое простой и безбашенный метод (кстати довольно эффективный, когда постоянно возникает ошибка, справиться с ней не можешь и это тебя достало) - присобачить к оператору On Error оператор Resume Next. Тогда получившийся оператор On Error Resume Next в случае возникновения ошибки возвратится в программу и передаст управление оператору, следующему за тем оператором, где возникла ошибка. Однако следует помнить, что On Error Resume Next действует во всей процедуре, и если процедура большая и сложная, то ошибка может возникнуть совсем в другом операторе, нежели ты ожидаешь. Она все равно будет обработана, VB перескочит на следующий оператор, а ты об этом ничего не узнаешь и будешь дня два ломать голову, почему не работает то-то и то-то, хотя все прописано в коде черным по-белому. Ну, тем не менее исправим наш код с использованием оператора On Error:

Private Sub Command1_Click()
On Error Resume Next
Open "aaa.txt" For Input As #1
Close #1
Form1.Print "Процедура кое-как завершена"
Form1.Print "с помощью On Error Resume Next"
Form1.Print "файл не считан, работать не с чем"
End Sub

Текст в операторах Form1.Print написан условно, так как ясно, что он будет выводится независимо от того, есть ошибка или нет. Т.е на самом деле пользователь никакой реальной информации об ошибке не получит и поправить ничего не сможет. Иное дело, если мы к нашему On Error попробуем пристроить оператор безусловного перехода GoTo метка. На самом деле он унаследован от старого Бейсика и использовать его нужно крайне осторожно. Этот оператор отсылает ( в нашем случае, если возникла ошибка) к строке, помеченной меткой, но только внутри текущей процедуры. Положим следующую кнопку Command2 (ее назание GoTo метка) и в ней напишем процедуру

Private Sub Command2_Click()
Dim Msg As String
On Error GoTo ErrorMark
Open "aaa.txt" For Input As #1
Close #1
Exit Sub
ErrorMark:
Msg = "Ой, наверно файл куда-то запропал, попробуйте восстановить!?"
MsgBox Msg, , "Караул, нет данных!"
End Sub

Таким образом, при возникновении ошибки оператор GoTo возвращает нас в программу к строке, помеченной меткой ErrorMark. Под ней пишутся операторы, которые выполняются для реакции программы (или юзера) на ошибку. Обратите внимание, чтобы они не выполнялись в случае, когда ошибки нет, перед меткой поставлен оператор Exit Sub, который прерывает выполнение процедуры, не дожидаясь оператора End Sub. В качестве реакции на ошибку у нас выводится MessageBox с советом для юзера. Обратите снова Ваше драгоценное внимание на то, что в сообщении я употребил слово "наверное". Реально при открытии файла могут возникать разные ошибки и я лишь только предполагаю, что у нас ошибка 53.
Как я говорил выше, в сложных и больших процедурах, чтобы не отлавливать ошибки в других операторах оператор GoTo метка. надо выключить. Для этого, после сомнительного оператора, могущего вызвать ошибку, надо вставить строку
On Error GoTo 0
Тогда обработчик ошибок прекратит свою работу и ошибки в последующих операторах отслеживаться не будут.
Если в подпрограмме обработки ошибок имеется возможность устранить причину ее возникновения, то надо вернуться на тот же оператор и снова его выполнить, уже без ошибки. Для этого подпрограмму обработки ошибок заканчивают оператором Resume. Если же надо перейти к другой строке, помеченной меткой, то программу обработки ошибок надо закончить оператором Resume метка. Ну а теперь перейдем к самому интересному - анализу ошибок.

Объект Err.
Вот как раз с помощью этого объекта мы можем проанализировать, какая ошибка произошла, т.е. по сути, получить ее номер, и в зависимости от ситуации программно решить вопрос об ее устранении или наоборот, заставить юзера производить какие-либо судоржные действия.
Основным свойством объекта Err является свойство .Number, которое и возвращает нам номер ошибки. Настолько основным, что .Number можно не писать. Синтаксис будет такой:
Переменная=Err.Number или Переменная=Err
Это свойство для нас главное. Кроме того (для сведения) имеются еще такие свойства:
.Sourse - имя текущего проекта VB
. Description - cтрока, соответствующая строке, возвращаемой функцией Error для кода ошибки, указанного в свойстве Number, если такая строка существует
.HelpFile - полное имя (включая диск и путь) файла справки Visual Basic.
. HelpContext - контекстный идентификатор файла справки Visual Basic, соответствующий ошибке с кодом, указанным в свойстве Number.
. LastDLLError - содержит системный код ошибки для последнего вызова DLL (только для 32-разрядных Microsoft Windows)

Положим на форму Text1 со свойством Multiline - True, кнопку Command3 (название - Прочитать файл), а также для того, чтоб нам было удобно создавать, убивать файл, положим еще три кнопки Command4, Command5 и Command6, называющиеся соответственно "Создать aaa.txt", "Создать пустой aaa.txt" и "Убить aaa.txt". В процедурах этих трех кнопок напишем:
код для создания файла путем открытия и записи туда слов "не пустой" в Command4

Private Sub Command4_Click()
'создать aaa.txt
On Error Resume Next
Kill (App.Path & "\aaa.txt")
Open App.Path & "\aaa.txt" For Output As #1
Print #1, "не пустой"
Close #1
Text1.Text = "Создан не пустой файл aaa.txt"
End Sub

в Command5 создадим пустой файл, т.е. файл не содержащий ни одного символа

Private Sub Command5_Click()
'создать пустой aaa.txt
On Error Resume Next
Kill (App.Path & "\aaa.txt")
Open App.Path & "\aaa.txt" For Output As #1
Close #1
Text1.Text = "Создан пустой файл aaa.txt"
End Sub

и в Command6 - программу для уничтожения файла

Private Sub Command6_Click()
'убить aaa.txt
On Error Resume Next
Kill (App.Path & "\aaa.txt")
Text1.Text = "Убит файл aaa.txt"
End Sub

Обратите внимание, что перед созданием файла, я убиваю старый. Так как старый файл может на тот момент не существовать (в этом случае возникнет ошибка), я использую On Error Resume Next.
Теперь создадим коротенькую процедуру загрузки формы для проверки в самом начале, существует ли файл aaa.txt

Private Sub Form_Load()
Text1.Text = ""
If Dir(App.Path & "\aaa.txt") = "" Then Text1.Text = "Файла aaa.txt не существует"
End Sub

А вот теперь в кнопке Command3 напишем полноразмерный обработчик ошибок.

Private Sub Command3_Click()
'объявляем переменную для считывания данных из файл а
Dim Stroka As String
'использование объекта Err при обработке ошибок
On Error GoTo ErrorMark
ForOpen:
Open App.Path & "\aaa.txt" For Input As #1
Line Input #1, Stroka
Text1.Text = Text1.Text & "Файл успешно открыт, содержание файла: " & Stroka & vbCrLf
Close #1
Exit Sub
ErrorMark:
Close #1
'выводим возможные данные об ошибке
Text1.Text = "Возникла ошибка # " & Err.Number & vbCrLf
Text1.Text = Text1.Text & Err.Description & vbCrLf
Text1.Text = Text1.Text & "В " & Err.Source & vbCrLf
Text1.Text = Text1.Text & "Полный путь хелпа " & Err.HelpFile & vbCrLf
Text1.Text = Text1.Text & "Идентификатор файла спраки VB " & Err.HelpContext & vbCrLf
Text1.Text = Text1.Text & "DLL " & Err.LastDllError & vbCrLf
'Ecли файла нет, то создем его
If Err = 53 Then
Open App.Path & "\aaa.txt" For Output As #1
Print #1, " "
Close #1
Text1.Text = Text1.Text & "Создан новый файл aaa.txt" & vbCrLf
'возвращаемся на исходную позицию
Resume ForOpen
'если файл пустой дописываем в него пробел
ElseIf Err = 62 Then
Open App.Path & "\aaa.txt" For Append As #1
Print #1, " "
Close #1
Text1.Text = Text1.Text & "Ошибка файла aaa.txt исправлена " & vbCrLf
'возвращаемся на исходную позицию
Resume ForOpen
Else
Text1.Text = Text1.Text & "Не знаю, что делать с ошибкой, файл не открыт "
End If
End Sub

Теперь, если мы убъем файл кнопкой "Убить aaa.txt" и нажмем кнопку "Прочитать файл", то в Text1 выведется вся доступная информация об ошибке, создастся новый файл и процедура начнет выполнятся по-новой уже с существующим файлом. Аналогичная ситуация возникает при создании пустого файла. При чтении такого файла возникает ошибка 62 - чтение после конца файла. Мы также получаем информацию об ошибке, дописываем в файл пробел и по Resume вновь запускаем процедуру. Если возникает ошибка с номером, который мы не знаем, то просто выводим об этом сообщения в Text1.

Замечу, что с операторами Open и Close надо обращаться аккуратно. Все файлы должны своевременно открываться и закрываться. Если путаешься с работой с файлами, посмотри Главу 7. Скачать исходник можешь вверху страницы.

Резюме:
Таким образом, обработка ошибки включает в себя:
- Обнаружение ошибки On Error.
- Переход на подпрограмму обработки GoTo (или, на крайняк, на следующий оператор с помощью Resume Next).
- Определение ошибки Err.Number и в зависимости от этого выполнение каких-то действий.
- Возврат Resume после устранения ошибки на оператор, вызвавший ошибку.

Для очистки совести скажу еще про функцию Error. Она по номеру ошибки выводит пояснение - что это за ошибка. Синтаксис:
Строковая переменная=Error(номер ошибки)
Если хотите, положите на форму последнюю кнопку Command7 и для вывода пояснений к ошибкам, например с 52 по 62 напишите такой код и посмотрите, что получится.

Private Sub
  Command7_Click()
Dim ErrorNumber As Long
Text1.Text = ""
For ErrorNumber = 52 To
  64
Text1.Text = Text1.Text & Error(ErrorNumber) & vbCrLf
Next ErrorNumber
End Sub

Совершенно ничего сложного. Ну-с с этим все.


Visual Basic с нуля. Справочник. Коды перехватываемых ошибок.

Справочник. Коды перехватываемых ошибок.
По материалам Help VB

Перехватываемые ошибки возникают при выполнении приложения. Некоторые из них возникают также в процессе разработки приложения или во время компиляции. Для отладки и задания отклика на возникновение перехватываемых ошибок предназначены инструкция On Error и объект Err. Неиспользованные коды ошибок в диапазоне от 1 до 1000 зарезервированы для будущего применения в Visual Basic.

Код Сообщение
3 Инструкция Return без GoSub
5 Недопустимый вызов процедуры
6 Переполнение
7 Не хватает памяти
9 Индекс выходит за пределы допустимого диапазона
10 Массив имеет фиксированную длину или временно блокирован
11 Деление на 0
13 Несоответствие типа
14 Не хватает памяти для строки
16 Слишком сложное выражение
17 Невозможно выполнение требуемой операции
18 Произошло прерывание, вызванное пользователем
20 Инструкция Resume не в программе обработки ошибок
28 Не хватает стековой памяти
35 Процедура Sub, Function или Property не определена
47 Слишком много клиентов библиотеки приложения
48 Ошибка при загрузке библиотеки
49 Неверное соглашение о вызовах библиотеки
51 Внутренняя ошибка
52 Неверное имя или номер файла
53 Файл не найден
54 Неверный режим файла
55 Файл уже открыт
57 Ошибка устройства ввода/вывода
58 Файл уже существует
59 Неверная длина записи
61 Переполнение диска
62 Ввод за пределами конца файла
63 Неверный номер записи
67 Слишком много файлов
68 Устройство недоступно
70 Нет разрешения
71 Диск не готов
74 Невозможно переименование с другим именем диска
75 Ошибка доступа к файлу/каталогу
76 Путь не найден
91 Не задана объектная переменная или переменная блока With
92 Цикл For не инициализирован
93 Недопустимая строка шаблона
94 Недопустимое использование Null
97 Невозможен вызов процедуры Friend для объекта, не являющегося экземпляром определяющего класса
298 Системная библиотека DLL не загружается
320 Нельзя использовать имена символьных устройств в именах данных файлов
321 Недопустимый формат файла
322 Невозможно создание требуемого временного файла
325 Недопустимый формат файла ресурсов
327 Не найдено значение данных по имени
328 Недопустимый параметр. Невозможна запись массивов
335 Невозможен доступ к системному реестру
336 Компонент ActiveX неправильно зарегистрирован
337 Компонент ActiveX не найден
338 Компонент ActiveX выполняется неправильно
360 Объект уже загружен
361 Невозможно загрузить или выгрузить данный объект
363 Указанный элемент ActiveX не найден
364 Объект выгружен
365 Невозможна выгрузка в данном контексте
368 Истек срок действия данного файла. Программе требуется файл более новой версии
371 Данный объект не может использоваться в качестве формы-владельца метода Show
380 Недопустимое значение свойства
381 Недопустимый индекс массива свойств
382 Инструкция Property Set не поддерживается во время выполнения
383 Инструкция Property Set не поддерживается (свойство доступно только для чтения)
385 Требуется индекс массива свойств
387 Инструкция Property Set не разрешена
393 Инструкция Property Get не поддерживается во время выполнения
394 Инструкция Property Get не поддерживается (свойство доступно только для записи)
400 Форма уже отображается; модальное отображение невозможно
402 Сначала необходимо закрыть самую верхнюю модальную форму
419 Нет разрешения на использование объекта
422 Свойство не найдено
423 Не найдено свойство или метод
424 Требуется объект
425 Недопустимое использование объекта
429 Невозможно создание объекта или возвращение ссылки на этот объект компонентом ActiveX
430 Класс не поддерживает программирование объектов
432 Не найдено имя файла или класса при программирования объектов
438 Объект не поддерживает это свойство или метод
440 Ошибка программирования объектов
442 Потеряна связь с библиотекой типов или библиотекой объектов для удаленного доступа
443 Программируемый объект не имеет значения по умолчанию
445 Объект не поддерживает эту команду
446 Объект не поддерживает именованные аргументы
447 Объект не поддерживает текущую национальную настройку
448 Именованный аргумент не найден
449 Аргумент является обязательным или неправильно присвоено значение свойства
450 Недопустимое число аргументов или неправильное присвоение значения свойства
451 Объект не является семейством
452 Недопустимый порядковый номер
453 Указанная функция библиотеки DLL не найдена
454 Программный ресурс не найден
455 Ошибка блокировки программного ресурса
457 Запись уже связана с элементом данного семейства
458 Переменная использует не поддерживаемый в Visual Basic тип
459 Компонент не поддерживает события
460 Недопустимый формат буфера обмена
461 Указанный формат не соответствует формату данных
480 Невозможно создание автоматически обновляемого изображения
481 Недопустимый рисунок
482 Ошибка принтера
483 Драйвер принтера не поддерживает данное свойство
484 Ошибка получения системной информации о принтере. Убедитесь, что принтер установлен правильно
485 Недопустимый тип рисунка
486 Невозможно напечатать изображение формы на принтере данного типа
520 Невозможна очистка буфера обмена
521 Невозможно открыть буфер обмена
735 Невозможно сохранить файл в каталог TEMP
744 Образец поиска не найден
746 Слишком длинная замена
31001 Не хватает памяти
31004 Отсутствует объект
31018 Класс не определен
31027 Невозможно активизировать объект
31032 Невозможно создать внедренный объект
31036 Ошибка сохранения в файл
31037 Ошибка загрузки из файла


Visual Basic с нуля. Справочник. Типы данных.

Справочник. Типы данных, поддерживаемые VB.
По мотивам Help VB

Название типа данных
Размер (Бт)
Диапазон
Объявление и
представление
Byte байт
2
 От 0 до 255 Dim Chislo As Byte
Boolean логический
2
 True или False Dim Chislo As Boolean
Integer целое
4
 От -32 768 до 32 767 Dim Chislo As Integer
Dim Chislo%
Long
         длинное целое
4
 От -2 147 483 648
до 2 147 483 647 		Dim Chislo As Long
Dim Chislo&
Single
с плавающей точкой одинарной точности
4
 От -3,402823E38
до 3,402823E38 		Dim Chislo As Single
Dim Chislo!
Double

с плавающей точкой двойной точности
8
 От -1,79769313486232E308
до 1,79769313486232E308 		Dim Chislo As Double
Dim Chislo#
Currency
денежный
8
 От -922 337 203 685 477,5808
до 922 337 203 685 477,5807 		Dim Chislo As Currency
Dim Chislo@
Decimal
масштабируемое целое
14
 +/-79 228 162 514 264 337 593 543 950 335
без дробной части;
+/-7,9228162514264337593543950335
с 28 знаками справа от запятой; 		Dim Chislo As Decimal
Date дата и время
8
 От 1 января 100 г. до 31 декабря 9999 г. Dim Chislo As Date
Object объект
4
 Любой указатель объекта Dim Chislo As Object
String
строка
10 байт + число символов*1 байт для строк переменной длины и
число символов*1 байт для строк постоянной длины
 От 0 до приблизительно 2 млрд для строк переменной длины
и от 1 до приблизительно 65400 для cтрок постоянной длины 		Dim Chislo As String
Dim Chislo$
Variant
         все типы данных
16 байт для чисел и
22 байт + число символов*1 байт для символов
 От 0 до приблизительно 2 млрд Dim Chislo
Dim Chislo As Variant
Пользовательский тип данных описывается с помощью ключевого слова Type
определяется включаемыми элементами
 Определяется диапазон каждого элемента, в зависимости от его типа данных Type MyType
MyName As String
MyBirthDate As Date
MyHeight As Integer
End Type

Dim MyData As MyType

Copyright © 2005 4us