Почему пользовательская форма не отвечает во время выполнения в VBA Excel?

Я очень новичок в VBA Excel, и я знаю только то, что мне нужно для этой задачи форматирования отчета.

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

Просто чтобы было ясно, под «не отвечает» я подразумеваю, что он зависает на экране и говорит «Не отвечает» в своем фрейме Windows, и когда я нажимаю на него, он выдает следующее сообщение:

введите здесь описание изображения

*ps: лист, который я получаю, имеет 20997 строк и 7 столбцов, и я делаю некоторые записи на другом листе с файлом того же размера и 20997 строк 23 столбца. И мой графический интерфейс очень прост, в нем нет ничего, кроме кнопки CommandButton, которая запускает прогресс.

Как я могу это исправить?


person t1w    schedule 28.11.2012    source источник
comment
Ну, сам код, вероятно, будет работать нормально, хотя стиль, кхм, далек от идеала. Вы говорите UserForm: вы запускаете это из модальной формы? Если это так, вам нужно скрыть форму, чтобы вернуться к рабочей книге. Форма не позволит вам ничего сделать, кроме как ответить на нее. Может быть, в конце вашего кода вам нужно Formname.Hide ?   -  person Vinny Roe    schedule 28.11.2012
comment
@VinnyRoe: Привет, спасибо за ответ. Извините, я не понял, что вы имеете в виду под модальной формой. По сути, я сделал следующее: после открытия файла Excel я нажал ALT+F11, затем в меню «Файл» я выбрал Вставить › Пользовательская форма. Но я думаю, что скрытие формы не решит мою проблему, потому что в будущем у меня могут быть файлы большего размера, и это может вызвать больше проблем.   -  person t1w    schedule 29.11.2012


Ответы (4)


Вы можете предотвратить зависание окна Excel, поставив

DoEvents

внутри вашего цикла.

person Mroz    schedule 15.03.2013
comment
пожалуйста, проголосуйте за это, это фактическое решение для большинства проблем с зависанием в формах VBA. - person NeatNerd; 16.12.2013
comment
это лучшее решение, вам действительно нужно разрешить приложению обрабатывать регулярные события (например, щелчок, перетаскивание окна, перерисовку экрана и т. д.), поэтому, поскольку ваш код работает интенсивно и время от времени не отдыхает или не отказывается от управления, тогда У приложения почти нет процессорного времени для обработки этих событий, поэтому я лучше всего нахожу все операторы «Next» и «Loop» и записываю «DoEvents» на одну строку перед ними, таким образом, я могу гарантировать, что каждый цикл цикл позволит приложению обрабатывать события, если оно имеет некоторые - person Shaybc; 21.03.2014

Это происходит потому, что ваша процедура очень занята работой. Например, ваш Sub TheLoop() обращается к ячейке размером 20995 x 16 раз, чтобы написать на них строку. Взаимодействие VBA с Excel происходит медленно.

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

1. Отключите обработчики событий, обновление экрана и расчеты перед запуском процедуры. По окончании процедуры снова восстановите настройки.

   'Disable'
   Application.EnableEvents = False
   Application.ScreenUpdating = False
   Application.Calculation = xlCalculationManual

   '......  Code'

   'Enable'
   Application.EnableEvents = True
   Application.ScreenUpdating = True
   Application.Calculation = xlCalculationAutomatic

2.Вы можете оптимизировать Sub TheLoop. Вместо того, чтобы писать сразу в ячейках, записывайте значения внутри массива. После заполнения массива значениями присвойте значения массива нужному диапазону. Например:

Dim ResultValues() As String
Dim j As Long

ReDim ResultValues(2 To 20997, 1 To 3)

For j = 2 To 20997
    ResultValues(j, 1) = "New Defect"
    ResultValues(j, 2) = "3"
    ResultValues(j, 3) = "2"
Next j

With ThisWorkbook.Worksheets("myWorksheet")
    .Range(.Cells(2, 3), .Cells(20997, 5)) = ResultValues
End With

ИЗМЕНИТЬ:

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

  1. прочитать весь диапазон в массив.
  2. Затем измените массив так же, как вы сейчас изменяете ячейки.
  3. После внесения изменений снова выгрузите всю матрицу в диапазон».

Например:

Sub TheLoop()
Dim arrRangeValues() as Variant
Dim j as Long

arrRangeValues= Range("A2:V20997").Value2

