Mandatory field validation

Koushik

Member
Licensed User
Longtime User
Hi,

How to validate mandatory fields(EditText) within a custom dialog? The dialog should not be closed if the mandatory fields are not entered.
Any example would be highly appreciated.

Regards,
Koushik
 

JesseW

Active Member
Licensed User
Longtime User
I don't believe it is possible. According to AGraham, the author of the library, he is not even free from the modal constraints inside the function calls to do things.

I also am in the same position and what I do is put my custom dialog inside a Do/Loop and Exit once the field value passes validation, and it works well. I tried using an edittext and its events never fire while the dialog is visible. This is a limitation of b4a's ability to have modal dialogs.

I hope I said that right :)
 
Upvote 0

Koushik

Member
Licensed User
Longtime User
Hi Jesse,

Thanks for your quick reply.
However, I did not get that "custom dialog inside a Do/Loop and Exit once the field value passes validation".

Would you mind to share any sample code on how you are doing this.

Regards,
Koushik
 
Upvote 0

agraham

Expert
Licensed User
Longtime User
I tried using an edittext and its events never fire while the dialog is visible.
They should do, like in the demo where a Button Click is shown firing. I just tried EditText_TextChanged and it worked fine for me. EnterPressed also fires as long as the EditText is not in SingleLine mode when I assume it is swallowed in closing the keyboard. Playing with the Text property in a TextChanged event risks a stack runaway unless you check an "I'm already doing this" flag in the event code.

However there is no way to prevent a user closing the dialog so the Do... Loop is the only way of ensuring validation.
 
Upvote 0

JesseW

Active Member
Licensed User
Longtime User
Hi Jesse,

Thanks for your quick reply.
However, I did not get that "custom dialog inside a Do/Loop and Exit once the field value passes validation".

Would you mind to share any sample code on how you are doing this.

Regards,
Koushik

Here is a bit of code from my upcoming app. It ensures the input is between 0 hours to 24 hours with 15 minute increments, expressed as .25, .5 and .75.


B4X:
Sub lblRecap_Click
   Dim lbl As Label
   lbl = Sender
   Dim id As CustomDialog2
   Dim ph As Phone
   Dim rentry, msg, idi As String
   'r = row. there are many labels per row, and many rows, like a grid
   'each labels .Tag on a row is the number for that row
   r = lbl.Tag
   Dim pnl As Panel   'panel for CustomDialog2
   pnl.Initialize("")
   pnl.Color = Colors.DarkGray
   Dim idl As Label   'info label for CustomDialog2
   idl.Initialize("")   'it has which day your entering hours for
   idl.Text = lblRecap(r, 0).Text & " " & lblRecap(r, 1).Text
   idl.Color = Colors.Transparent
   idl.TextColor = Colors.White
   idl.TextSize = 18
   idl.Gravity = Gravity.CENTER
   pnl.AddView(idl, 10dip, -10dip, 200dip, 40dip)   'add to panel
   Dim ide As EditText   'edittext for CustomDialog2
   ide.Initialize("idEditText")   'attempt to catch text for validation
   ide.Text = lblRecap(r, 3).Text.Trim   'prefill with hours from main screen
   If ide.Text = "0.00" Then ide.Text = ""   'blank if zero hours
   ide.Typeface = Typeface.DEFAULT_BOLD
   ide.Gravity = Gravity.RIGHT
   ide.InputType = ide.INPUT_TYPE_PHONE
   ide.ForceDoneButton = True   '???
   pnl.AddView(ide, 10dip, 34dip, 70dip, 38dip)   'add to panel
   Dim idc As CheckBox   'checkbox for CustomDialog2
   idc.Initialize("")
   idc.Text = "34 Restart"
   idc.Checked = lblRecap(r, 2).Visible
   idc.TextColor = Colors.White
   idc.TextSize = 18
   idc.Typeface = Typeface.DEFAULT_BOLD
   pnl.AddView(idc, 80dip, 20dip, 160dip, 60dip)   'add to panel
   id.AddView(pnl, 220, 80)   'add panel to CustomDialog2
   ide.RequestFocus      'attempt to show keyboard - failed
