#excel #vba #combobox
#excel #vba #поле со списком
Вопрос:
У меня есть приложение с несколькими диаграммами и множеством выпадающих списков (элемент управления ActiveX). Когда пользователь изменяет значение любого combobox, диаграммы обновляются. Здесь нет проблем.
Итак, я создал код для экспорта всего экрана приложения в виде изображения. Это используется для моделирования нескольких сценариев.
Но здесь начинается проблема.
В этом коде есть несколько циклов «для … следующего», чтобы изменить значения этих списков со списком. При экспорте изображений диаграммы обновляются, как и ожидалось, но поля со списком НЕ меняют своих значений. Они показывают одно и то же значение в каждом сценарии, даже если диаграммы обновляются.
Итак, вопрос в следующем: есть ли способ обновить значение combobox до завершения кода?
Sub example()
For Each elem In myArray
Sheets("App").ComboBox1.Value = elem
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
Next
End Sub
Комментарии:
1. Добавление однострочных листов («Приложение»). ComboBox1.Активировать после установки значения поля со списком также помогает.
2. если в экспортируемом изображении также требуется очистить поле со списком, можно выбрать любую ячейку, например «Листы» («Приложение»). Диапазон («A1»). Выберите `после строк таблицы («Приложение»). ComboBox1.Activate и на картинке также есть поле со списком
Ответ №1:
Объяснение
Прежде всего, поздравляю: вы обнаружили здесь очень досадную ошибку. Я попытался воспроизвести вашу проблему, и я могу сделать это очень легко.
- Если вы установили точку останова после обновления поля со списком (т.Е. Поток приостановлен) => компонент 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
планирует выполнение процедуры в указанное время в будущем (либо в определенное время суток, либо по истечении определенного времени).
Насколько уродливо это может выглядеть с технической точки зрения, вы можете обновить свой combobox, а затем запланировать выполнение остальной части вашего кода через секунду после того, как вы это сделали. С технической точки зрения:
- Поток 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