Ask Reuben

UNBUFFERED

Why do I have to explicitly display variables after their value changes?

What is difference between buffered and unbuffered mode?

If changing to UNBUFFERED, what do I need to take into consideration?

The concept of Buffered mode is the default Informix-4gl behaviour that we inherited.  It has two key characteristics …

  1. Program variable changes are not automatically displayed to form fields, you need to execute a DISPLAY TO or DISPLAY BY NAME to force the changed value to be displayed
  2. When an action is triggered the value of the current field is not validated and has not been copied into the underlying program variable, instead it is in a buffer accessible with FGL_DIALOG_GETBUFFER() / GET_FLDBUF() / ui.Dialog.getFieldBuffer()

As a result of this, you would typically see code of the nature …

INPUT BY NAME value ...
    ON  ACTION action
        LET value = foo()
        DISPLAY BY NAME value    -- DISPLAY explicitly needed to display the new value in the form

… and …

INPUT BY NAME value ...
    ON  ACTION action INFIELD value
        LET value = FGL_DIALOG_GETBUFFER() -- Need to extract the value user has typed into the field from the buffer
        CALL foo(value)

UNBUFFERED mode was introduced to tightly couple the form fields and the underlying 4gl variables.

When a dialog is in unbuffered mode, you do not need to explicitly execute a DISPLAY for the new value to appear in the form.  The runtime recognises when an underlying variable in an active dialog has changed and will update the display at the next user interaction.

When an action is triggered in unbuffered mode, the value entered in the form field with focus is validated and copied into the underlying 4gl variable.  You do not need to read the buffer.

Note in the above I said that the value is validated.  There is one situation where you do not want this to occur and that is with actions such as cancel, close, help etc.  You do not want when that action is triggered for the runtime to say that the value is not valid and not trigger the action.  Hence for those actions a new attribute validate was added and if that attribute is specified with value=no, the value remains in the buffer and is not validated and the action is triggered no matter what the value.

To experiment more with buffered and unbuffered mode,   I have placed some example code at the end of this article.  First run the program in buffered mode, and note what happens that …

  • when you click add1, the value of c does not change in the form
  • when you click add2, the value of c does change in the form due to the explicit display
  • type a different number in field a and note that when you click myhelp1, the variable a does not contain the new variable
  • type a different number in field a and note that when you click myhelp2, the variable a does not contain the new variable
  • type the letter a in field a, and note that when you click myhelp1, the variable a does not contain the letter a and no error occurs
  • type the letter a in field a, and note that when you click myhelp2, the variable a does not contain the letter a and no error occurs

Now comment out line 10 and uncomment line 11 so that we are now in UNBUFFERED mode and repeat the above series of steps

  • when you click add1, the value of c does change in the form, even though there is no explicit display
  • type a different number in field a and note that when you click myhelp1, the variable a does contain the new variable
  • type a different number in field a and note that when you click myhelp2, the variable a does not contain the new variable
  • type the letter a in field a, and note that when you click myhelp1, an error occurs
  • type the letter a in field a, and note that when you click myhelp2, an error does not occur

There are two ways you can use unbuffered mode.    For individual dialogs, you can add UNBUFFERED to the list of attributes as in that example.  You can also set this mode globally with the method ui.Dialog.setDefaultUnbuffered.

By transitioning to unbuffered mode you will find that you have less lines of code and your code is more intuitive.

  • You can remove DISPLAY statements in your ON ACTION code.  You no longer have to perform the mental gymnastics of thinking what is the underlying 4gl variable, is that what is displayed on the screen?
  • You can remove the code that reads the value from the buffer and instead use the underlying 4gl variable.  What you do have to make sure of is that certain actions such as cancel, close, help have the validate=no attribute defined so that the action is still triggered even if an invalid value has been typed and you are not stuck having to enter a valid value.
  • What you may also find is that you add fields to the dialog statement even though they are NOENTRY.  In this example note how c is NOENTRY but is included in the dialog.  This means that changes to c are updated in the form.

You can read more about buffered and unbuffered modes and data validation at action invocation.

#! askreuben89.4gl

MAIN
    DEFINE a, b, c INTEGER

    LET a = 1
    LET b = 2

    CLOSE WINDOW SCREEN
    OPEN WINDOW w WITH FORM "askreuben89"

    INPUT BY NAME a, b, c ATTRIBUTES(WITHOUT DEFAULTS = TRUE)
    --INPUT BY NAME a, b, c ATTRIBUTES(WITHOUT DEFAULTS = TRUE, UNBUFFERED)

        ON ACTION add1
            LET c = a + b

        ON ACTION add2
            LET c = a + b
            DISPLAY BY NAME c

        ON ACTION myhelp1
            MESSAGE SFMT("a=%1 buffer = %2", a, FGL_DIALOG_GETBUFFER())

        ON ACTION myhelp2 ATTRIBUTES(VALIDATE = NO)
            MESSAGE SFMT("a=%1 buffer = %2", a, FGL_DIALOG_GETBUFFER())
    END INPUT

END MAIN
#! askreuben89.per

LAYOUT
GRID
{
   a [a  ]
+  b [b  ]
     -----
=  c [c  ]
}
END
END
ATTRIBUTES
a = formonly.a;
b = formonly.b;
c = formonly.c, NOENTRY;