To make a DW column not editable (i.e. protected), many developers mark that field as Protected (or insert an expression if the protection is conditional) in the DataWindow Painter. That is very bad practice - I suggest that you use the function uf_protect_col(), whose code is provided below.
It comes in 4 overloads: to protect a single column conditionally and unconditionally, and to protect a few columns at one blow (passed as an array), conditionally and unconditionally as well. The conditional overloads can also be used to remove protection - simply pass the expression "1=0".
Call uf_protect_col() in a script, which initializes your object - like Open event of a window, or the Constructor of a visual UserObject. If you use a framework, then it's likely that you have an event like ue_post_open or ue_post_constructor, which is fired when the object has already been constructed.
Advantages
Using uf_protect_col() (instead of setting the Protect property in the DataWindow Painter) gives you 6 advantages:
@ Looking at the script, you see the whole picture - which columns are protected and when. If you set the Protect property in the DataWindow Painter, that information is hidden - you need to investigate each column's properties.
@ You can build a complicated expression dynamically and assign it in a more elegant way than Modify().
@ In Protect expressions, you can easily use application constants rather than hard-code their values (please read Use constants always).
@ If many columns have a same expression, you can write that expression once and assign to each column, thus preventing code duplication:
@ In expressions, hardcoded in the Protect property, you can access only the DW columns, built-in functions (like IsRowNew()) and global user functions. But uf_protect_col() makes it possible to protect columns using Boolean expressions in PowerScript code, where you can do whatever - for example, call objects' functions (not only global functions). These functions can query the database with no performance issues since such a function is called only once - when your code is being executed. If you query the database in a global function, called from the hardcoded expression, then that query will be executed each time the expression is being evaluated, so you will be forced to add caching of the retrieved data. If the condition is coded in PowerScript (rather than as an expression of the Protect property), then, of course, the unconditional overload is used:
if dw_billing.uf_get_payment_method() <> n_payment_method.CREDIT_CARD then gnv_util.uf_protect_col(this, ls_credit_card_cols[]) end if
@ You can protect fields from any script at any time without using Modify(). For example, you can call uf_protect_col() just after data retrieval, or in ItemChanged. But, in fact, you should avoid that! Do your best to manage protection of all the columns in one script (like Open event) - it will be easier to manage and less bug-prone. Protect from other scripts only if you have absolutely no choice.
Source code
If an error occurs, uf_protect_col() displays an error message. That is done using the straightforward MessageBox(). Even though it's not the best way, many developers will choose it since their applications don't use exceptions. But if you want to use the exceptions mechanism (described here), which is the correct and preferable way, then use the version, provided in the next comment (rather than the version which appears in the next CODE section).
Here is the source code of uf_protect_col() (the error message version, not the exception version) - please add it to your utilities NVO and pass the DW as the adw argument. If you decide to add the function to your ancestor DW, then delete that argument, and use this instead:
/*********************************************************************************************************************************************** Dscr: Protects DW column conditionally, using a logical expression - protect if true, unprotect if false. To protect unconditionally, use the overload without as_protect_expr. To remove protection, call the overload which accepts an expression, passing "1=0" as the expression. ************************************************************************************************************************************************ Arg: adw, as_col_name, as_protect_expr ************************************************************************************************************************************************ Developer: Michael Zuskin > http://linkedin.com/in/zuskin | http://code.intfast.ca/ ***********************************************************************************************************************************************/ string ls_err = '' string ls_modify_expr
constant string BG_COLOR__PROTECTED = "536870912" // 536870912 = Transparent; 12632256 = Silver; 67108864 = Button Face constant string BG_COLOR__EDITABLE = "1073741824" // 1073741824 = Windows Background; 16777215 = White
if IsNull(as_col_name) then ls_err = "Column name is NULL." elseif IsNull(as_protect_expr) then ls_err = "Protect expression is NULL." end if
// Set the expression for the Protect property: if ls_err = '' then ls_modify_expr = as_col_name + ".Protect = ~"0~~t if("+ as_protect_expr + ", 1, 0)~"" ls_err = adw.Modify(ls_modify_expr) if ls_err <> '' then if Trim(adw.DataObject) = '' or IsNull(adw.DataObject) then ls_err = "DataObject is empty." elseif adw.Describe(as_col_name + '.Name') <> as_col_name then ls_err = "Column " + as_col_name + " doesn't exist in " + adw.DataObject + "." else ls_err = "Failed to set Protect property of column " + as_col_name + " (in DataObject" + adw.DataObject + & ") to the following expression:~r~n~r~n" + as_protect_expr end if end if end if
// Change the background color to make the column looking not-editable: if ls_err = '' then ls_modify_expr = as_col_name + ".Background.Color = ~"0~~t if(" + as_protect_expr + "," + BG_COLOR__PROTECTED + "," + BG_COLOR__EDITABLE + ")~"" adw.Modify(ls_modify_expr) end if
if ls_err <> '' then MessageBox(this.ClassName() + ".uf_protect_col()", ls_err) end if
return
The overload which protects a single column unconditionally:
/*********************************************************************************************************************************************** Dscr: Protects DW column unconditionally. To protect conditionally, use the overload which accepts a logical expression. To remove protection, call the overload which accepts an expression, passing "1=0" as the expression. ************************************************************************************************************************************************ Arg: adw, as_col_name ************************************************************************************************************************************************ Developer: Michael Zuskin > http://linkedin.com/in/zuskin | http://code.intfast.ca/ ***********************************************************************************************************************************************/
this.uf_protect_col(adw, as_col_name, "1=1")
return
The overload which protects a few columns at one blow conditionally:
/*********************************************************************************************************************************************** Dscr: Protects DW columns, passed as an array, conditionally, using a logical expression - protect if true, unprotect if false. To protect unconditionally, use the overload without as_protect_expr. To remove protection, call the overload which accepts an expression, passing "1=0" as the expression. ************************************************************************************************************************************************ Arg: adw, as_col_names[], as_protect_expr ************************************************************************************************************************************************ Developer: Michael Zuskin > http://linkedin.com/in/zuskin | http://code.intfast.ca/ ***********************************************************************************************************************************************/ int i int li_upper_bound
li_upper_bound = UpperBound(as_col_names[])
for i = 1 to li_upper_bound this.uf_protect_col(adw, as_col_names[i], as_protect_expr) next
return
The overload which protects a few columns at one blow unconditionally:
/*********************************************************************************************************************************************** Dscr: Protects DW columns, passed as an array, unconditionally. To remove protection, call the overload which accepts an expression, passing "1=0" as the expression. ************************************************************************************************************************************************ Arg: adw, as_col_names[] ************************************************************************************************************************************************ Developer: Michael Zuskin > http://linkedin.com/in/zuskin | http://code.intfast.ca/ ***********************************************************************************************************************************************/ int i int li_upper_bound
li_upper_bound = UpperBound(as_col_names[])
for i = 1 to li_upper_bound this.uf_protect_col(adw, as_col_names[i], "1=1") next
If you want to use the exceptions mechanism (described here) rather than simply display an error message, then use this version of uf_protect_col() (don't forget to fill the "Throws:" field in the header with n_ex):
/*********************************************************************************************************************************************** Dscr: Protects DW column conditionally, using a logical expression - protect if true, unprotect if false. To protect unconditionally, use the overload without as_protect_expr. To remove protection, call the overload which accepts an expression, passing "1=0" as the expression. ************************************************************************************************************************************************ Arg: adw, as_col_name, as_protect_expr ************************************************************************************************************************************************ Developer: Michael Zuskin > http://linkedin.com/in/zuskin | http://code.intfast.ca/ ***********************************************************************************************************************************************/ string ls_err string ls_modify_expr
constant string BG_COLOR__PROTECTED = "536870912" // 536870912 = Transparent; 12632256 = Silver; 67108864 = Button Face constant string BG_COLOR__EDITABLE = "1073741824" // 1073741824 = Windows Background; 16777215 = White
if IsNull(as_col_name) then f_throw(PopulateError(1, "Column name is NULL.")) // f_throw(): http://code.intfast.ca/viewtopic.php?t=1 elseif IsNull(as_protect_expr) then f_throw(PopulateError(2, "Protect expression is NULL.")) end if
// Set the expression for the Protect property: ls_modify_expr = as_col_name + ".Protect = ~"0~~t if("+ as_protect_expr + ", 1, 0)~"" ls_err = adw.Modify(ls_modify_expr) if ls_err <> "" then if Trim(adw.DataObject) = '' or IsNull(adw.DataObject) then f_throw(PopulateError(3, "DataObject is empty.")) elseif adw.Describe(as_col_name + '.Name') <> as_col_name then f_throw(PopulateError(4, "Column " + as_col_name + " doesn't exist in " + adw.DataObject + ".")) else f_throw(PopulateError(5, "Failed to set Protect property of column " + as_col_name + " (in DataObject" + adw.DataObject + & ") to the following expression:~r~n~r~n" + as_protect_expr)) end if end if
// Change the background color to make the column looking not-editable: ls_modify_expr = as_col_name + ".Background.Color = ~"0~~t if(" + as_protect_expr + "," + BG_COLOR__PROTECTED + "," + BG_COLOR__EDITABLE + ")~"" adw.Modify(ls_modify_expr)