Вызов формы VBA из кнопки приводит к тому, что UserForm_Initialize запускается дважды, нарушая мой код? - PullRequest
0 голосов
/ 23 ноября 2018

Здравствуйте, замечательное сообщество VBA, я все еще новичок в VBA и пытаюсь многому научиться.Заранее благодарим за просмотр моего кода и моего описания проблемы, с которой я сталкиваюсь.

У меня есть кнопка на странице, которая вызывает новую форму пользователя.

КОД SNIPPET1:

Sub btnShowDetails_Click()
    Call frmShowDeets.ShowDeets
End Sub

... который вызывает следующий бит кода в пользовательской форме 'frmShowDeets':

SNIPPET CODE 2:

Public Sub ShowDeets()

Dim frm As frmShowDeets
Set frm = New frmShowDeets 'this line triggers the Userform_Initialize() event below
  frm.Show

End Sub

... запуск:

КОД SNIPPET 3:

Private Sub UserForm_Initialize()

Dim comboBoxItem As Range

For Each comboBoxItem In ContactList.Range("tblContactList[CompanyName]")
                                            '^refers to unique values in a named range
  With Me.boxCompanySelection
    .AddItem comboBoxItem.Value
  End With
Next comboBoxItem

End Sub

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

КОД SNIPPET 4:

Public Sub boxCompanySelection_Change()
  Call frmShowDeets.PullData
End Sub

Sub PullData()
Dim numCompanies As Long
  numCompanies = ContactList.Range("B6").Value 'this holds a count of the rows in the named range
Dim FoundCell As Range
  Set FoundCell = ContactList.Range("tblContactList[Company Name]").Find(What:=boxCompanySelection.Text, LookIn:=xlValues, LookAt:=xlWhole)

Dim CompanyRow As Long
  CompanyRow = FoundCell.Row

With ContactList
  'pull a bunch of the company's details
End With
End Sub

Вот где это становится странным... Как только форма показывается, и пользователь выбирает один из элементов поля со списком, вызывая событие Combobox_Change, код обрывается, потому что часть Range () What: = boxCompanySelection.Text '. Метод Find читается как "" пустой(хотя Code Snippet 3 предназначен для загрузки названий компаний, а Code Snippet 4 запускается только тогда, когда пользователь выбирает одно из этих названий компаний в выпадающем списке), и мне не нужно создавать что-то для обработки исключений «не найдено», посколькуединственными возможными значениями должны быть значения, извлеченные из моего именованного диапазона.

После перехода по коду я определил, что по какой-то причине фрагменты кода 2 и 3 запускаются ДВАЖДЫ до фрагмента кода4 запускается.Кто-нибудь знает, что из-за моего кода это происходит?Я думаю, что есть несоответствие между формой, которая отображается и загружается со значениями в выпадающем списке, и тем, из чего кодовый фрагмент 4 считывает данные.

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

Проблема, вероятно, проста, которую я пропускаю, но я просто не могу понять,что это такое.Еще раз спасибо!

1 Ответ

0 голосов
/ 23 ноября 2018

Вы должны понимать, что форма - это объект - точно так же, как любой другой модуль класса, за исключением того, что у формы есть конструктор и базовый класс , поэтому UserForm1 наследует члены UserForm class.

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

Call frmShowDeets.ShowDeets

Предполагая, что frmShowDeets является именем класса формы, и предполагая, что это первая ссылка на эту форму, которая запускается, затемобработчик UserForm_Initialize экземпляра по умолчанию запускается, когда оператор точки . выполняет и разыменовывает объект. Затем запускается метод ShowDeets.

Public Sub ShowDeets()

Dim frm As frmShowDeets
Set frm = New frmShowDeets 'this line triggers the Userform_Initialize() event below
  frm.Show

End Sub

Эта строка вызывает UserForm_Initialize на локальном экземпляре с именем frm - который является совершенно отдельным объектомтого же класса.Обработчик Initialize запускается всякий раз, когда экземпляр класса инициализируется, т. Е. Создается.Обработчик Terminate запускается, когда этот экземпляр уничтожается.

Таким образом, ShowDeets действует как некий «фабричный метод», который создает и показывает новый экземпляр класса / формы frmShowDeets - в другомСлова, что бы ни случилось в экземпляре по умолчанию, не имеют значения за пределами этой точки: объект, с которым вы работаете, существует в области действия ShowDeets, называется frm и уничтожается, как только выходит из области видимости.

Удалите метод ShowDeets в целом.Замените это:

Call frmShowDeets.ShowDeets

этим:

With New frmShowDeets
    .Show
End With

Теперь обработчик Initialize больше не работает на экземпляре по умолчанию.

То, что вы хотите, этоизбегать использования экземпляра по умолчанию вообще.Замените все frmShowDeets в выделенном коде формы на Me (см. Понимание «Я» (без цветов, без пчел) ), чтобы ни одно состояние случайно не сохранялось в экземпляре по умолчанию.

Call frmShowDeets.PullData

Становится просто:

Call Me.PullData

Или даже:

PullData

Поскольку Call нигде не требуется, а квалификатор Me всегданеявный, когда вы делаете вызов члена в коде модуля класса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...