Тема создана из масштабного оффтопа в этой теме. Возникла необходимость расставить все точки над "ё", как говорится.
Итак, предлагаю следующие критерии определения эффективности: 1. скорость, 2. стабильность, 3. понятность для НЕавтора кода. Можно ещё добавить подкритерии "универсальность" и "размер кода". Всякие "ментальные" понятия типа "так не принято" предлагаю не учитывать.
Начну с меток, т.к. они вызывают очень спорные мнения. Я их всегда использую для обхода непредвиденных ошибок и не представляю, чем их можно было бы заменить (разве что писать On Error Resume Next и проверять в каждой строке переменную ошибки If Err>0 Then. Кроме того, я использую метки вместо многоуровневых If - Then — для меня так код намного понятнее и меньше.
Вот, собственно, пример кода с метками. Тот самый, о котором я вчера писал в оффтопе. Что и почему вы бы заменили в нём?
КОД
Код
Function PRDX_FindCellsInRange(WF_rng As Range, WF_Val, Optional WF_NoCase As Boolean = False, Optional WF_Part As Boolean = False) As Range
Dim WF_arrRng(), arrVal(), x, tmpRng As Range
Dim WF_i&, WF_j&, n&
On Error GoTo er
If WF_rng.Areas.Count <> 1 Then GoTo er
WF_arrRng = WF_rng.Value
'========== Приведение к верхнему регистру и ветвление
If WF_NoCase Then
WF_Val = UCase(WF_Val)
For WF_i = 1 To UBound(WF_arrRng, 1)
For WF_j = 1 To UBound(WF_arrRng, 2)
WF_arrRng(WF_i, WF_j) = UCase(WF_arrRng(WF_i, WF_j))
Next WF_j
Next WF_i
End If
If WF_Part Then GoTo onePart
'========== Одно значение и полное совпадение
For WF_i = 1 To UBound(WF_arrRng, 1)
For WF_j = 1 To UBound(WF_arrRng, 2)
If WF_arrRng(WF_i, WF_j) = WF_Val Then Set tmpRng = PRDX_RangeUnion(tmpRng, WF_rng(WF_i, WF_j))
Next WF_j
Next WF_i
GoTo done
'========== Одно значение и частичное совпадение
onePart:
For WF_i = 1 To UBound(WF_arrRng, 1)
For WF_j = 1 To UBound(WF_arrRng, 2)
If InStr(WF_arrRng(WF_i, WF_j), WF_Val) > 0 Then Set tmpRng = PRDX_RangeUnion(tmpRng, WF_rng(WF_i, WF_j))
Next WF_j
Next WF_i
done: If tmpRng Is Nothing Then GoTo er Else: Set PRDX_FindCellsInRange = tmpRng
GoTo fin
er: Set PRDX_FindCellsInRange = Nothing
fin:
End Function
Вспомогательная функция объединения диапазонов
Код
Public Function PRDX_RangeUnion(WF_gr As Range, WF_cl As Range) As Range
On Error GoTo er
If WF_gr Is Nothing Then
Set PRDX_RangeUnion = WF_cl
Else
Set PRDX_RangeUnion = Application.Union(WF_gr, WF_cl)
End If
Exit Function
er: Set PRDX_RangeUnion = Nothing
End Function
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Как по мне, то в данном случае GoTo нарушают структуру и мешают. Чем хуже If? (Not поставил, чтобы не переставлять блоки строк):
Код
If Not WF_Part Then
'========== Одно значение и полное совпадение
For WF_i = 1 To UBound(WF_arrRng, 1)
For WF_j = 1 To UBound(WF_arrRng, 2)
If WF_arrRng(WF_i, WF_j) = WF_Val Then Set tmpRng = PRDX_RangeUnion(tmpRng, WF_rng(WF_i, WF_j))
Next WF_j
Next WF_i
'========== Одно значение и частичное совпадение
Else
For WF_i = 1 To UBound(WF_arrRng, 1)
For WF_j = 1 To UBound(WF_arrRng, 2)
If InStr(WF_arrRng(WF_i, WF_j), WF_Val) > 0 Then Set tmpRng = PRDX_RangeUnion(tmpRng, WF_rng(WF_i, WF_j))
Next WF_j
Next WF_i
End If
Nordheim, в данном случае совершенно согласен, но, обычно у меня в начале отключается всё (.ScreenUpdating, например), а в конце всё включается (по метке fin). Как, например, в процедуре ниже.
vikttur, опять же — да, НО… Часто уровней вложенности намного больше и получается "поваленное дерево" кода, уходящее далеко вправо (из-за отступов для удобочитаемости) а с метками всё проще — если не то-то, то идём сразу туда-то вкусовщина, наверное?))
Процедура с метками
Код
Sub УдалитьДоИлиПослеГраниц()
Dim cl As Range
Dim choose1 As Byte, choose2 As Byte, pos1 As Byte, pos2 As Byte
Dim txt$
On Error GoTo er
If PRDX_HaveFull(Selection) = False Then MsgBox "Все пустые…", vbInformation, "NOTHING": GoTo ex
If PRDX_HaveHide(Selection) = True Then MsgBox "Присутствуют скрытые!", vbCritical, "RANGE ERROR": GoTo ex
If Selection.Cells.Count > 1000000 Then MsgBox "Не более 1 млн ячеек!", vbCritical, "RANGE ERROR": GoTo ex
choose1 = MsgBox("Удалить ДО пограничных символов [ДА] или ПОСЛЕ [НЕТ]?", vbYesNoCancel + vbQuestion + vbDefaultButton1) 'да/нет/выход = 6/7/2
If choose1 = 2 Then GoTo ex
choose2 = MsgBox("Удалить пограничные символы?", vbYesNoCancel + vbQuestion + vbDefaultButton1) 'да/нет/выход = 6/7/2
If choose2 = 2 Then GoTo ex
On Error GoTo ex
txt = Application.InputBox("Пограничные символы:", "Введите текст", " ", Type:=2)
If Len(txt) < 1 Or txt = "False" Then GoTo ex
Application.ScreenUpdating = 0
On Error GoTo er
If choose1 = 7 Then GoTo aft
For Each cl In Selection
If (Len(cl) < 2) Or (Not cl.Value Like "*" & txt & "*") Then GoTo nx_bef
pos1 = InStr(cl.Value, txt): If (pos1 = 1 Or pos1 = Len(cl.Value)) And choose2 = 7 Then GoTo nx_bef
pos2 = pos1 + Len(txt) - 1
If choose2 = 6 Then
cl.Value = Mid$(cl.Value, pos2 + 1)
cl.Value = Application.WorksheetFunction.Trim(cl.Value)
Else
cl.Value = Mid$(cl.Value, pos1)
cl.Value = Application.WorksheetFunction.Trim(cl.Value)
End If
nx_bef:
Next cl
GoTo fin
aft:
For Each cl In Selection
If (Len(cl) < 2) Or (Not cl.Value Like "*" & txt & "*") Then GoTo nx_aft
pos1 = InStr(cl.Value, txt): If (pos1 = 1 Or pos1 = Len(cl.Value)) And choose2 = 7 Then GoTo nx_aft
pos2 = pos1 + Len(txt) - 1
If choose2 = 6 Then
cl.Value = Left$(cl.Value, pos1 - 1)
cl.Value = Application.WorksheetFunction.Trim(cl.Value)
Else
cl.Value = Left$(cl.Value, pos2)
cl.Value = Application.WorksheetFunction.Trim(cl.Value)
End If
nx_aft:
Next cl
GoTo fin
er: MsgBox "КРИТИЧЕСКАЯ ОШИБКА!", vbCritical, "FATAL ERROR"
ex: MsgBox "Отмена выполнения…", vbInformation, "EXIT"
fin: On Error GoTo 0: Application.ScreenUpdating = 1
End Sub
Вспомогательные функции
Код
Public Function PRDX_HaveHide(WF_rng As Range) As Boolean
Dim WF_cl As Range
For Each WF_cl In WF_rng
If WF_cl.EntireRow.Hidden = True Or WF_cl.EntireColumn.Hidden = True Then PRDX_HaveHide = True: Exit Function
Next WF_cl
PRDX_HaveHide = False
End Function
'=========================================================================================================================
Public Function PRDX_HaveEmpty(WF_rng As Range) As Boolean
Dim WF_cl As Range
For Each WF_cl In WF_rng
If Len(WF_cl) = 0 Then GoTo ex
Next WF_cl
PRDX_HaveEmpty = False
GoTo fin
ex: PRDX_HaveEmpty = True
fin:
End Function
'=========================================================================================================================
Public Function PRDX_HaveFull(WF_rng As Range) As Boolean
Dim WF_cl As Range
For Each WF_cl In WF_rng
If Len(WF_cl.Value) > 0 Then GoTo ex
Next WF_cl
PRDX_HaveFull = False
GoTo fin
ex: PRDX_HaveFull = True
fin:
End Function
Запросы сумм с повтором, в случае ошибки
Код
' объявления переменных
On Error GoTo er
Application.CalculateFull
' 3 функции ниже выполняют проверку разных областей в файле
If Not file_flagSet() Then MsgBox "Ошибка листа настроек!", vbCritical, "ОШИБКА НАСТРОЕК": shSetttings.Select: GoTo ex
If file_flagDBEmpty() Then MsgBox "Хранилище пустое!", vbCritical, "ОШИБКА ВЫВОДА": GoTo ex
If Not file_flagDBFull() Then MsgBox "В хранилище присутствуют ошибки!", vbCritical, "ОШИБКИ": GoTo ex
If MsgBox("Ошибки не обнаружены." & vbLf & "Будет создано 2 файла." & vbLf & _
"Лист «ведомость» и хранилище будут очищены." & vbLf & vbLf & "Продолжить?", vbYesNo + vbQuestion + vbDefaultButton1) = vbNo Then GoTo ex
GoTo nxNR
repNR:
MsgBox "Недопустимое значение!", vbCritical, "ОШИБКА ВВОДА"
nxNR:
On Error GoTo repNR
sumNR = Application.InputBox("Введите сумму Накладных Расходов по данной ведомости:", "Шаг №1", "", Type:=2)
If sumNR = "False" Then GoTo ex
If InStr(sumNR, ".") > 0 Or InStr(sumNR, "-") > 0 Then GoTo repNR
On Error Resume Next
If --sumNR <= 0 Or --sumNR <> --sumNR Or --sumNR <> Application.WorksheetFunction.Round(--sumNR, 2) Then GoTo repNR
If Err <> 0 Then GoTo repNR
sumNR = --sumNR
GoTo nxKP
repKP:
MsgBox "Недопустимое значение!", vbCritical, "ОШИБКА ВВОДА"
nxKP:
On Error GoTo repKP
sumKP = Application.InputBox("Введите сумму коммерции по данной ведомости:", "Шаг №2", "", Type:=2)
If sumKP = "False" Then GoTo ex
If InStr(sumKP, ".") > 0 Or InStr(sumKP, "-") > 0 Then GoTo repKP
On Error Resume Next
If --sumKP <= 0 Or --sumKP <> --sumKP Or --sumKP <> Application.WorksheetFunction.Round(--sumKP, 2) Then GoTo repKP
If Err <> 0 Then GoTo repKP
sumKP = --sumKP
On Error GoTo er
' основной код
MsgBox "2 файла успешно сформировано!" & vbLf & vbLf & "Время работы макроса (сек.): " & Round(Timer - t, 4), vbInformation, "ГОТОВО"
GoTo fin
er: MsgBox "Сообщите разработчику…", vbCritical, "НЕПРЕДВИДЕННАЯ ОШИБКА"
ex: MsgBox "Отмена выполнения…", vbInformation, "ВЫХОД"
fin: On Error GoTo 0: Application.ScreenUpdating = 1: Application.DisplayAlerts = 1
End Sub
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
не помню у кого читал то ли у Лукина, то ли у Уокенбаха, но там написано совершенно противоположное, что с метками намного все сложней и можно запутаться, даже зациклить выполнение кода, сам того не подозревая, вот поэтому я вообще не использую метки.
"Все гениальное просто, а все простое гениально!!!"
Nordheim, может быть просто таких объёмов не было у меня))) как по мне, метки позволяют пропускать/перепрыгивать огромные участки кода, сразу перемещаясь в нужное место. Фактически, получается точно. как на блок-схеме. Осмысленные названия меток обязательны
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Jack Famous, просто Вы привыкли к, так называемому, выносному методу обработки ошибок, ну и это влияет на остальные . Не скажу что часто сажусь за VBA(VBS) но метки не использовал уже много лет. Вот чем хуже?
Код
Public Function PRDX_HaveFull(WF_rng As Range) As Boolean
Dim WF_cl As Range
PRDX_HaveFull = False
For Each WF_cl In WF_rng
If Len(WF_cl.Value) > 0 Then
PRDX_HaveFull = True
Exit For
End If
Next WF_cl
End Function
или
Код
Public Function PRDX_HaveFull(WF_rng As Range) As Boolean
Dim WF_cl As Range
For Each WF_cl In WF_rng
If Len(WF_cl.Value) > 0 Then
PRDX_HaveFull = True
Exit For
End If
Next WF_cl
PRDX_HaveFull = (PRDX_HaveFull = True)
End Function
vikttur, 1. в первом спойлере повсюду метки, второй - просто функции для работы первого (вдруг кто-то из новичков заглянет и себе возьмёт, да и просто для примера), 3ий код сокращён (убрано ненужное) 2. ну не знаааааюууууу. Сравнивать метки с объединёнными ячейками - это мне вот щас обидно было
БМВ, согласен — в PRDX_HaveHide без меток сделан такой же цикл. Я вообще эти функции давно не переписывал - надо на массивы перевести)) прикрепил, чтобы первый код работал (2 из 3х в нём есть).
В первом спойлере без меток получится "поваленное дерево", в 3ем не знаю как "возврат" при ошибке ввода сделать без меток. Да и остаётся вопрос обхода непредусмотренных ошибок. А даже, если все ошибки известны: есть ведь разница — при каждой не пройденной проверке писать
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
vikttur написал: 2. Метки - большое зло, родственник объединения ячеек или ДВССЫЛ
Полностью согласен, не так давно программирую и по началу пару раз споткнулся при применении меток, и вот уже 1,5 года моего скромного стажа в VBA я не использую метки
"Все гениальное просто, а все простое гениально!!!"
Jack Famous, разместите ссылку в теме "о вредном GoTo". Фактически те же обсуждения.
Цитата
второй - просто функции для работы первого (вдруг кто-то из новичков заглянет и себе возьмёт, да и просто для примера)
Давайте во всех темах попутно выкладывать все, что может пригодиться... Мое мнение - много кода отвлекает от основного вопроса, тему становится неинтересно читать. Вопрос по меткам - так и коды сократить, чтобы отразить суть вопроса.
А когда пишем любителям объединения о неправильных их действиях - им не обидно? Украшал-прихорашивал - а они раскритиковали... И невдомек им, что делают не всегда правильно.
Jack Famous написал: PRDX_HaveHide без меток сделан такой же цикл.
сделан то сделан, только вот Then PRDX_HaveHide = True: Exit Function - осторожнее с этим не увлекайтесь. Лучше лишняя строка чем гемор из-за нерабочего кода. Не стоит после THEN использовать разделитель операторов. Я там тоже не совсем прав , по умолчанию уже функция вернет FALSE, так что просто
Код
Public Function PRDX_HaveFull(WF_rng As Range) As Boolean
Dim WF_cl As Range
For Each WF_cl In WF_rng
If Len(WF_cl.Value) > 0 Then
PRDX_HaveFull = True
Exit For
End If
Next
End Function
Цитата
Jack Famous написал: в 3ем не знаю как "возврат" при ошибке ввода сделать без меток.
Алексей, вот читать код вроде умею, но сейчас читаю - как рецепт врача, вроде и написано но что ??? . :-) er: MsgBox "Сообщите разработчику…", vbCritical, "НЕПРЕДВИДЕННАЯ ОШИБКА" ex: MsgBox "Отмена выполнения…", vbInformation, "ВЫХОД"
то есть при определенных ошибках будет два сообщения? ну как то это …….
это только для начала))) оффтопить начали по поводу того, что я ещё и Do-Loop не использую, поэтому далее хотел здесь разобрать примеры, где Do-Loop наиболее эффективен по сравнению с For-Next и If-Then
Цитата
БМВ написал: Лучше лишняя строка чем гемор из-за нерабочего кода
я тестирую)) а вообще сталкивался пару раз с проблемами, но закономерности не выявил и пока не могу отказать себе в удовольствии писать одну строку вместо минимум 4х (в классическом виде)
Цитата
БМВ написал: при определенных ошибках будет два сообщения?
совершенно верно. Так и задумано. Если возникает непредвиденная ошибка, то пользователю сначала сообщается об этом, а потом о том, что вся процедура отменяется. Если же осуществлён переход по "ex", то это просто отмена процедуры (часто самим пользователем, например, через кнопку "Отмена" в MsgBox или InputBox)
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Начинал программировать я в 8м классе с рисования блок-схем. Это на долго и глубоко увлекшее меня занятие выработало целую систему записи для сложных алгоритмов. Я одно время очень заморачивался этим и по букварям стремился проектировать код без лишних меток и goto. Но есть совершенно банальные конструкции, даже из четырех блоков алгоритма, которые не разрешимы без goto либо без повторения кода одного из блоков. Поэтому совсем избегать goto порою эквивалентно раздуванию кода и я не пытаюсь любой ценою их избегать. Однако изначально заложенные в мой мозг концептуальные подходы, среди которых было и построение общей архитектуры с минимумом перескоков между частями кода, на всю жизнь избавили написанные мною коды от излишних меток и goto.
Neufazendnik написал: на всю жизнь избавили написанные мною коды от излишних меток и goto.
В яблочко. Вредно именно излишество, а не сам GoTo.
Цитата
БМВ написал: осторожнее... не увлекайтесь... Не стоит после THEN использовать разделитель операторов.
Помедленнее, пожалуйста... Я иногда использую. То, что " осторожнее" и нужно смотреть, не вызовет ли такой прыжок нежелательных последствий, конечно, нужно. Но, может, еще какие предостережения? Не так понял. Вернее, написано не так. Двоеточие - разделитель строк, а не операторов. Об этом же писалось? А я предложение понял как предостережение от выхода из-под If, минуя End If Да, все, что в одной строке после Then, будет исполнено только при выполнении условия перед Then. Поначалу на ти грабли наступал.
OFF Не то чтоб в защиту Jack Famous, но просто скажу. Он пытается "впитать" всю информацию, которую можно взять и поднимает темы которые позволяют ему развивать свои навыки. Такое стремление заслуживает уважения, несмотря на все ошибки и иные подходы.
По организации циклов я предпочитаю Do while всем остальным. Он мне кажется почему-то более маневренным и прозрачным. В нем нет скрытного механизма приращения счетчика и ограничений на использование счетчика после покидания тела цикла. Потому больше простор для манипуляций его значением и к тому же мне кажется, что исполняться он будет быстрее. Кроме того, мне нарвится, что в Do циклах проверочное условие можно располагать как до так после тела цикла. В общем Do нахожу более предпочтительным, нежели for и чаще всего организую через Do то, что можно было бы писать и через For за редким исключением.
не получится. Одно сообщение говорит И об ошибке, И о выходе, а выход у меня ещё отдельно используется, так что так компактнее и логичнее (наверное).
Код
'примеры использования ТОЛЬКО выхода
If Selection.Cells.Count > 1000000 Then MsgBox "Не более 1 млн ячеек!", vbCritical, "RANGE ERROR": GoTo ex
'или
On Error GoTo ex
txt = Application.InputBox("Пограничные символы:", "Введите текст", " ", Type:=2)
If Len(txt) < 1 Or txt = "False" Then GoTo ex
Цитата
БМВ написал: Не то чтоб в защиту Jack Famous , но просто скажу
спасибо большое — от ВАС это очень приятно слышать, Михаил Васильевич
Neufazendnik, наверное, у меня что-то похожее, но обратное — к For…Next привык есть ли у вас пример (не просто код, а именно бытовая задача), в которой, на ваш взгляд, уверенно лидирует ваш метод?
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Jack Famous написал: а выход у меня ещё отдельно используется,
У меня та же ситуация. В этом году я пришел к правилу, что выходы лучше проектировать не множественные в отдельных частях кода, а по метке сводить все в одну строку, в которой и Screenupdating и Enableevents и StatusBar и on error возвращаются в нужное состояние. Т.е разумно все-таки выходить из подпрограммы в одной двух (в случае да\нет) точках, а не делать множество Exit Sub
Об операторах циклов. Если логика позволяет - For Each. For - когда счетчик и нужно проходить весь диапазон без условий выхода из цикла. Do работает быстрее и при условиях правильнее применять именно этот оператор, а не For. Но часто не следую этому совету - стопор в мозгу Нужно искоренить.
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
vikttur написал: For - когда счетчик и нужно проходить весь диапазон без условий выхода из цикла
правильно я понимаю, что Do может мне помочь обойтись без метки nx_bef в данном участке кода? (понимаю, что можно обойтись обратной проверкой во второй строке, но это возвращает к "поваленному дереву")
Код
For Each cl In Selection
If (Len(cl) < 2) Or (Not cl.Value Like "*" & txt & "*") Then GoTo nx_bef
pos1 = InStr(cl.Value, txt): If (pos1 = 1 Or pos1 = Len(cl.Value)) And choose2 = 7 Then GoTo nx_bef
pos2 = pos1 + Len(txt) - 1
If choose2 = 6 Then
cl.Value = Mid$(cl.Value, pos2 + 1)
cl.Value = Application.WorksheetFunction.Trim(cl.Value)
Else
cl.Value = Mid$(cl.Value, pos1)
cl.Value = Application.WorksheetFunction.Trim(cl.Value)
End If
nx_bef:
Next cl
UPD: блин - у меня же не выход из цикла, а следующая ячейка
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Jack Famous написал: То же самое у меня и с обобщающими сообщениями типа "Непредвиденная ошибка" и "Отмена выполнения"
И у меня нечто подобное реализовано следующим образом: Я делаю метку с msgbox "А" или inputbox "A" и вспомогательными манипуляциями, заканчивающуюся по resume next А в эту метку попадаю не по goto, а записываю в "А" текстовое сообщение для вывода и далее генерирую икслючение, которое автоматом будет обработано по данной метке. Выведет сообщение с тестом "А", отменит тот же screenupdating или enableevents, чего-нибудь спросит у пользователя и отправит код на продолжение. Компакнее выходит, чем по 10 раз один и тот же код, выводящий разные сообщения, строчить.
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Jack Famous написал: Nordheim , главное, что всё работает! Как вы обходите ошибки? Как сделаете возврат к вводу при ошибке?
Для этого есть цикл Do.....Loop, но честно говоря не приходилось сталкиваться с тем, что бы делать возврат при ошибке, к чему ведь ошибка не исчезла и в итоге мы получаем бесконечный цикл.
"Все гениальное просто, а все простое гениально!!!"
Nordheim написал: не приходилось сталкиваться с тем, что бы делать возврат при ошибке
вот, что я имею ввиду
Код
repNR:
MsgBox "Недопустимое значение!", vbCritical, "ОШИБКА ВВОДА"
On Error GoTo repNR
sumNR = Application.InputBox("Введите сумму Накладных Расходов по данной ведомости:", "Шаг №1", "", Type:=2)
If sumNR = "False" Then GoTo ex
If InStr(sumNR, ".") > 0 Or InStr(sumNR, "-") > 0 Then GoTo repNR
On Error Resume Next
If --sumNR <= 0 Or --sumNR <> --sumNR Or --sumNR <> Application.WorksheetFunction.Round(--sumNR, 2) Then GoTo repNR
If Err <> 0 Then GoTo repNR
sumNR = --sumNR
цикла здесь не будет — выход по штатной кнопке "Отмена" или крестику
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
If sErr <> "" Then
Beep
With ufQuote: .Show: .lbText.Caption = sErr: End With ' сообщение об ошибке
Application.Wait Time:=Now + TimeValue("0:00:04") ' задержка
Unload ufQuote
End If
ufQuote - форма пльзователя с одним Label (lbText), в который выводится текст sErr. 4 секунды - и форма закрывается.
vikttur, спасибо. Я бы просто описание ошибки в MsgBox передавал (пользователям это точно не нужно, так что, видимо, для отладки). Оно, конечно, без задержки будет, но вполне норм)) вкусовщина
Цитата
vikttur написал: Вот уж действительно - тема обо всем )
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