' this is the validation loop
' it will loop forever by default and will only exit if validation passed
   Do While True
      'show CustomDialog2
      result = id.Show("On-Duty hours", "Ok", "Cancel", "Help", Null)
      If result = DialogResponse.CANCEL Then
         Exit   'user pressed [Cancel] or [Back]
      Else If result = DialogResponse.NEGATIVE Then
         Msgbox("...display help text...", "Recap Help")
      Else
         idi = ide.Text.Trim   'load edittext text to string
         If idi = "" Then idi = "0"   'if blank, make zero
         'if not a valid number at all, display error
         If Not(IsNumber(idi)) Then 
            Msgbox("Please enter your ON DUTY hours as a numeric value from 0 - 24, with quarter hours expressed as .25, .5 and .75" & CRLF & CRLF & "(ex. 4 or 6.25 or 9.5 or 11.75)", "Recap Error")
         Else
            'now, split the number  into a whole part and remainder
            'perform inside Try just in case of error
            Try
               remainder = idi Mod 1
               wholepart = idi - remainder
            Catch
               wholepart = -1   'any error will cause validation to fail
            End Try
            'test number here
            If wholepart < 0 OR wholepart + remainder > 24 OR (remainder <> 0 AND remainder <> .25 AND remainder <> .5 AND remainder <> .75) Then
               'display error message if validation failed
               Msgbox("Please enter your ON DUTY hours as a numeric value from 0 - 24, with quarter hours expressed as .25, .5 and .75" & CRLF & CRLF & "(ex. 4 or 6.25 or 9.5 or 11.75)", "Recap Error")
            Else
               'validation passed at this point, process the user input
               'I left my code intact for this post so you can see
               ' what I do with it... Note the Exit statement at the end of this block
               idi = lstRecap.Get(r)
               idi = idi.SubString2(0, 8)
               If idc.Checked Then
                  idi = idi & "."
               Else
                  idi = idi & " "
               End If
               idi = idi & NumberFormat2(wholepart + remainder, 2, 2, 2, False)
               lstRecap.Set(r, idi)
               File.WriteList(File.DirDefaultExternal, "Recap.lst", lstRecap)
               Calc
               Exit   'validation passed, user input processed, exit the do/loop now
            End If
         End If
      End If
   Loop
   'drop the keyboard (not necessary with updated Dialogs library)
   DoEvents
   ph.HideKeyboard(Activity)
   DoEvents
End Sub

Hope this helps. See how the only way the Do/Loop exits is if the user aborts by pressing [Cancel] or [Back], or the user input passes validation and is processed. If user validation fails, it just loops again for the user to adjust their input and keeps doing so until they abort or get it right. It's a different validation that what you will need obviously, but here is the code I use and it works well for my app. Hope you get some use from it.
 
Last edited:
Upvote 0

JesseW

Active Member
Licensed User
Longtime User
They should do, like in the demo where a Button Click is shown firing. I just tried EditText_TextChanged and it worked fine for me. EnterPressed also fires as long as the EditText is not in SingleLine mode when I assume it is swallowed in closing the keyboard. Playing with the Text property in a TextChanged event risks a stack runaway unless you check an "I'm already doing this" flag in the event code.

However there is no way to prevent a user closing the dialog so the Do... Loop is the only way of ensuring validation.

I tried. You can see the event name in the code above, but the actual event has been removed since I couldn't get it right. I know I got the event name and sub definition right because it fired twice in prep for the dialog, probably one during initialization and perhaps once while adding it to the panel or the panel to the dialog, but once the CustomDialog2.Show method executed, it would not fire. I just assumed this was part of b4a's blocking mechanism you've mentioned. I'm going to try right now in a brand new clean project, then will post my results...

Edit: ...or was it that I tried to display the old and new text inside a msgbox from within the event sub generated from within the dialog, and that's not allowed. Shall soon see...
 
Last edited:
Upvote 0

JesseW

Active Member
Licensed User
Longtime User
Ok, I stand corrected :) And gratefully so! I'm glad it works. I can now re-write my validation routine to not allow a user to enter invalid characters at all. But that brings up another question: While I was testing, in the TextChanged event, I did this

B4X:
Dim edt as EditText
edt = Sender
edt.Text = Old & New

And it caused a force close every time. Is that because I'm trying to change the text inside the textchanged event, or from the way I accessed the view? I'd like to be able to disallow invalid input by setting the .Text property to Old if the New string fails validation. Any ideas?
 
Upvote 0

agraham

Expert
Licensed User
Longtime User
And it caused a force close every time. Is that because I'm trying to change the text inside the textchanged event?
Yes, that's the stack runaway I mentioned above. If you look in the unfiltered log you will see it.

So something like this.
B4X:
// a global
Dim EditBusy as Boolean
....

Sub Edit_TextChanged (Old As String, New As String)
    If EditBusy Then Return
    EditBusy = True
    ....
    EditBusy = False
End Sub
 
Upvote 0

JesseW

Active Member
Licensed User
Longtime User
I completely missed the stack part of your message. Got it now....

Once again, many thanks :)
 
Last edited:
Upvote 0
Top