私UserForm
は、独自のキャプションと3つの異なるバリエーションを持ついくつかのコントロールキャプションが動的に割り当てられているものを使用します。特にこのユーザーフォームでCheckBox
は、2つのバリエーションで4つが必要であり、1つでは表示されません。
私のデータ検証では、すべての必須フィールドに値が入力されていることを確認します(4つのチェックボックスの1つがチェックされていることを含む)ので、チェックボックスをオンにする必要のないフォームを使用すると(コントロールが表示されないため)、 "Please enter a value into each field." MessageBox.
どうすればこれを回避できますか?
私は過去数か月にわたってUserForm1.Showを読んでいますが、これは私自身と他の多くの人にとって、UserFormとは何かとその仕組みを理解するのに役立ちました。
多かれ少なかれ「完了」したばかりの既存のプロジェクトにMVPパターンを実装しようとしています。
当然、問題や混乱に遭遇すると、グーグルにジャンプし、ほとんどの場合、著者からの十分な回答を含む別の記事またはSOの質問を見つけます。だが。MSForms.Control
そこにあるかもしれないし、ないかもしれないを検証するためのものを見つけることができません-つまり、フォームのバリエーションによっては、フォームで使用されることがあります。
注意してください、私はおそらく自分のフォーム(まあ、単数形)を設計した方法が間違っていると感じているので、その場合は、そのトピックを特定してカバーする回答も最も役立ちます!
これが私の基本フォームです(テストボタンは...テスト用です):
そして、これら3つのボタンのいずれかがクリックされると(ワークシートActiveXコマンドボタン)、以下のユーザーフォームのいずれかが入力されます(キャプションはボタンに対応します)。
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.
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
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?
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]
コメントを追加