We can not create dialogs/user messages inside bEditWhen and bEditValid of xbrowse.
As I suggested earlier, let us use these codeblocks to only for the purpose they are intended/designed.
When a columns EditGet is in focus, any attempt to change the focus to any other window/dialog/control, either programatically or by the user, "cancels" the edit itself. In the above case, the moment the dialog in ChooseValue() function is activated, the EditGet got "cancelled" without changing the value of the cell and xbrowse proceeded with its next actions.
The purpose of the bEditValid is to return whether the value entered by the user is valid of not. You may replace the value by oGet:oGet:VarPut or oGet:cText. It is
desirable not to direcly write to database here because that may have unexpecte side-effects like this.
It is agreed that in some situations we need to provided guided/controlled dataentry and for that purpose, xbrowse specifically provides
EDIT_LISTBOX, EDIT_GET_LISTBOX, EDIT_BUTTON, EDIT_GET_BUTTON.
The same purpose that was intended in the sample provided can be achieved by EDIT_LISTBOX or EDIT_BUTTON. XBrowe provided these facilities for such purposes specifically. In particula EDIT_BUTTON offers the greatest flexibility.
The following example achieves the same purpose as intended in the above sample without any side effects and in lesser yet safer code:
- Code: Select all Expand view
#include 'fivewin.ch'
#include 'xbrowse.ch'
request DBFCDX
function main()
local oDlg, oBrw, oCol
USE CUSTOMER SHARED VIA "DBFCDX"
DEFINE DIALOG oDlg SIZE 500,400 PIXEL
@ 10,10 XBROWSE oBrw SIZE -10,-10 PIXEL OF oDlg ;
DATASOURCE "CUSTOMER" ; // COMPULSORY
COLUMNS "Age", "First", "Last" ;
CELL LINES FASTEDIT NOBORDER
oBrw:Age:nEditType := EDIT_BUTTON
oBrw:Age:bEditBlock := { |nRow,nCol,oCol,nKey| ChooseValue( oCol:Value ) }
oBrw:CreateFromCode()
ACTIVATE DIALOG oDlg CENTERED
return nil
function ChooseValue( nValue )
Local oDlg, oBrw
Local aValues := {1,2,3,4,5}
Local nReturn := nil
DEFINE DIALOG oDlg SIZE 100,300 PIXEL TITLE "Choose"
@ 0,0 XBROWSE oBrw SIZE 0,-50 PIXEL OF oDlg ;
ARRAY aValues COLUMNS 1 HEADERS "Age" LINES CELL NOBORDER
oBrw:CreateFromCode()
@ 110,10 BUTTON "&Ok" OF oDlg SIZE 30,12 PIXEL ;
ACTION ( nReturn := aValues[ oBrw:KeyNo ], oDlg:End() )
@ 130,10 BUTTON "&Cancel" OF oDlg SIZE 30,12 PIXEL ;
ACTION oDlg:End()
ACTIVATE DIALOG oDlg CENTERED
Return nReturn
XBrowse replaces the value in the cell with the value "returned" by the bEditBlock. If the return value is NIL, the cell value is not changed. Returning NIL indicates that the user decided not to change the value.
In the above case, we can also use EDIT_LISTBOX. We can use EDIT_LISTBOX even if the data is not character type.
- Code: Select all Expand view
#include 'fivewin.ch'
#include 'xbrowse.ch'
request DBFCDX
function main()
local oDlg, oBrw, oCol, aList := Array( 80 )
AEval( aList, { |u,i| aList[ i ] := { i, Str( i, 2 ) } } )
USE CUSTOMER SHARED VIA "DBFCDX"
DEFINE DIALOG oDlg SIZE 500,400 PIXEL
@ 10,10 XBROWSE oBrw SIZE -10,-10 PIXEL OF oDlg ;
DATASOURCE "CUSTOMER" ; // COMPULSORY
COLUMNS "Age", "First", "Last" ;
CELL LINES FASTEDIT NOBORDER
oBrw:Age:nEditType := EDIT_LISTBOX
oBrw:Age:aEditListTxt := aList
oBrw:CreateFromCode()
ACTIVATE DIALOG oDlg CENTERED
return nil
Using the recent versions of FWH it needs only 2 lines of code to do this. The same can be done even with older versions but it requires just a few more lines of code.
Some more clarifications may be useful. There can be cases where when user modifies the value of a column, some other column's value may need to be changed based on some calculation.
oCol:bOnChange codeblock is intended for this purpose.
Example usage:
oBrw:Quantity:bOnChange := { |oCol, uOldVal| oBrw:Sales:VarPut( oCol:Value * oBrw:Price:Value )]
if we need to change a field which is not shown in the browse:
oBrw:Quantiry:bOnChange := { |oCol,uOldVal| (oBrw:cAlias)->SALES := oCol:Value * oBrw:Price:Value ) }
( XBrowse already locked the row and is holding the lock)
Some advices:
1) Please indicate the DATASOURCE ( alias/array/etc) "compulsorily" in the @ r,c, XBROWSE command (a) to gain ALL the benefits and features of xbrowse and (b) avoid unexpected side-effects.
2) The commands XBROWSE ... FIELDS and ADD TO oBrw DATA were created years back "only" with the intention to facilitate easier migration from WBrowse and TCBrowse.
But we always recommend using the native XBROWSE ... COLUMNS command. This is the only way to harness full power of xbrowse.
I can only say that the difference is phenomenal.
Here I am giving the same sample posted above with minimal changes required to achieve the same objective safely using EDIT_BUTTON:
- Code: Select all Expand view
#include 'fivewin.ch'
#include 'xbrowse.ch'
function main()
local oDlg, oBrw, oCol
use customer
DEFINE DIALOG oDlg FROM 0,0 TO 500,400 PIXEL TITLE "Test"
@ 0,0 XBROWSE oBrw ;
SIZE 200,250 PIXEL ;
OF oDlg ;
DATASOURCE "CUSTOMER" ; // Added
LINES CELL FASTEDIT
ADD oCol TO oBrw DATA customer->Age ;
HEADER "Age" ;
EDITABLE ;
ON EDIT { |o,x,k| If( k == VK_ESCAPE .or. x == nil, nil, customer->age := x ) } // Added
// VALID { | oGet | MyValidFunc(oGet,oBrw) } // Removed
oCol:nEditType := EDIT_BUTTON // Added
oCol:bEditBlock := { |r,c,o,k| ChooseValue( o:Value ) } // Added
ADD oCol TO oBrw DATA customer->First ;
HEADER "First"
ADD oCol TO oBrw DATA customer->Last ;
HEADER "Last"
// oBrw:nMoveType := 0 // Removed
oBrw:CreateFromCode()
ACTIVATE DIALOG oDlg CENTERED
return nil
// ----------------------
/*
// Function Removed
Function MyValidFunc(oGet,oBrw)
Local nValue := ChooseValue( nValue )
customer->Age := nValue
oBrw:Refresh()
Return .t.
*/
// -------------------------
Function ChooseValue(nValue)
Local oDlg, oBrw, oCol, oBtn
Local aValues := {1,2,3,4,5}
Local nReturn := nil // 1 Changed
DEFINE DIALOG oDlg FROM 0,0 TO 250,100 PIXEL TITLE "Choose"
@ 0,0 XBROWSE oBrw ;
SIZE 50,100 PIXEL ;
ARRAY aValues ;
OF oDlg ;
LINES CELL FOOTERS
// ON CHANGE nReturn := aValues[oBrw:KeyNo] // removed
ADD oCol TO oBrw DATA ARRAY ELEMENT 1 HEADER "Age"
oBrw:CreateFromCode()
@ 110,10 BUTTON oBtn PROMPT "&Ok" OF oDlg SIZE 35,12 PIXEL ;
ACTION ( nReturn := oCol:Value, oDlg:End() ) // Changed
ACTIVATE DIALOG oDlg CENTERED
Return nReturn