Dialog/Form Validation

From wiki.visual-prolog.com

Revision as of 23:38, 2 December 2012 by Thomas Linder Puls (talk | contribs) (7.4)

When a user presses OK in a dialog, it means that something should happen. But this can only happen if the data in the dialog are valid. The dialog/form must be validated. This tutorial describes the validation mechanism supported by PFC GUI. It also shows how to use the validating controls integerControl and realControl, and how to validate other controls in a dialog/form.

In this tutorial, we will show you how to validate controls in a dialog or in a form. First we will describe the validation concept. We will show how integerControl and realControl use the validation concept. Then we consider the validation in a container control. And finally we will add the validation for several usual controls: editControl, listBox and a newly created custom control.

Download source files of the example project used in this tutorial:


Validation Concept

The validation concept is based on the idea: "control values should be valid". The validation check happens on pressing OK button in a dialog or a form, but the validation check can be forced programmatically also.

It is the responsibility of a control to state that its contents is valid, therefore a validation responder can be set to a control.

When a dialog or a form makes the validation check, the controls are tested sequentially, but if one of the tests results in

contentsInvalid(Source, FocusControl, ErrorMessage)

then the validation check is terminated.

Validation in integerControl and realControl

Let us create a form and add two custom controls in it with the classes integerControl and realControl. You can see the result in the supplied example project in the form integerControlAndRealControl.

The generated code will look like:

integerControl_ctl := integercontrol::new(This),
integerControl_ctl:setPosition(92, 4),
integerControl_ctl:setSize(60,12),
realControl_ctl := realcontrol::new(This),
realControl_ctl:setPosition(92, 20),
realControl_ctl:setSize(60,12),

Also we need to add invocation of integerControlAndRealControl form:

predicates
    onFileIntegercontrolRealcontrol : window::menuItemListener.
clauses
    onFileIntegercontrolRealcontrol(Source, _MenuTag) :-
        Form = integerControlAndRealControl::new(Source),
        Form:show().

When we run the project and input non-integral values, we will see a dialog box with an explanation about the mistake.

We can add also onOK responder to retrieve values of the validated controls:

predicates
    onOk : button::clickResponder.
clauses
    onOk(_) = button::defaultAction() :-
        Integer = integerControl_ctl:getInteger(),
        Real = realControl_ctl:getReal(),
        stdIO::write("Integer value = ",Integer, "\n"),
        stdIO::write("Real value = ",Real, "\n").

It is important to notice onOK responder is not invoked if the dialog validation is not successful. And of course, onOK responder is invoked after all validation predicates.

Let us look in file \pfc\gui\controls\integerControl.pro to inspect how the validation is implemented. We can see:

clauses
    new() :-
        editControl::new(),
        addValidateResponder(onIntValidate).
 
predicates
    onIntValidate : validateResponder.
clauses
    onIntValidate(_) = contentsInvalid(This,This, ErrorMessage) :-
        string(ErrorMessage) = checkContent(getText()),
        !.
    onIntValidate(_) = contentsOk.

I.e. if the local predicate checkContent considers the text as an integer, then the validation is successful.

As it is mentioned above we can also force validation programmatically. We will add button TryValidation and code:

predicates
    onTryValidation : button::clickResponder.
clauses
    onTryValidation(_Source) = button::defaultAction() :-
        tryValidateWithErrorDialog(),
        !,
        stdIO::write("All controls are valid\n").
    onTryValidation(_Source) = button::defaultAction().

Validation in Container Controls

For considering the validation in a container control, we should create such control first. Let us create a new control fromTo in our project and add two controls integerControl in it.

We will consider that fromTo control is valid if From value is less than To value, therefore we will add a validation responder to fromTo control:

predicates
    validateFromTo : control::validateResponder.
clauses
    validateFromTo (Source) = control::contentsInvalid(Source,from_ctl,
      "From value must be less than To value") :-
        from_ctl:getInteger() >= to_ctl:getInteger(),
        !.
    validateFromTo (_) = control::contentsOk.

Please notice that control from_ctl will receive the focus if the validation is not successful.

Validation in Usual Controls

Let us create a new control myControl in our project, and then create a form and add three controls in it:

  • edit control;
  • listbox control;
  • custom control of myControl class.

You can see the result in the supplied example project in the form usualControls.

Initialization code will contain the setting of responders and initialization for these three controls:

clauses
    new(Parent) :-
        formWindow::new(Parent),
        generatedInitialize(),
        edit_ctl:addValidateResponder(validateEditCtl),
        listbox_ctl:addList(["First","Second","Third"]),
        listbox_ctl:addValidateResponder(validateListBox).

And validation responders will look like:

predicates
    validateEditCtl : control::validateResponder.
clauses
    validateEditCtl(_) = control::contentsOk :-
        "OK" = edit_ctl:getText(),
        !.
    validateEditCtl(Source) = control::contentsInvalid(
          Source, Source,
          "Edit control expects to have the text 'OK'").
 
predicates
    validateListBox : control::validateResponder.
clauses
    validateListBox(Source) = control::contentsInvalid(
          Source,Source,
          "Selection of the first row is not allowed") :-
        0 = listbox_ctl:tryGetSelectedIndex(),
        !.
    validateListBox(_) = control::contentsOk().
 
predicates
    validateMyControl : control::validateResponder.
clauses
    validateMyControl(Source) = control::contentsInvalid(
          Source,Source,
          string::concat("A marker in the ",Missed,
            " part is mandatory")) :-
        Missed = toString(isMandatoryMarkNotActive(
          mandatoryMark, activatedMark)),
        !.
    validateMyControl(_) = control::contentsOk().

That is, you can see that it is possible to validate a control in really different ways.

References