Combobox показывает значение только после окончания макроса - PullRequest
3 голосов
/ 14 апреля 2019

У меня есть приложение с несколькими диаграммами и множеством комбинированных списков (контроль ActiveX). Когда пользователь изменяет значение любого комбинированного списка, диаграммы обновляются. Здесь нет проблем.

Итак, я сделал код для экспорта всего экрана приложения в виде изображения. Это используется для имитации нескольких сценариев.

Но вот где начинается проблема.

В этом коде есть несколько циклов for ... next для изменения значений этих комбинированных списков. Когда изображения экспортируются, диаграммы обновляются, как и ожидалось, но поля со списком НЕ меняют свои значения. Они показывают одинаковое значение в каждом сценарии, даже если диаграммы обновляются.

Итак, вопрос таков: есть ли способ обновить значение комбинированного списка до завершения кода?

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 Ответ

1 голос
/ 14 апреля 2019

Объяснение

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

  • Если вы установите точку останова после обновления выпадающего списка (т. Е. Поток приостановлен) => компонент 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...