Ответ 1
Отказ от ответственности Я написал статью , связанную с= "/info/459142/are-there-disadvantages-in-putting-code-into-userforms-instead-of-modules#comment4526141_459142" > с. Я владею этим блогом и управляю проектом надстройки VBIDE с открытым исходным кодом для него.
Ни один из ваших вариантов не идеален. Назад к основам.
Чтобы выбрать разные фильтры, у меня есть разные пользовательские формы.
В ваших спецификациях требуется, чтобы пользователь мог выбирать разные фильтры, и вы выбрали для него интерфейс, используя UserForm
. До сих пор, так хорошо... и все это вниз оттуда.
Создание формы, ответственной за что-либо, кроме проблемы с представлением, является распространенной ошибкой, и у нее есть имя: это шаблон Smart UI [anti-], и проблема с ним заключается в том, что он не масштабируется. Это отлично подходит для прототипирования (т.е. Быстро, что "работает" - обратите внимание на цитаты с испугом), а не столько для чего-то, что нужно поддерживать в течение многих лет.
Вероятно, вы видели эти формы с 160 элементами управления, 217 обработчиками событий и тремя частными процедурами, закрывающимися на 2000 строк кода: насколько сильно Умный пользовательский интерфейс масштабируется, и это единственный возможный результат по этой дороге.
Вы видите, что UserForm
- это модуль класса: он определяет схему объекта. Объекты обычно хотят создать экземпляр, но тогда у кого-то была гениальная идея предоставить все экземпляры MSForms.UserForm
predeclared ID, который в терминах COM означает, что вы в основном получаете глобальный объект бесплатно.
Отлично! Нет? Нет.
UserForm1.Show decisionInput1 = UserForm1.decision If decisionInput1 Then UserForm2.Show Else UserForm3.Show End If
Что произойдет, если UserForm1
- "X'd-out"? Или если UserForm1
Unload
ed? Если форма не обрабатывает событие QueryClose
, объект уничтожается - но поскольку этот экземпляр по умолчанию, VBA автоматически/молча создает новый для вас, как раз перед тем, как ваш код читает UserForm1.decision
- в результате вы получаете независимо от начального глобального состояния для UserForm1.decision
.
Если это не был экземпляр по умолчанию, а QueryClose
не обрабатывался, доступ к элементу .decision
уничтоженного объекта дал бы вам классическую ошибку времени выполнения 91 для доступа к ссылке на нулевой объект.
UserForm2.Show
и UserForm3.Show
оба делают одно и то же: огонь и забыть - что бы ни случилось, и чтобы выяснить, что именно состоит, вам нужно выкопать его в соответствующем коде для форм.
Другими словами, формы запускают показ. Они несут ответственность за сбор данных, представление этих данных, сбор пользовательских данных и выполнение любой работы с ним. Именно поэтому он назвал "Smart UI": пользовательский интерфейс все знает.
Там лучший способ. MSForms является предшественником COM-интерфейса .NET WinForms UI, и то, что имеет предок вместе с его преемником .NET, заключается в том, что он особенно хорошо работает со знаменитым шаблоном Model-View-Presenter (MVP).
Модель
Это ваши данные. По сути, это то, что ваша логика приложения должна знать из формы.
-
UserForm1.decision
отпустите это.
Добавьте новый класс, назовите его, скажем, FilterModel
. Должен быть очень простой класс:
Option Explicit
Private Type TModel
SelectedFilter As String
End Type
Private this As TModel
Public Property Get SelectedFilter() As String
SelectedFilter = this.SelectedFilter
End Property
Public Property Let SelectedFilter(ByVal value As String)
this.SelectedFilter = value
End Property
Public Function IsValid() As Boolean
IsValid = this.SelectedFilter <> vbNullString
End Function
Это действительно все, что нам нужно: класс для инкапсуляции данных формы. Класс может нести ответственность за какую-либо логику проверки или что-то еще - но он не собирает данные, он не представляет его пользователю и не потребляет его. Это данные.
Здесь есть только 1 свойство, но у вас может быть еще много: подумайте об одном поле в свойстве form = > one.
Модель также должна знать форма из логики приложения. Например, если форма нуждается в раскрывающемся списке, который отображает несколько возможных вариантов, модель будет объектом, подвергая их.
Вид
Это ваша форма. Он несет ответственность за знание об элементах управления, написании и чтении из модели и... о том, что все. Мы смотрим на диалог здесь: мы его поднимаем, пользователь заполняет его, закрывает его, и программа действует на него - сама форма ничего не делает с данными, которые она собирает. Модель может подтвердить ее, форма может решить отключить ее кнопку Ok, пока модель не сообщит, что ее данные действительны и хороши, но ни при каких обстоятельствах a UserForm
читает или записывает с рабочий лист, базу данных, файл, URL-адрес или что-то еще.
Образ кода формы прост: он соединяет пользовательский интерфейс с экземпляром модели и включает/отключает его кнопки по мере необходимости.
Важно помнить:
-
Hide
, неUnload
: представление - это объект, а объекты не подвергаются самоуничтожению. - НИКОГДАобратитесь к экземпляру формы по умолчанию.
- Всегда обрабатывайте
QueryClose
, чтобы избежать саморазрушающего объекта ( "X-ing out" формы в противном случае уничтожил бы экземпляр).
В этом случае код-код может выглядеть так:
Option Explicit
Private Type TView
Model As FilterModel
IsCancelled As Boolean
End Type
Private this As TView
Public Property Get Model() As FilterModel
Set Model = this.Model
End Property
Public Property Set Model(ByVal value As FilterModel)
Set this.Model = value
Validate
End Property
Public Property Get IsCancelled() As Boolean
IsCancelled = this.IsCancelled
End Property
Private Sub TextBox1_Change()
this.Model.SelectedFilter = TextBox1.Text
Validate
End Sub
Private Sub OkButton_Click()
Me.Hide
End Sub
Private Sub Validate()
OkButton.Enabled = this.Model.IsValid
End Sub
Private Sub CancelButton_Click()
OnCancel
End Sub
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
If CloseMode = VbQueryClose.vbFormControlMenu Then
Cancel = True
OnCancel
End If
End Sub
Private Sub OnCancel()
this.IsCancelled = True
Me.Hide
End Sub
Это буквально вся форма. Он не несет ответственности за знание того, откуда поступают данные или что с ним делать.
Ведущий
Это объект "клей", который соединяет точки.
Option Explicit
Public Sub DoSomething()
Dim m As FilterModel
Set m = New FilterModel
With New FilterForm
Set .Model = m 'set the model
.Show 'display the dialog
If Not .IsCancelled Then 'how was it closed?
'consume the data
Debug.Print m.SelectedFilter
End If
End With
End Sub
Если данные в модели должны поступать из базы данных или какого-либо рабочего листа, он использует экземпляр класса (да, другой объект!), который отвечает за выполнение именно этого.
Вызывающий код может быть вашим клиентом обработчика кнопки ActiveX, New
-подготовкой ведущего и вызовом его метода DoSomething
.
Это не все, что нужно знать о OOP в VBA (я даже не упоминал интерфейсы, полиморфизм, тестовые заглушки и модульное тестирование), но если вы хотите объективно масштабируемый код, вы захотите спуститься вниз MVP кролика и изучить возможности по-настоящему объектно-ориентированного кода привести к VBA.
TL; DR:
Код ( "бизнес-логика" ) просто не принадлежит коду кода в любой кодовой базе, которая означает масштабирование и сохранение в течение нескольких лет.
В "варианте 1" код трудно выполнить, потому что вы перепрыгиваете между модулями, а проблемы с представлением смешиваются с логикой приложения: это не задание формы, чтобы узнать, какую другую форму отображать данную кнопку A или кнопку B был нажат. Вместо этого он должен позволить ведущему знать, что пользователь хочет делать, и действовать соответствующим образом.
В "варианте 2" код трудно следовать, потому что все скрыто в коде кода пользователя: мы не знаем, что такое логика приложения, если мы не вникнем в этот код, который теперь намеренно смешивает презентационную и бизнес-логику проблемы. Это именно то, что делает анти-шаблон Smart UI.
Иными словами, вариант 1 немного лучше варианта 2, потому что по крайней мере логика не находится в коде, но она по-прежнему является "интеллектуальным интерфейсом", потому что она запускает шоу, а не говорит своему вызывающему, что происходит.
В обоих случаях кодирование против экземпляров по умолчанию для форм является вредным, поскольку оно помещает состояние в глобальную область видимости (любой может получить доступ к экземплярам по умолчанию и сделать что-либо в своем состоянии из любого места в коде).
Обработать формы, подобные объектам: создать их экземпляр!
В обоих случаях, поскольку код формы тесно связан с логикой приложения и переплетается с проблемами представления, совершенно невозможно написать один unit test, который охватывает даже один аспект того, что происходит. С шаблоном MVP вы можете полностью отделить компоненты, отвлечь их от интерфейсов, изолировать обязанности и написать десятки автоматических модульных тестов, которые охватывают все отдельные функции и документировать то, что спецификация - без написания одного бита документации: код становится его собственной документацией.