В начало
Статьи
Библиотека
Разное

Вот здесь - новый сайт, заходите немедленно!

kift - Коллекция Интересных Фактов и Теорий

А тут можно поболтать и побухтеть, милости просим:

kift - неизданное

Самоучитель Visual Basic

15. Основы отладки программ. Пользовательская процедура

14. ЦиклыVB.

В этом разделе Вы узнаете о циклах языка и научитесь с ними работать.

Цикл For Each... Next

Способ проверить, не существует ли уже объект коллекции – последовательный перебор всех членов набора и проверка условия.

  • Перебор коллекции производится с помощью конструкции For Each (Для Каждого):

For Each <член_коллекции> In <коллекция>
	<некоторые действия>
Next <член коллекции>
  • В такой конструкции последовательно выбирается каждый <член_коллекции>, принадлежащий к <коллекции>, с ним производятся <некоторые действия>, а затем действия повторяются для следующего <члена_коллекции> до тех пор, пока не будут задействованы все элементы набора.

Для нашего случая это будет выглядеть так:

For Each NewSheet In colSheets
	<некоторые действия>
Next NewSheet

Преимущество такого подхода – нет нужды знать, сколько именно форм-заметок нами открыто, обработаны будут все.

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

  • Как уже говорилось, заголовок формы-странички – уникальное значение. При выборе записи на frmDiary текущее значение поля “ Dates” определяет заголовок вновь создаваемой формы.

  • Значит, следует проверить, не совпадает ли текущее значение поля “ Dates” с заголовком одной из созданных форм-страничек, и если да – вывести эту форму-страничку на передний план:

Вот как это делается:

For Each NewSheet In colSheets
If NewSheet.Caption = datPrimaryRS.Recordset("Dates") Then
NewSheet.ZOrder
Exit Sub
End If
Next NewSheet

Строка Exit Sub – для выхода из процедуры, если существующая форма-страничка выведена на передний план. Если опустить эту строку, то будет создаваться новый экземпляр уже существующей странички, что приведет к ошибке.

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

Добавьте код для игнорирования ошибок сами.

Теперь обработчик события нажатия на кнопку приобретет вид:

Private Sub cmdSheet_Click()
On Error Resume Next
For Each NewSheet In colSheets
 If NewSheet.Caption = datPrimaryRS.Recordset("Dates") Then
 NewSheet.ZOrder
 Exit Sub
 End If
Next NewSheet
Set NewSheet = New frmSheet
NewSheet.Caption = datPrimaryRS.Recordset("Dates")
NewSheet.txtSheet.Text = datPrimaryRS.Recordset("Note")
colSheets.Add NewSheet, NewSheet.Caption
End Sub

Следует пояснить, что же здесь получилось.

  • Если при переборе набора страничек искомая найдена – она выводится на первый план и происходит выход из процедуры.

  • Если же выход из процедуры не произошел – значит, искомой        формы нет! В этом коде имитируется проверка условий подобного рода:

If <форма найдена> Then <выход>

.

.

.

If <из процедуры не вышли, значит, форма не найдена> Then <продолжаем>

  • Для успеха подобного приема следует включить игнорирование ошибок.

Форма frmDiary готова.

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

  • Здесь мы будем выполнять задачу, обратную той, что делали при создании формы-странички, а именно – по идентификатору (заголовку) странички будем искать соответствующую ей запись БД.

  • Делается это последовательным перебором всех записей БД и сравниванием их с образцом (заголовком странички). Но записи БД не входят в коллекцию, и цикл For Each здесь не подойдет. К счастью, в VB есть несколько других видов циклов, в том числе и подходящих для нашей задачи.

Язык VB различает циклы For ... Next (частный случай – цикл For Each) и циклы Do... Loop.

Цикл For... Next

  • Цикл For... Next (Для... Следующий) имеет общую структуру:

For index = 0 to counter
	<действия в цикле>
Next index
  • Здесь index – переменная, которой присваивается начальное значение (в нашем случае 0), после выполнения неких действий значение переменной index увеличивается (автоматически происходит увеличение на 1) и цикл повторяется до тех пор, пока index не станет равен counter. Как видно, в общем случае количество циклов должно быть известно до начала выполнения.

ЦиклDo... Loop

Если же число проходов цикла заранее не известно, предпочтительнее цикл Do... Loop (Делай... Цикл).

Общая структура его такова:

Do
	<некие действия>
