We had a scenerio where an 800+ line CFL routine was used to determine the optimal machine and materials. It looped through over 4000 options. Even though it was optimized and took only a second, that was still enough that we did not want it running in between every calculation. The user was agreeable to having a button that said “Click to Calculate” that would then run the optimization. We further wanted the button to disappear when it was up to date. This WIKI article describes the way we accomplished this.

I've created a simple product and pricing form for illustrative purposes. The product adds two numbers to produce a result. However, the result is only computed when the button is pressed. The pricing form looks like this: The product consists of the following variables:

• Quantity1 - The first number -
• Quantity2 - The second number
• Quantity3 - The result. (The formula for this is the key.)
• ActionAdd - A yes/no variable that appears to be the “button” to track when it is clicked.

There are three other psuedo-variables, which are stored as properties on Quantity3. A variable can have as many properties as you want, and you don't declare them in advance. However, they can't have a formula. Unlike regular variables, though, you can write them using CFL. These psuedo-variables are:

• LastResult - This stores the previous value of Quantity3
• IsDirty - This stores if changes have occurred since the last calculations.

All of the action to pull this off occurs in the formula for Quantity3 (the result). The CFL code for our example looks like this:

This routine checks if the value of ActionAdd has changed from the last calculation. If it has not changed, we return the current value but set that we might be dirty.

If it has changed, or was never defined, we compute the new one and store that for next time. declare CurrentActionAdd := ActionAdd; declare LastActionAdd := GetVariablePropertyValue(Quantity3, “LastActionAdd”, -1); if (LastActionAdd CurrentActionAdd) then we must have a change or undefined value

`// Make sure we have a value stored`
`declare NewValue := Quantity1 + Quantity2;`
`// Save this value for next time`
`SetVariableProperty(Quantity3, "CurrentValue", NewValue);`
`// And save the setting for ActionAdd for next time`
`SetVariableProperty(Quantity3, "LastActionAdd", CurrentActionAdd);`
`// And clear that we are dirty`
`SetVariableProperty(Quantity3, "IsDirty", False);`
`// And return it as the current value;`
`NewValue;`

else

`// Store that we might be dirty`
`SetVariableProperty(Quantity3, "IsDirty", True);`
`// retrieve the stored value -`
`// make sure you do this last`
`GetVariablePropertyValue(Quantity3, "CurrentValue", -1);`

endif;

```The actual work (where you would put the real work) is the line
[[code_formatpascal|]]

declare NewValue := Quantity1 + Quantity2;

<code>
Everything else is just managing the tracking.  The code is fairly well documented, but here is how it works:
- Determine if the ActionAdd has changed by comparing it to the saved property.
* If ActionAdd has changed, recompute the new value but also update all the tracking property variables.
* If ActionAdd has not changed, return the last value but also set that you may be dirty (since a refresh was triggered by something).
The code for the pricing form looks like this.  I deleted some of the default properties, but Control will recreate those values anyway.```

object DesignerLabel1: TDesignerLabel

`  Left = 260`
`  Top = 73`
`  Width = 84`
`  Height = 25`
`  Alignment = taCenter`
`  AutoSize = False`
`  Caption = 'Result'`
`  Layout = tlCenter`
`  BorderInner = fsBump`
`  DisplayedInfo = pmdValue`
`  ParameterName = 'Quantity3'`
`  VariableName = 'Quantity3'`
`end`
`object DesignerLabel2: TDesignerLabel`
`  Left = 144`
`  Top = 69`
`  Width = 11`
`  Height = 24`
`  Caption = '+'`
`  Font.Charset = DEFAULT_CHARSET`
`  Font.Color = clWindowText`
`  Font.Height = -19`
`  ParentFont = False`
`  DisplayedInfo = pmdText`
`end`
`object DesignerLabel3: TDesignerLabel`
`  Left = 232`
`  Top = 74`
`  Width = 11`
`  Height = 24`
`  Caption = '='`
`  Font.Charset = DEFAULT_CHARSET`
`  Font.Color = clWindowText`
`  Font.Height = -19`
`  ParentFont = False`
`  DisplayedInfo = pmdText`
`end`
`object DesignerSpinEdit1: TDesignerSpinEdit`
`  Left = 88`
`  Top = 70`
`  Width = 47`
`  Height = 21`
`  Max = 999999.000000000000000000`
`  Value = 1.000000000000000000`
`  TabOrder = 0`
`  DisplayedInfo = pmdValue`
`  ParameterName = 'Quantity1'`
`  VariableName = 'Quantity1'`
`end`
`object DesignerSpinEdit2: TDesignerSpinEdit`
`  Left = 169`
`  Top = 72`
`  Width = 47`
`  Height = 21`
`  Max = 999999.000000000000000000`
`  Value = 1.000000000000000000`
`  TabOrder = 1`
`  DisplayedInfo = pmdValue`
`  ParameterName = 'Quantity2'`
`  VariableName = 'Quantity2'`
`end`
`object DesignerCheckBox1: TDesignerCheckBox`
`  Left = 117`
`  Top = 114`
`  Width = 179`
`  Height = 42`
`  AlignmentVertical = avCenter`
`  Caption = '   Click to Calculate'`
`  Color = clHighlightText`
`  Font.Charset = DEFAULT_CHARSET`
`  Font.Color = clWindowText`
`  Font.Height = -16`
`  Font.Name = 'MS Sans Serif'`
`  Font.Style = [fsBold]`
`  ParentColor = False`
`  ParentFont = False`
`  TabOrder = 2`
`  UseCustomGlyphs = True`
`  DisplayedInfo = pmdValue`
`  ParameterName = 'ActionAdd'`
`  VariableName = 'ActionAdd'`
`  VisibleFormula = 'GetVariablePropertyValue(Quantity3, "IsDirty", False);'`
`end`

end

One trick we did was to use make a checkbox look like a button. A button does not have a variable, so we couldn't use it to track when something changed. We had a checkbox, but it looked … inappropriate for the purpose. To make it look like a button, we did a few things:

• Set the background color (Color)
• Centered the text (Alignment and AlignmentVertical)
• Hid the checkbox itself but setting the UseCustomGlyphs to true but not providing any image (glyph) to show.

Presto .. fake button! Finesse There are two more things we did to finesse this further. Added a Warning/Error. What happens if the user changes a value and then closes the form without recomputing. To avoid this, or at least notify the user, we added a check for this in the products warning message. If you are not familiar with this, it allows you to provide warning messages to the user when they click OK to close the form. The screen looks like this: if GetVariablePropertyValue(Quantity3, “IsDirty”, False) then

```''"The value was not calculated with the most recent inputs!"

''```

endif;

Note, you could have also put this in the Error Message, in which case it would not allow the user to close the form until they recalculate first. It's your call on the right place for you. Hiding the Button. When the value was current, we wanted to hide the recalc button (which is really a checkbox). To do this we used the visibility formula of the checkbox and set it to only show when the result is “Dirty”, which is a software term for not-up-to-date. The code for this is: code_formatpascal

GetVariablePropertyValue(Quantity3, “IsDirty”, False);

That's it! Simple, right? Ok, perhaps not the simplest thing but once you get the technique down it's pretty easy to replicate it.

Contributor: Cyrious Software Date: 8/2/2013 Version: Control 4.5+