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

Мой вопрос: используя VBA в Excel 2013, как я могу изящно закрыть весь экземпляр Excel, когда пользователь решает, что он не хочет заполнять пользовательскую форму, и нажимает кнопку «Выход» или «Отмена»?

В настоящее время, если пользователь нажимает «выход» или «отмена», я проверяю, является ли мой экземпляр единственным открытым. Если это не так, я могу использовать ThisWorkbook.Close, и я думаю, что все будет в порядке. Однако, если это так, я не хочу, чтобы приложение все еще присутствовало, поэтому я использовал Application.Quit. Это, тем не менее, пытается завершить выполнение макроса, выдает ошибки (первоначально «несоответствие типов», потому что я выгружаю форму) и закрывается только после того, как я нажимаю «Отладка» или «Конец» (что он делает так быстро, я не могу на самом деле отлаживать). Я пока игнорирую первый случай и просто пытаюсь выйти из всего приложения. Это очень длинный макрос с множеством подпрограмм и функций, поэтому для отладки и публикации здесь я его сократил. Ошибка несоответствия типов больше не возникает, но я считаю, что это было следствием фактической ошибки: код, выполняемый после вызова команды для закрытия приложения.

Во-первых, вот код, который запускает все:

Private Sub CommandButton1_Click()

    Call form_variables
    frm_REQUEST.Show
    Call a_REQUEST_main

End Sub

Подпрограмма

form_variables

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

frm_REQUEST.Show

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

a_REQUEST_main

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

Код, который выполняется при вызове .Show:

Private Sub UserForm_Initialize()

    ' Get job numbers from other workbook
    Dim job_selection_list As Variant
    job_selection_list = get_job_list()

    With frm_REQUEST.Job_Number_ComboBox
        .List = job_selection_list
    End With

    ' set focus on Job Numbers
    JN_combobox.SetFocus

End Sub

Private Sub cancel_button_Click()

    Set job_selection_list = Nothing

    Unload Me

    Application.Quit

End Sub


Private Sub submit_button_Click()

    ' Values from userform saved as global (?) variables so other subroutines can access.

End Sub

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

Call a_REQUEST_main

но на самом деле он должен просто закрыть все. Я пытался выполнять команды «сохранить» и изменять порядок вещей, и читал об объектах, которые не нужно устанавливать ни на что (отсюда и настройка job_selection_list, которая создается при инициализации выпадающего списка), но я не могу получить это, чтобы работать, или найти что-нибудь в Интернете. Может ли кто-нибудь дать некоторые рекомендации или сообщить мне о лучшем способе закрытия экземпляра Excel? Помоги мне Stack-Overflow Кеноби, ты моя единственная надежда!

Спасибо.


person TenaciousSisyphus    schedule 20.06.2014    source источник
comment
Другой вопрос: зачем возиться с определяемой пользователем кнопкой отмены, когда в пользовательской форме уже есть кнопка отмены, которая запускает процедуру QueryClose и/или Terminate, если они у вас есть. Я думаю, вы делаете это излишне сложным, пытаясь воспроизвести встроенный обработчик событий с помощью кнопки.   -  person David Zemens    schedule 21.06.2014
comment
Это, вероятно, звучит неуместно, но с точки зрения пользовательского интерфейса, я думаю, моя пользовательская база предпочла бы красивую кнопку с надписью «Отмена», поэтому я добавил ее. Затем отключение функциональности Close казалось излишне сложным, поэтому я просто поместил Call cancel_button_Click в блок кода QueryClose, но, как я объясню в ответ на ваш комментарий ниже, я не думаю, что все это действительно имело значение...   -  person TenaciousSisyphus    schedule 21.06.2014
comment
Обработчик QueryClose на самом деле необходим только в том случае, если вам нужно перехватить какое-то условие, из-за которого Terminate не произойдет. Если у вас нет ничего существенного в этом случае, просто используйте обработчик Terminate сам по себе :)   -  person David Zemens    schedule 21.06.2014


