vba - 使用 MVP 方法验证动态控件的用户表单输入

标签 vba userform mvp

TL;DR

我使用一个UserForm,它动态分配自己的标题和一些具有三种不同变体的控制标题。具体来说,在此用户窗体上,两个变体需要四个 CheckBox,而在其中一个变体上不可见。

我的数据验证检查所有必填字段是否已输入值(包括选中这四个复选框之一),因此当使用不需要勾选复选框的表单时(因为控件不可见),我得到了我的“请在每个字段中输入一个值。”消息框。

如何避免这种情况?


我一直在读UserForm1.Show在过去的几个月里,对我自己以及许多其他人来说,这帮助我理解了用户表单是什么以及它是如何工作的。

我正在尝试将 MVP 模式实现到我现有的项目中,该项目或多或少刚刚已经“完成”。

自然地,当我遇到问题或困惑时,我会跳到谷歌,在大多数情况下,要么找到另一篇文章,要么找到一个作者给出了足够充分答案的问题。但。我找不到用于验证可能存在或可能不存在的 MSForms.Control 的方法 - 即有时在表单上使用,具体取决于表单的变体。

请注意,我觉得我设计表单的方式可能是错误的(好吧,单数形式),所以如果确实如此,识别并涵盖该主题的答案也将是最有帮助的!

这是我的基本表单(测试按钮用于...测试):
snip of design view of userform

当单击这 3 个按钮中的任何一个(工作表 ActiveX 命令按钮)时,它会填充以下用户窗体之一(标题与按钮相对应):
Worksheet ActiveX Commandbuttons

NEC userform LG userform Other userform

现在,我的数据验证对于 NECLG 表单运行良好,但在进入 Other 表单时失败。这是因为 NECLG 产品需要一种产品类型 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/

相关文章:

VBA从组中获取真实的单选按钮值

c# - 通知最终用户 Winforms-MVP 和 WPF-MVVM 中的异常

java - 使用 MVP 模式和 OO 原则

sql-server - VBA 将数据推送到数据库中

VBA公式: Variable File Name and Variable Sheet Name

vba - 暂停 VBA 循环以允许编辑工作表(无论有或没有用户窗体)

excel - 在 Excel VBA 中调用用户窗体并根据单击的按钮继续

vba - 无法设置字体类的斜体属性

vba - 选择一系列单元格,并为这些单元格序列偏离的每个数字插入一个空白行

c# - MVP 和丰富的用户界面