TL;DR
我使用一个UserForm
,它动态分配自己的标题和一些具有三种不同变体的控制标题。具体来说,在此用户窗体上,两个变体需要四个 CheckBox
,而在其中一个变体上不可见。
我的数据验证检查所有必填字段是否已输入值(包括选中这四个复选框之一),因此当使用不需要勾选复选框的表单时(因为控件不可见),我得到了我的“请在每个字段中输入一个值。”消息框。
如何避免这种情况?
我一直在读UserForm1.Show在过去的几个月里,对我自己以及许多其他人来说,这帮助我理解了用户表单是什么以及它是如何工作的。
我正在尝试将 MVP 模式实现到我现有的项目中,该项目或多或少刚刚已经“完成”。
自然地,当我遇到问题或困惑时,我会跳到谷歌,在大多数情况下,要么找到另一篇文章,要么找到一个作者给出了足够充分答案的问题。但。我找不到用于验证可能存在或可能不存在的 MSForms.Control
的方法 - 即有时在表单上使用,具体取决于表单的变体。
请注意,我觉得我设计表单的方式可能是错误的(好吧,单数形式),所以如果确实如此,识别并涵盖该主题的答案也将是最有帮助的!
当单击这 3 个按钮中的任何一个(工作表 ActiveX 命令按钮)时,它会填充以下用户窗体之一(标题与按钮相对应):
现在,我的数据验证对于 NEC 和 LG 表单运行良好,但在进入 Other 表单时失败。这是因为 NEC 和 LG 产品需要一种产品类型 CheckBox
,但对于 NEC 产品则不需要。 >其他产品,如果没有产品类型,则数据验证失败。
在这里,我将包含 CommandButton1_Click
(测试按钮)事件和类模块代码。我的数据验证是在 UserForm 模块中完成的,但我最近读到我应该将其放入模型中,所以我认为我需要将其移至模块中执行所有其他操作事物。
用户表单代码模块 - MCVE
Option Explicit
Public DataEntryForm As New TestForm
Private Sub CommandButton1_Click()
With Me
If .CheckBox1.Value = True Then
DataEntryForm.TestProduct = .CheckBox1.Caption
ElseIf .CheckBox2.Value = True Then
DataEntryForm.TestProduct = .CheckBox2.Caption
ElseIf .CheckBox3.Value = True Then
DataEntryForm.TestProduct = .CheckBox3.Caption
ElseIf .CheckBox4.Value = True Then
DataEntryForm.TestProduct = .CheckBox4.Caption
End If
End With
If Not FormIsComplete Then
MsgBox "Please enter a value into each field.", vbCritical, "Missing Values"
Exit Sub
End If
End Sub
Private Function FormIsComplete() As Boolean
FormIsComplete = False
If DataEntryForm.TestProduct = "" Then Exit Function
FormIsComplete = True
End Function
类模块 - TestForm (MCVE)
Private pTestProduct As String
Public Property Get TestProduct() As String
TestProduct = pTestProduct
End Property
Public Property Let TestProduct(NewValue As String)
pTestProduct = NewValue
End Property
所以,更具体地说;
问题出在DataEntryForm.TestProduct
。它位于 IsFormCompleted
函数中,因为 2/3 表单要求此属性具有值,但对于没有任何产品类型的表单自然不需要。
我的想法是简单的解决方法是为其他产品版本创建另一个单独的表单,它可以具有单独的数据验证功能,但我想尽量保持可维护性并避免使用超过 1 个这种形式。
如何让这种类型的数据验证适应识别控件是否应该具有值?
最佳答案
你的模型类被命名为*Form
让我困惑了一分钟;我可能会像这样命名表单(或TestView
)并使用TestModel
作为模型类:)
如果 View /表单的角色是呈现数据,那么模型的角色就是数据。 TestProduct
就是这样一种数据:其有效性也是可呈现的数据。您可以考虑这个元数据,并使用一些TestModelValidator
类来实现一些IModelValidator
接口(interface),可能如下所示:
Public Function IsValid() As Boolean
End Function
...但这可能有点过分了。如果我们擅长让模型负责数据及其验证,那么模型类可能如下所示:
Option Explicit
Private Type TState
ValidationErrors As Collection
ProductName As String
'...other state members
End Type
Private this As TState
Private Sub Class_Initialize()
Set this.ValidationErrors = New Collection
End Sub
Public Property Get ProductName() As String
ProductName = this.ProductName
End Property
Public Property Let ProductName(ByVal value As String)
this.ProductName = value
End Property
'...other model properties...
Public Property Get IsValid() As Boolean
Dim validProductName As Boolean
validProductName = Len(this.ProductName) <> 0
this.ValidationErrors.Remove "ProductName" '<~ NOTE air code, verify this works
If Not validProductName Then this.ValidationErrors("ProductName") = "Product name cannot be empty"
'...validation logic for other properties...
IsValid = validProductName
End Property
Public Property Get ValidationErrors() As String
ReDim result(0 To this.ValidationErrors.Count)
Dim e As Variant, i As Long
For Each e In this.ValidationErrors
result(i) = e
i = i + 1
Next
ValidationErrors = Join(vbNewLine, result)
End Property
现在 View 可以操纵模型 - 而不是这里发生的事情:
Private Sub CommandButton1_Click() With Me If .CheckBox1.Value = True Then DataEntryForm.TestProduct = .CheckBox1.Caption ElseIf .CheckBox2.Value = True Then DataEntryForm.TestProduct = .CheckBox2.Caption
不要查询 UI,监听 UI 告诉您发生了什么 - 处理每个控件的 Change
事件,然后让模型驱动控件的状态用户界面:
Private Sub CheckBox1_Change()
If Me.CheckBox1.Value Then
Model.ProductName = Me.CheckBox1.Caption
Validate
End If
End Sub
Private Sub CheckBox2_Change()
If Me.CheckBox2.Value Then
Model.ProductName = Me.CheckBox2.Caption
Validate
End If
End Sub
Private Sub CodeBox_Change()
Model.Code = CodeBox.Text
Validate
End Sub
Private Sub DescriptionBox_Change()
Model.Description = DescriptionBox.Text
Validate
End Sub
Private Sub Validate()
Dim valid As Boolean
valid = Model.IsValid
Me.OkButton.Enabled = valid
Me.ValidationErrorsLabel.Caption = Model.ValidationErrors
Me.ValidationErrorsLabel.Visible = Not valid
End Sub
希望对你有帮助!
编辑/附录:使用模型状态来驱动此类控件是否可见;您的模型类应该封装尽可能多的逻辑(而不是将其放在表单的代码隐藏中) - 这样您就可以轻松地针对模型类编写测试来验证和记录其行为,而无需手动测试每个边缘情况每次您做出可能会破坏某些内容的更改时,都会以实际形式出现! 换句话说,如果 View /表单需要具有供应商名称的集合来填充组合框或创建尽可能多的复选框控件,那么封装这些数据就是模型的工作。
换句话说,如果您需要一个标志来驱动某些模型逻辑,请将该标志作为模型状态的一部分:
Private Type TState
'...
ProductTypes As Collection
End Type
Public Property Get HasProductTypes() As Boolean
HasProductTypes = this.ProductTypes.Count > 0
End Property
Public Property Get ProductTypes() As Variant
Dim result(0 To ProductTypes.Count)
Dim pt As Variant, i As Long
For Each pt In this.ProductTypes
result(i) = pt
i = i + 1
Next
ProductTypes = result
End Property
Public Property Get IsValid() As Boolean
Dim validProductName As Boolean
validProductName = Len(this.ProductName) <> 0
this.ValidationErrors.Remove "ProductName" '<~ NOTE air code, verify this works
If Not validProductName Then this.ValidationErrors("ProductName") = "Product name cannot be empty"
'...validation logic for other properties...
Dim validProductType As Boolean '<~ model is valid with an empty ProductType if there are no product types
validProductType = IIf(HasProductTypes, Len(this.ProductType) > 0, True)
IsValid = validProductName And validProductType
End Property
关于vba - 使用 MVP 方法验证动态控件的用户表单输入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61976881/