我用一个 UserForm
有一个动态分配自己的标题和一些控制标题的三个不同的变化。具体到这个UserForm上,有四个 CheckBox
在两个变化中是必需的,而在一个变化中是不可见的。
我的数据验证检查所有的必填字段都有一个输入值(包括四个复选框中的一个被选中),所以当使用不需要勾选复选框的表单时(因为控件不可见),我得到了我的 "Please enter a value into each field." MessageBox.
如何避免这种情况的发生?
我一直在阅读 UserForm1.Show 在过去的几个月里,我和其他许多人一起,帮助我理解了什么是UserForm以及它是如何工作的。
我正试图在我现有的一个项目中实现MVP模式,这个项目或多或少有 只是 已 "完成"。
自然,当我遇到问题或困惑时,我会跳到google,在大多数情况下,要么找到另一篇文章,要么找到一个SO的问题,并有作者比较充分的答案。但是,我找不到一个用于验证一个人的方法。MSForms.Control
可能有,也可能没有--即有时在表格上使用,这取决于表格的变化。
请注意我觉得我设计我的表单的方式可能是错误的(好吧,单一的表单),所以如果是这样的话,一个能确定并涵盖该主题的答案也将是最有帮助的。
当点击这3个按钮中的任何一个(工作表ActiveX命令按钮),它就会被填充到下面的UserForms中(标题与按钮对应)。
现在,我的数据验证工作正常了。NEC 和 LG 形式,但当它到了 其他 形式。这是因为一个 产品类型 CheckBox
是需要的。NEC 和 LG 的产品,但不是为了 其他 产品,并且如果没有 产品类型.
在这里,我将包括 CommandButton1_Click
(测试按钮)事件和类模块代码。我的数据验证是在UserForm模块中完成的,但我最近在阅读i 应 把它放在模型中,所以我 认为 我需要把它移到模块中去做所有其他的事情。
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
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
功能,因为有23个表单要求这个属性有一个值,但自然不需要表单的 橆 任何产品类型。
我的想法是,简单的解决方法是创建 另一个 分表 其他产品 的版本,它可以有一个独立的数据验证功能,但我想 尝试 保持可维护性,避免这种形式超过1个。
我怎样才能让这种类型的数据验证适应于识别控制的 应 是否有一个值?
你的模型类被命名为 *Form
我一时糊涂了,我可能把这个名字命名为 "我"。形成 那样 TestView
),并用于 TestModel
的模型类 :)
如果viewform的作用是展示数据,那么模型的作用就是,嗯。做 的数据。TestProduct
就是这样一个数据。它的有效性是 还 可观数据. 你可以考虑这样 元数据 肆意妄为,有一些 TestModelValidator
类,实现一些 IModelValidator
界面可能是这样的。
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
现在 观点 可以操纵模型--而不是这里的情况。
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告诉你发生了什么事时--处理每个控件的? Change
事件,然后让模型来驱动UI的状态。
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
希望对你有所帮助!
EditAddendum: 使用模型状态也可以驱动这样的控件是否应该是可见的;你的模型类应该封装尽可能多的逻辑(而不是把它放在表单的代码后面)--这样你就可以很容易地针对你的模型类写测试,验证和记录它的行为,而不需要每次你做一个可能会破坏某些东西的改变时,手动测试实际表单中的每一个边缘情况。 换句话说,如果viewform需要有一个供应商名称的集合来填充组合框或创建尽可能多的复选框控件,那么模型的工作就是封装这些数据。
换句话说,如果你需要一个标志来驱动某个模型逻辑,那么让这个标志成为你模型状态的一部分。
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