Is there somewhere a sourcecode example showing how to do such a browse?
For example: I would like to show all customers if there are more than one customer in a city the city is only shown once
(a kind of treeview)


oCol:bStrData := {||iif( oBrw:nArrayAt > 1 .and. aArray[oBrw:nArrayAt][1] == aArray[oBrw:nArrayAt-1][1], space(10),aArray[oBrw:nArrayAt][1] )}
The main requirement is to test whether the current field value = the previous records fieldvalue.
You can achieve this in different ways. What you do may just depend on dbf size, index key and so on.
If the dbf is not too large, and confident that other users will not change during the session, one way to do this may be like this.
Traverse the DBF and store the record numbers of the first occurance of the keyfield in an array. Then
bStrData := {|| iif( ascan( recno(), aUniques ) == 0, fieldget(1), space( <len> ) }
There are many other ways.
Few examples:
you can have a udf to look up previous value and return if the current value is a repeat.
If you create a compound index in a suitable way you can self relate the dbf so that the child points to prev record in the index.
You choose the optimal method depending on each specific case.
Sorry, I edited during you answered.
Regards,
Otto
NageswaraRao,
Thank you for you answer. I thought to fill a temp-array in this part
of xBrowser and then to access this array.
METHOD Paint() CLASS TXBrowse
do while nRowPos <= nMaxRows
// We must also paint some times after the last visible column
---> fill the temp array
...
Do you think this could be possible?
Regards,
Otto
My personal advise is not to change FWH xBrowse code. Even if you change please do not change Paint method for handling values.
Please try to manage it within our application code. I suggested some ways of doing it for dbf tables.
Hello NageswaraRao,
I thought doing that you don't lose much speed because the array will only be a few records < 50 ?.
Best regards,
Otto
Otto,
Here is an idea. Instead of using the CITY field in the browse use a function. Then use bSkip to pass the city field to the function. Inside the function you need a static var, cLastCity. Using this var return the city name of the current record only when it is not the same as cLastCity, otherwise just return nil or a space.
The ideal way to do this is to create a customer class as a subclass from TData, then add a method to handle the above. Otherwise I would suggest using a static function.
James
Otto,
There is a problem with my idea above. I have used this for reports but with browses users can move backwards. This makes it more complicated. For each record movement, you would have to determine if you are at the first occurance of the city or not. So, you would have to skip in the appropriate direction (forward or back) one extra record to determine this, then skip back to the original record. It's more complicated, but I still think it could be done.
James

/*
Purpose: Display city name only on first occurance in a browse
Date : 12/23/2007
Author : James Bott, jbott@compuserve.com
Note : Requires TData class
*/
#include "fivewin.ch"
function main()
local oWnd, oLbx, oCustomer
use customer exclusive
index on upper(city) to cust2
use
oCustomer:= TCustomer():new()
//oCustomer:setOrder(2)
oCustomer:gotop()
define window oWnd title "Test City Group"
@0,0 listbox oLbx fields oCustomer:firstCity(), oCustomer:city, oCustomer:last, oCustomer:first;
headers "City","City","Last","First";
sizes 100,100,100,100;
alias oCustomer:cAlias;
of oWnd
oLbx:bSkip := {| nRecs | oCustomer:skipper( nRecs ) }
oWnd:oClient:= oLbx
activate window oWnd
oCustomer:end()
return nil
//---------------------------------------------------------------------------//
class TCustomer from TData
method new
method firstCity
endclass
//---------------------------------------------------------------------------//
method new()
super():new(,"customer")
::use()
//::addIndex("cust1") // primary key
::addIndex("cust2") // city
::gotop()
return self
//---------------------------------------------------------------------------//
method firstCity()
local cPrev,cCurrent
cCurrent:= ::city
::skip(-1)
cPrev:= ::city
::skip()
return if( cCurrent = cPrev, "", cCurrent)
// endI have looked at this some more, and now I don't think you can do this without three record reads. You have to know what the city for the previous record is and you can't use static vars to keep track of this since you may be moving forward or backward in the browse. When I refer to the "previous" record I don't mean the last record read, but the record before the current record in the current order. So you have to do a skip(-1) to find this, then skip back to your original location.
In a browse the user can skip forward or backward one or more records at a time, so there is no way to keep track of the "previous" record--you just have to read it.
This means the browse will require three times the disk traffic as a browse without city groups. Only testing will tell if this is useable.
Regards,
James
Thats the reason I did not propose a similar approach. It it is a small table I would first scan and store recno's of the records to display. If it is large I would create an index like city + str(recno(),10,0) and set relation to the same table with prev rec. Then the condition is if main->city == child-city show blank else show city
Or we can use a UDF. Degrades performance. function is something like
func isRepeat
local thisval := field->city
local thisrec :=recno()
local lsame := .f.
dbskio(-1)
lsame := ( !bof() .and. thisval == field->city)
dbgoto(thisrec)
return lsame
Nageswara,
> If it is large I would create an index like city + str(recno(),10,0) and set relation to the same table with prev rec. Then the condition is if main->city == child-city show blank else show city.
Wouldn't this also require multiple disk reads, one for the main table and one for child table? Perhaps this is only requires two reads instead of three?
James
Yes, true. But lesser client server traffic. Reading related tables is faster than the program sending requests to read.