For j = 2 To 20997
    arrRangeValues(j, 1) = "Defect" 'Cells(row_index , column_index)'
    arrRangeValues(j, 3) = "New Defect"
    arrRangeValues(j, 4) = "3" ' this one also might be empty'
    arrRangeValues(j, 5) = "2" ' this one also might be empty'

    arrRangeValues(j, 7) = "Name Surname"
    arrRangeValues(j, 8) = arrRangeValues(j, 7)
    arrRangeValues(j, 16) = arrRangeValues(j, 7)
    ...
    arrRangeValues(j, 10) = " http://SERVER_NAME:8888/PROJECT_NAME/ "
Next j

Range("A2:V20997").Value2 = arrRangeValues
End Sub
person CaBieberach    schedule 28.11.2012
comment
Привет, спасибо за ответ. :) Когда я применил ваш первый совет, я выглядел немного быстрее, но я не думаю, что этого достаточно, потому что моя форма все еще зависает. Затем я решил попробовать ваш второй совет, но хотя я не знаю о Dim< /b> и ReDim в VBA, кажется необходимым требованием написать мои столбцы рядом с следующим здесь ** ReDim ResultValues(2 To 20997, 1 To 3) ** но есть некоторые другие столбцы, которые не содержат ничего, кроме заголовка и большинство из этих столбцов находятся между столбцами, которые я записываю в подпрограмме TheLoop. - person t1w; 29.11.2012
comment
Я хотел бы применить ваше решение, но я не хочу писать дополнительные коды для перемещения столбцов между другими. Есть ли другой способ, который вы можете предложить мне? :) - person t1w; 29.11.2012
comment
Ячейки в столбцах 2,6,11,17,19,21 содержат только текст или пробелы? - person CaBieberach; 29.11.2012
comment
3 из этих столбцов импортируются из другого листа в подпрограмме CostComplexity(), остальные пусты, кроме заголовков столбцов. - person t1w; 29.11.2012

Хорошо, я считаю, что нашел лучшее решение для этого. (а) :)

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

Sub TheLoop()

    Cells(2, 1).Resize(20996) = "Defect"
    Cells(2, 3).Resize(20996) = "New Defect"
    Cells(2, 4).Resize(20996) = "3"
    Cells(2, 5).Resize(20996) = "2"
    Cells(2, 7).Resize(20996) = "Name Surname"
    Cells(2, 8).Resize(20996) = "Name Surname"
    Cells(2, 9).Resize(20996) = "FALSE"


    Cells(2, 10).Resize(20996) = " http://SERVER_NAME:8888/PROJECT_NAME/ "


    Cells(2, 12).Resize(20996) = "Software Quality"
    Cells(2, 13).Resize(20996) = "Unsigned"
    Cells(2, 14).Resize(20996) = "Software Quality"
    Cells(2, 15).Resize(20996) = "1"
    Cells(2, 16).Resize(20996) = "Name Surname"
    Cells(2, 18).Resize(20996) = "Software Quality"
    Cells(2, 20).Resize(20996) = "Development"
    Cells(2, 22).Resize(20996) = " TYPE YOUR MODULE'S NAME TO HERE"

End Sub
person t1w    schedule 29.11.2012
comment
это очень специфическое решение для вашего кода, на самом деле обход, а не правильное решение проблемы такого типа, правильное решение: запись DoEvents перед оператором «Следующий» в вашем цикле - person Shaybc; 21.03.2014
comment
@Shaybc Прошло много времени с тех пор, как я в последний раз имел дело с vba. DoEvent предотвращает зависание, но ваш метод быстрее? с этим решением я могу сделать это мгновенно. Если я не ошибаюсь, цикл занимает так много времени для массивных файлов Excel. - person t1w; 27.03.2014

Меня пробовали с Application.ScreenUpdating, Application.EnableEvents, Application.Calculation, DoEvents и Application.Wait (Now + TimeValue(0:00:10)), и, к сожалению, последние варианты не могут решить эту проблему, глядя на Веб-страница Microsoft сообщает мне, если программа сообщает, что Не отвечает

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

В моем случае я попытался показать и MsgBox пользователю с окончательностью, чтобы предоставить обратную связь для операционной системы Win10 и клиента, но это настолько скучно, что пользователь нажимает да, да, да, да, хорошо , ок, ок, ок и посмотрев на других форумах про VBA для MsgBox (автоматически закрывается), мне предлагают этот код CreateObject("WScript.Shell").PopUp "Please Wait", 1 и тада!! Я перестану видеть сообщение «Не отвечает», и если пользователь не нажмет какой-либо щелчок или кнопку для отключения сообщения, не беспокойтесь, программа будет выполняться после закрытия сообщения через 1 секунду, в моем случае это решит меня много проблем, я надеюсь помочь вам, удачи.

person JohnMayorga18    schedule 17.05.2021