I would like to share with you a small, but useful object (NVO) named nvuo_parm
which helps in the very common task of passing parameters between application units (for example, between windows). It is extremely important for communication between objects to be easy for developers, type-safe (bugs-prone) and effective (quick), so nvuo_parm
is a good candidate!
Passed parameters are added to the NVO using its public setter methods. The names of these methods are constructed by the pattern uf_set_{type}
where {type} is a type identifier - one or two letters taken from the standard PB variables naming convention (like "s" for string, "i" for integer etc.). Each setter method has 2 arguments: the keyword (in fact, the "name" of the passed parameter - of course, its type is always string) and the value (its type depends on the {type}). The passed parameters are accepted by the destination object using public getter methods (uf_get_{type}
) which has only one argument - the keyword - and returns the passed value (so, the getters return different types).
The following table lists all the public methods of nvuo_parm
:
Parameter's Data Type |
Setter | Getter |
integer | uf_set_i |
uf_get_i |
long | uf_set_l |
uf_get_l |
decimal | uf_set_dc |
uf_get_dc |
double | uf_set_db |
uf_get_db |
real | uf_set_r |
uf_get_r |
string | uf_set_s |
uf_get_s |
char | uf_set_c |
uf_get_c |
date | uf_set_d |
uf_get_d |
time | uf_set_t |
uf_get_t |
datetime | uf_set_dt |
uf_get_dt |
boolean | uf_set_b |
uf_get_b |
any (+ arrays and structures) | uf_set_a |
uf_get_a |
NonVisualObject | uf_set_nv |
uf_get_nv |
GraphicObject | uf_set_go |
uf_get_go |
Window | uf_set_w |
uf_get_w |
DataWindow | uf_set_dw |
uf_get_dw |
DataStore | uf_set_ds |
uf_get_ds |
As you see, special getters and setters are used for for parameters of different types. This approach is type-safe, but if you prefere a generic solution (one getter and one setter for all data types) then use a class named n_parm
(downloadable here) - the new, more lightweight version. It is more convenient in use but, unfortunately, not type-safe: compiler will not prevent you from getting an integer parameter into a string variable... But if that problem doesn't stop you (anyway, you will get a run-time error doing a unit test), that class is easier to use. It will not be mentioned anymore in the page you are currently reading.
Situation: we want to pass parameters from window A to window B. Write in window A (in a function which opens window B):
nvuo_parm lnv_parm
lnv_parm.uf_set_i("birth_year", ii_birth_year)
lnv_parm.uf_set_l("order_id", ll_order_id)
lnv_parm.uf_set_s("first_name", ls_first_name)
lnv_parm.uf_set_s("mid_name", ls_mid_name)
lnv_parm.uf_set_s("last_name", ls_last_name)
lnv_parm.uf_set_dt("order_date", ldt_order_date)
lnv_parm.uf_set_b("record_is_new", true)
lnv_parm.uf_set_a("last_companies_worked", ls_last_companies_worked_arr[]) // array
lnv_parm.uf_set_a("str_address", lstr_address) // structure
lnv_parm.uf_set_nv("nvo_customer_controller", inv_customer_controller)
lnv_parm.uf_set_go("cb_details", cb_details)
lnv_parm.uf_set_w("calling_window", this)
lnv_parm.uf_set_dw("dw_main", this.dw_main)
lnv_parm.uf_set_ds("ds_details", this.ids_details)
OpenWithParm(w_B, lnv_parm)
Open event of window B:
nvuo_parm lnv_parm
lnv_parm = Message.PowerObjectParm
ii_birth_year = lnv_parm.uf_get_i("birth_year")
il_order_id = lnv_parm.uf_get_l("order_id")
is_first_name = lnv_parm.uf_get_s("first_name")
is_mid_name = lnv_parm.uf_get_s("mid_name")
is_last_name = lnv_parm.uf_get_s("last_name")
idt_order_date = lnv_parm.uf_get_dt("order_date")
ib_record_is_new = lnv_parm.uf_get_b("record_is_new")
ls_last_companies_worked_arr = lnv_parm.uf_get_a("last_companies_worked") // array
istr_address = lnv_parm.uf_get_a("str_address") // structure
inv_customer_controller = lnv_parm.uf_get_nv("nvo_customer_controller")
icb_details = lnv_parm.uf_get_go("cb_details")
iw_calling_window = lnv_parm.uf_get_w("calling_window")
idw_main = lnv_parm.uf_get_dw("dw_main")
ids_details = lnv_parm.uf_get_ds("ds_details")
Trim()
med). So, the following names are the same: "first_name", " first_name", "first_name ", " first_name ", but the following are different: "first_name", "first name", "firstname".uf_set_...
with a same name overrides (replaces) the existing (old) parameter having that name and data type. Let's experiment:
uf_set_i("today", 30) // parameter of type "integer" is created
uf_set_i("today", 31) // 30 is lost
li_day = uf_set_i("today") // li_day contains 31
uf_set_d("today", Today()) // absolute new parameter is created - this time of type "date"
li_day = uf_set_i("today") // li_day still contains 31
uf_get_...
methods return NULL (for arrays - an array with zero upper bound) of the appropriate data type if the asked parameter doesn't exist (i.e. no parameter with that name has been ever added, or it was added and NULLified later - see the next remark).int li_null
long ll_empty_arr[]
SetNull(li_null)
lnv_parm.uf_set_i("birth_year", li_null)
lnv_parm.uf_set_a("departments", ll_empty_arr)
n_parm
public string constants for the names of parameters, widely passed in the application, and use them with both uf_add
and uf_get
instead of hard-coded names. For example, if your application deals a lot with invoices then you can create a constant INVOICE_NUM (keeping the value "invoice_num") and write
lnv_parm.uf_set_l(lnv_parm.INVOICE_NUM, ll_invoice_num) // source object's script
il_invoice_num = anv_parm.uf_get_l(lnv_parm.INVOICE_NUM) // target object's script
lnv_parm.uf_set_l("invoice_num", ll_invoice_num) // source object's script
il_invoice_num = anv_parm.uf_get_l("invoice_num") // target object's script
uint
). So, you can pass up to 65535 integers, up to 65535 booleans etc. There is no chance it will be not enough in a real-life application, so the object doesn't check overflow (it would downgrade performance).n_parm
, so you can pass nested constructions of any depth.uf_..._a
), GraphicObject (uf_..._go
) and NonVisualObject (uf_..._nv
) are NOT type safe - on types mismatch you will get a run-time failure instead of a compilation error. If the passed parameter is not really of the functions' type (any, GraphicObject or NonVisualObject accordingly) then two conversions between data types are required. So, don't use them for types having their own setter/getter even if it is possible technically:
uf_..._a
methods to pass strings, integers, user objects etc. - use them only to transport parameters, physically having datatype "any", and also for arrays and structures.
uf_..._go
methods to pass Windows and DataWindows (these types have their own setters and getters).
uf_..._nv
methods to pass DataStores (by the same reason).nvuo_parm
your own uf_set_...
and uf_get_...
functions for other classes if their objects are passed intensively in your application. It will make development more type-secure!nvuo_parm
, so you can pass nested constructions of any depth. For example, you can pack into one nvuo_parm
data for one worker (first and last names, array of departments he has worked etc.), and create an array of such workers to be passed in the outer (main) nvuo_parm
.