Loop
  • Такой цикл будет повторяться до тех пор, пока не будет произведен принудительный выход (с помощью строки Exit Do).

Разновидности цикла Do... Loop:

Do Until <условие>
	<действия>
Loop
  • Цикл будет происходить, пока не выполнится <условие>, причем проверка происходит в самом начале, и, если <условие> заранее выполняется, не будет ни одного прохода.

Do
	<действия>
Loop Until <условие >
  • Цикл будет происходить, пока не выполнится <условие>, но, так как проверка в конце цикла, то цикл будет выполнен минимум один раз.

Do While <условие>
	<действия>
Loop
  • Цикл будет происходить, пока <условие>не перестанет выполняться, причем проверка происходит в самом начале, и, если <условие> заранее не выполняется, не будет ни одного прохода.

Do
	<действия>
Loop While <условие>
  • Цикл будет происходить, пока не перестанет выполняться <условие>, но, т.к. проверка в конце цикла, то цикл будет выполнен минимум один раз.

Так вот, для нашей программы следует «пролистать» всю БД от начала до конца. Если помните, у набора записей есть свойство EOF, обозначающее достижение конца набора записей и есть методы для перемещения по набору…

  • Нам подойдет цикл Do Until... Loop (мы будем проверять, не достигли ли мы конца набора записей БД, но проверку надо производить вначале, чтобы исключить проверку пустой БД).

Итак, создайте обработчик события Form_Unload (не забудьте, мы работаем с кодом frmSheet).

Чтобы перемещаться до конца набора записей БД, сперва обязательно следует сделать так:

<набор_записей>.MoveLast

<набор_записей>.MoveFirst

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

Добавьте код в обработчик события Form_Unload:

frmDiary.datPrimaryRS.Recordset.MoveLast

frmDiary.datPrimaryRS.Recordset.MoveFirst

А теперь создадим заготовку цикла:

  • Внеситекод:

Do Until frmDiary.datPrimaryRS.Recordset.EOF
	frmDiary.datPrimaryRS.Recordset.MoveNext
Loop

Метод MoveNext перемещает указатель на следующую позицию набора записей. Без этого цикл станет бесконечным (так как будет постоянно проверяться одна и та же запись) и программа зависнет!

В циклах Do... Loop обязательно следует предусматривать условие завершения цикла, иначе цикл может стать бесконечным и программа «зависнет».

Обратите внимание: мы явно указываем, на какой форме находится элемент управления данными. Если программа большая, это избавит вас от проблем.

  • Теперь добавьте код для проверки, соответствует ли текущая запись набора текущей страничке:

If frmDiary.datPrimaryRS.Recordset(“Dates”) = Me.Caption Then

End If

  • А теперь выполним действия по обновлению набора записей:

frmDiary.datPrimaryRS.Recordset(“Note”) = Me.txtSheet.Text

  • И принудительно выйдем из цикла, т.к. дальнейшее его прохождение не нужно:

Exit Do

Код примет вид:

Private Sub Form_Unload(Cancel As Integer)
frmDiary.datPrimaryRS.Recordset.MoveLast
frmDiary.datPrimaryRS.Recordset.MoveFirst
Do Until frmDiary.datPrimaryRS.Recordset.EOF
If frmDiary.datPrimaryRS.Recordset("Dates") = Me.Caption Then
frmDiary.datPrimaryRS.Recordset("Note") = Me.txtSheet.Text
Exit Do
End If
frmDiary.datPrimaryRS.Recordset.MoveNext
Loop
End Sub

Но на этом работа не закончена! Следует правильно удалить текущую страничку из коллекции страничек.

  • Это делается аналогично тому, как мы прибавляли страничку к коллекции:

For Each NewSheet In colSheets
 If NewSheet.Caption = Me.Caption Then
 colSheets.Remove Me.Caption
 Exit For
 End If
Next NewSheet

При удалении члена коллекции применяется метод коллекции Remove, параметр метода - тот же, что и при добавлении члена методом Add, т.е. – идентификатор члена коллекции.

Код примет вид:

Private Sub Form_Unload(Cancel As Integer)
frmDiary.datPrimaryRS.Recordset.MoveLast
frmDiary.datPrimaryRS.Recordset.MoveFirst
Do Until frmDiary.datPrimaryRS.Recordset.EOF
If frmDiary.datPrimaryRS.Recordset("Dates") = Me.Caption Then
frmDiary.datPrimaryRS.Recordset("Note") = Me.txtSheet.Text
Exit Do
End If
frmDiary.datPrimaryRS.Recordset.MoveNext
Loop
For Each NewSheet In colSheets
 If NewSheet.Caption = Me.Caption Then
 colSheets.Remove Me.Caption
 Exit For
 End If
