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

塞缪尔·埃弗森

TL; DR

我使用的UserForm是一种动态分配的字幕,以及一些带有三种不同变体的控制字幕。特别是在此用户窗体上,CheckBox两个变体需要四个,而一个变体不可见。

我的数据验证检查所有必填字段均输入了值(包括四个复选框中的一个已选中),因此在使用不需要选中复选框的表单时(因为控件不可见),我得到了 "Please enter a value into each field." MessageBox.

如何避免这种情况?


在过去的几个月中,我一直在阅读UserForm1.Show,这对我自己以及其他许多人来说,都有助于我理解UserForm是什么以及它如何工作。

我正在尝试将MVP模式实施到现有的项目中,而该项目或多或少只是被“完成”了。

自然,当我遇到问题或困惑时,我会跳到google,在大多数情况下,或者找到另一篇文章或一个SO问题,而作者的答案也足够多。但。我找不到用于验证MSForms.Control可能存在或可能不存在的验证码的方法-即有时在窗体上使用,具体取决于窗体的变化形式。

请注意,我觉得我的表单设计方式可能不对(嗯,单数形式),因此,在这种情况下,标识并涵盖该主题的答案也将最有帮助!

所以这是我的基本表单(测试按钮用于...测试):
用户窗体设计视图片段

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

NEC用户表单 LG用户表格 其他用户形式

Now, my data validation works fine for the NEC and LG forms, but fails when it gets to the Other form. This is because one Product Type CheckBox is required for the NEC and LG products, but not for the Other products and the data validation fails if there is no Product Type.

Here I'll include the CommandButton1_Click (test button) event and the class module code. My data validation is done in the UserForm module but I was recently reading i should put it in the Model so I think I need to move it to the Module doing all the other things.

UserForm Code Module - 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

Class Module - 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

So, more specifically;

The problem lies with DataEntryForm.TestProduct. It is within the IsFormCompleted function as 2/3 forms require this property to have a value, but naturally isn't required for the form without any Product Types.

My thoughts are the easy fix is to create another separate form for the Other Products version which can have a separate data validation function, but I want to try keep maintainability and avoid having more than 1 of this form.

How can I have this type of data validation adapt to recognise if the control should have a value or not?

Mathieu Guindon

Your model class being named *Form got me confused for a minute; I might have named the form like that (or TestView) and used TestModel for the model class :)

If the role of the view/form is to present the data, the role of the model is to, well, be the data. TestProduct is one such piece of data: its validity is also presentable data. You could consider this metadata and go wild and have a some TestModelValidator class implementing some IModelValidator interface that might look like this:

Public Function IsValid() As Boolean
End Function

...but that's probably overkill. If we're good with having the model responsible for both the data and its validation, then the model class could look like this:

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

Now the view can manipulate the model - not what's happening here:

Private Sub CommandButton1_Click()

With Me
    If .CheckBox1.Value = True Then
        DataEntryForm.TestProduct = .CheckBox1.Caption
    ElseIf .CheckBox2.Value = True Then
        DataEntryForm.TestProduct = .CheckBox2.Caption

Instead of querying the UI, listen in when the UI tells you what's going on - handle each control's Change event, and then let the model drive the state of the 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

Hope it helps!


编辑/附录:使用模型状态还可以驱动该控件或控件是否可见;您的模型类应该封装尽可能多的逻辑(相对于将其包含在表单的代码中)-这样,您可以轻松地针对模型类编写测试来验证和记录其行为,而无需手动测试每个极端情况每次进行实际更改时都可能会破坏某些东西!换句话说,如果视图/表单需要具有供应商名称的集合以填充组合框或创建尽可能多的复选框控件,则封装此数据是模型的工作。

换句话说,如果您需要一个标志来驱动某些模型逻辑,请将该标志作为模型状态的一部分:

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

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

您如何使用MVP访问表单的控件

来自分类Dev

MVP模式中的用户输入验证

来自分类Dev

MVP模式中的用户输入验证

来自分类Dev

如何使用Java servlet从用户获取动态输入的表单?

来自分类Dev

具有动态输入的表单验证

来自分类Dev

验证动态添加的表单输入-Vanilla JS

来自分类Dev

动态创建的用户控件中的访问方法?

来自分类Dev

使用哪种HTTP方法来验证用户输入?

来自分类Dev

使用RegEx验证用户输入

来自分类Dev

使用空格验证用户输入

来自分类Dev

使用DialogFragment验证用户输入

来自分类Dev

代码应位于用户控件或使用该控件的表单中

来自分类Dev

使用表单控制器引用动态命名的表单控件

来自分类Dev

验证用户输入/赋予.NET控件状态为OK或NOK

来自分类Dev

仅当使用单独的用户控件时才验证控件

来自分类Dev

WinForms的MVP模式-访问用户输入的正确方法?

来自分类Dev

用户控件中的动态控件

来自分类Dev

验证表单输入字段的正确方法

来自分类Dev

使用指令内部的动态名称验证表单

来自分类Dev

使用UITableView验证动态表单的模式

来自分类Dev

无法使用php验证动态表单

来自分类Dev

动态添加用户控件

来自分类Dev

jQuery验证动态表单输入上的插件不起作用

来自分类Dev

VeeValidate不验证来自动态表单的输入

来自分类Dev

输入填充动态时,AngularJS 表单验证失败

来自分类Dev

验证用户指定的动态生成字段的Django表单

来自分类Dev

使用输入按钮创建动态表单

来自分类Dev

使用动态输入发布到Web表单

来自分类Dev

使用输入按钮创建动态表单