Ответы (2)


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

в виде

'hold flag if users cancels form
Public btnCancel As Boolean
Private Sub CommandButton1_Click()
Unload Me
btnCancel = True
End Sub

'set the flag each time the form active
Private Sub UserForm_Activate()
btnCancel = False
End Sub

то в вашем коде

Call form_variables
frm_REQUEST.Show

If frm_REQUEST.btnCancel Then
    Application.Quit
Else
    Call a_REQUEST_main
End If
person Sorceri    schedule 20.06.2014
comment
Спасибо Сорсери! Я немного изменил ваш код и просто попробовал его. Работал как шарм. - person TenaciousSisyphus; 21.06.2014

Поместите Application.Quit в обработчик событий Terminate формы вместо в обработчик событий Click кнопки.

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

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

Примечание. Вам может быть предложено сохранить/отменить изменения (если таковые имеются) в книге.

person David Zemens    schedule 20.06.2014
comment
Я не включил его в приведенный выше код, потому что он казался слишком длинным, но на самом деле у меня есть - person TenaciousSisyphus; 21.06.2014
comment
Сделайте это вместо того места, где оно у вас сейчас есть. - person David Zemens; 21.06.2014
comment
Я не включил его в приведенный выше код, потому что он стал слишком длинным, но у меня есть Terminate, QueryClose и cancel_button_Click с одной и той же пользовательской формой. Я продолжил попытки и отладку после того, как опубликовал сообщение и понял, что Unload Me в cancel_button_Click выполняет кодовый блок QueryClose и продолжается в Terminate (который находится прямо под ним), затем обратно в cancel_button_Click, в конечном итоге прорываясь через < i>три Exit Sub команды! - person TenaciousSisyphus; 21.06.2014
comment
На самом деле не имеет значения, есть ли у вас есть эти процедуры... не зная, что в них содержится, я не могу вам помочь. Что в них? Избавьтесь от Application.QUit в обработчике нажатия кнопки, как я предложил выше. Избавьтесь от него полностью в этой процедуре. И поместите его в обработчик Terminate. Вы можете избавиться от события QueryClose, если только вы не используете его активно для каких-то других проверок. - person David Zemens; 21.06.2014
comment
Кстати: пробиться через Exit Sub? Что еще вы ожидаете от него? Exit Sub просто завершает текущую процедуру и возвращается к вызывающей процедуре, если таковая имеется. - person David Zemens; 21.06.2014
comment
В конечном итоге я принял подход Сорсери, но хотел объяснить свои рассуждения на случай, если это поможет кому-то еще. Избавление от Application.Quit не помогло бы, потому что, проходя через выполнение, я иду от Unload Me (первая строка) в подпрограмме cancel_button_Click к началу подпрограммы Terminate, поэтому Application.Quit в подпрограмме cancel_button_Click не выполняется до тех пор, пока после выполняется в блоке Terminate (но, конечно, завершается до того, как это произойдет). - person TenaciousSisyphus; 21.06.2014
comment
Прорыв был для меня странным, потому что я думал, что выход из подпрограммы выведет меня из всей пользовательской формы. Думая об этом сейчас, кажется очевидным, что он просто перейдет к следующему подразделу... В любом случае, спасибо за помощь и понимание! - person TenaciousSisyphus; 21.06.2014
comment
Я не говорил избавиться от Application.Quit вообще... Я сказал конкретно и ясно, чтобы удалить его оттуда, где он у вас есть (в событии кнопки Click и в любом другом месте, где вы могли по ошибке разместить его...) и поместить его только в событии Terminate. Щелчок вашей кнопки затем выгружает форму, вызывая другие события (QueryClose и/или Terminate), которые затем закрывают приложение... Подход Sorceri работает, конечно, но он излишне сложен. - person David Zemens; 21.06.2014