Next NewSheet
End Sub

Запустите программу.

  • Вызовите дочернюю форму-дневник (команда меню нашей программы ВидДневник...).

  • Откройте несколько (2-3) странички путем нажатия на кнопку « Страничка».

  • Закройте эти странички в произвольном порядке, наблюдая при этом за поведением указателя («стрелочка влево» на сетке формы дневника).

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

  • Но если в БД десятки и сотни записей, такой поведение может оказаться неудобным.

  • Гораздо лучше, если указатель будет оставаться на выбранной записи и не будет перемещаться при закрытии странички.

  • Это сделать нетрудно, как известно, у набора записей есть свойство AbsolutePosition, обозначающее текущее положение указателя в наборе. Нам надо сначала сохранить значение свойства, а затем восстановить его.

Объявите переменную в самом начале процедуры:

Dim varPosition

  • Затем внесите код, в котором этой переменной присваивается текущее положение указателя:

varPosition = frmDiary.datPrimaryRS.Recordset.AbsolutePosition

  • А перед выходом из цикла Do... Loop восстановите положение указателя:

frmDiary.datPrimaryRS.Recordset.Absolute­Position = varPosition

Код примет вид:

Private Sub Form_Unload(Cancel As Integer)
Dim varPosition
varPosition = frmDiary.datPrimaryRS.Recordset.AbsolutePosition
frmDiary.datPrimaryRS.Recordset.MoveLast
frmDiary.datPrimaryRS.Recordset.MoveFirst
Do Until frmDiary.datPrimaryRS.Recordset.EOF
If frmDiary.datPrimaryRS.Recordset("Dates") = Me.Caption Then
frmDiary.datPrimaryRS.Recordset("Note") = Me.txtSheet.Text
frmDiary.datPrimaryRS.Recordset.AbsolutePosition = varPosition
Exit Do
End If
frmDiary.datPrimaryRS.Recordset.MoveNext
Loop
For Each NewSheet In colSheets
 If NewSheet.Caption = Me.Caption Then
 colSheets.Remove Me.Caption
 Exit For
 End If
Next NewSheet
End Sub

Проверьте программу в работе.

Наверное, вы уже обратили внимание, что в коде многократно встречается последовательность frmDiary.datPrimaryRS.Recordset. Безусловно, вы можете использовать конструкцию With.

Код примет вид:

Private Sub Form_Unload(Cancel As Integer)
Dim varPosition
With frmDiary.datPrimaryRS.Recordset
varPosition = .AbsolutePosition
.MoveLast
.MoveFirst
Do Until .EOF
If frmDiary.datPrimaryRS.Recordset ("Dates") = _
Me.Caption Then
frmDiary.datPrimaryRS.Recordset ("Note") = _
Me.txtSheet.Text
.AbsolutePosition = varPosition
Exit Do
End If
.MoveNext
Loop
End With
For Each NewSheet In colSheets
 If NewSheet.Caption = Me.Caption Then
 colSheets.Remove Me.Caption
 Exit For
 End If
Next NewSheet
End Sub

Как видите, код стал гораздо короче и понятнее. При больших БД такой прием даст вам значительное увеличение скорости работы.

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

Внесите строку для игнорирования ошибок.

Еще одна ситуация, когда мы закрываем страничку, но изменений в ней не было произведено – и обновлять БД не надо.

Для обхода этой ситуации сделаем следующее:

  • В области глобальных объявлений frmSheet объявите переменную логического типа:

Dim boolChange As Boolean

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

В обработчике события загрузки формы-странички присвойте переменной значение False:

Private Sub Form_Load()
	boolChange = False
End Sub

У контрола TextBox есть событие Change, которое происходит при любом редактировании текста.

В обработчике этого события присвойте переменной значение True:

Private Sub txtSheet_Change()
	boolChange = True
End Sub

В следующем разделе мы проверим, что получилось.

Вопросы

  1. Что такое цикл? Для чего он применяется?

  2. Какие виды циклов Вам известны?

  3. Какое обязательное условие следует выполнять при написании цикла Do... Loop? Почему?

  4. Что такое перебор коллекции, как он выполняется?

Задания

  1. Законспектируйте раздел.

 

Hosted by uCoz