Объяснение
Прежде всего, поздравляю: вы нашли очень раздражающую ошибку здесь.Я пытался воспроизвести вашу проблему, и я могу сделать это очень легко.
- Если вы установите точку останова после обновления выпадающего списка (т. Е. Поток приостановлен) => компонент ActiveX обновляется
- Если вы установите
Application.Wait (TimeSerial(Hour(Now()), Minute(Now()), Second(Now()) + 5))
(т. Е. Остановите визуальновыполнение в течение 5 секунд, но с технической точки зрения поток все еще работает) => вы можете ясно видеть, что компонент ActiveX не обновляется, и поэтому ваше изображение сгенерировано неправильно.
Я пробовалвсе очевидные уловки (Application.ScreenUpdating = True
, DoEvents
, Application.EnableEvents = True
, Application.Calculate
и т. д.), но безуспешно в любом случае.
Действительно, кажется, что компоненты ActiveX будут обновляться Microsoft Excel только тогда, когдапоток VBA заканчивается.Вот это да.
Единственный способ обойти эту ошибку, о которой я могу подумать
Единственный способ, с помощью которого я могу технически остановить выполнение после обновления компонента ActiveX и возобновить егопозже, это использовать Application.OnTime
метод Excel:
Application.OnTime
планирует процедуру для запуска в указанное время в будущем (либо в определенное время дня, либо после определенногоПрошло много времени).
Как ужасно это может выглядеть с технической точки зрения, вы можете обновить свой комбинированный список, а затем запланировать выполнение остальной части кода через секунду после того, как вы это сделали.,С технической точки зрения:
- VBA-поток 1 : обновляет ComboBox и завершает работу => компонент ActiveX обновляется
- 1-секундная пауза без VBA-потоков.
- VBA-поток 2 : создает диаграмму и экспортирует изображение с использованием обновленного компонента ActiveX.
Практически ваш код будет выглядеть примерно так:
Dim myArray(2) 'declare your array as global so that it can be accessed by all the macros - in my example I assume it contains 3 elements
Dim currentElem As Integer 'declare this index as global so it remains in memory even after the code ended execution
Sub example()
'call this macro.
'you first initialize your values:
myArray(0) = "test 1"
myArray(1) = "test 2"
myArray(2) = "test 3"
currentElem = 0
'and then call the first update of your activeX component
first_step_set_activeX
End Sub
Sub first_step_set_activeX()
If currentElem < UBound(myArray) Then
'for each element not treated yet
'(that's why the If currentElem < UBound(myArray)
elem = myArray(currentElem) 'get current element from array
Sheets("App").ComboBox1.Value = elem 'update your ActiveX component
currentElem = currentElem + 1 'increase the currentElem index
Application.OnTime TimeSerial(Hour(Now()), Minute(Now()), Second(Now()) + 1), "second_step_make_chart_and_print" 'schedule the call of the printing part
End If
End Sub
Sub second_step_make_chart_and_print()
'here do the job of the printing part
Sheets("Temp").Shapes.AddChart
Set cht = Sheets("Temp").ChartObjects(1)
Sheets("App").Range("A1:AM103").CopyPicture Appearance:=xlScreen, Format:=xlBitmap
With cht.Chart
.Paste
.Export Filename:="test.jpg", FilterName:="jpg"
.Parent.Delete
End With
'and reschedule the call for the next activeX component
Application.OnTime TimeSerial(Hour(Now()), Minute(Now()), Second(Now()) + 1), "first_step_set_activeX"
End Sub