xBrowser How to display customers/city a kind of treeview

xBrowser How to display customers/city a kind of treeview

Postby Otto » Sun Dec 23, 2007 9:38 am

I tried to display – this is easy and working- but if you skip than this seems to be much work.

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)


Image
User avatar
Otto
 
Posts: 6343
Joined: Fri Oct 07, 2005 7:07 pm

Postby nageswaragunupudi » Sun Dec 23, 2007 1:42 pm

For arrays I do like this :
Code: Select all  Expand view  RUN
      oCol:bStrData := {||iif( oBrw:nArrayAt > 1 .and. aArray[oBrw:nArrayAt][1] == aArray[oBrw:nArrayAt-1][1], space(10),aArray[oBrw:nArrayAt][1] )}

I commonly do this for arrays and ado recordsets. We can also do it for DBFs but the code depends on your specifc case.

Ideally we would like if no lines are painted within a group. This can be done but needs one more data item in the column object and some change in the browse's paint method
Regards

G. N. Rao.
Hyderabad, India
User avatar
nageswaragunupudi
 
Posts: 10648
Joined: Sun Nov 19, 2006 5:22 am
Location: India

Postby Otto » Sun Dec 23, 2007 3:51 pm

Hello NageswaraRao,

Thank you for your code.
I tested and it works as suspected for arrays.
Image

Am I right that if you use dbf-files aArray can’t be used?

Do I have to make an own array parallel to get this functionality?

To test I tried with dbf files - I think it is similar to your code but hard coded - I used:

STATIC cCity
func bStrDataFIELD_CITY()
local cTEmpCity := _FIELD->CITY
IF cCity = cTEmpCity
cTEmpCity := " "
else
cCity := _FIELD->CITY
ENDIF
return cCity

But if you skip this does not work and I thought to fill an array parallel with the current scope.
Could you imagine if this will work.

What is the reason why xBrowse distinguish between array and dbf-file. If you look at other languages they make a recordset or a dataadapter and bind the browser to this.
Regards,
Otto
Last edited by Otto on Sun Dec 23, 2007 4:26 pm, edited 2 times in total.
User avatar
Otto
 
Posts: 6343
Joined: Fri Oct 07, 2005 7:07 pm

Postby nageswaragunupudi » Sun Dec 23, 2007 4:24 pm

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.
Regards

G. N. Rao.
Hyderabad, India
User avatar
nageswaragunupudi
 
Posts: 10648
Joined: Sun Nov 19, 2006 5:22 am
Location: India

Postby Otto » Sun Dec 23, 2007 4:27 pm

Sorry, I edited during you answered.
Regards,
Otto
User avatar
Otto
 
Posts: 6343
Joined: Fri Oct 07, 2005 7:07 pm

Postby Otto » Sun Dec 23, 2007 4:42 pm

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
User avatar
Otto
 
Posts: 6343
Joined: Fri Oct 07, 2005 7:07 pm

Postby nageswaragunupudi » Sun Dec 23, 2007 4:51 pm

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.
Regards

G. N. Rao.
Hyderabad, India
User avatar
nageswaragunupudi
 
Posts: 10648
Joined: Sun Nov 19, 2006 5:22 am
Location: India

Postby Otto » Sun Dec 23, 2007 5:46 pm

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
User avatar
Otto
 
Posts: 6343
Joined: Fri Oct 07, 2005 7:07 pm

Postby James Bott » Sun Dec 23, 2007 8:54 pm

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
User avatar
James Bott
 
Posts: 4840
Joined: Fri Nov 18, 2005 4:52 pm
Location: San Diego, California, USA

Postby James Bott » Sun Dec 23, 2007 9:25 pm

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
User avatar
James Bott
 
Posts: 4840
Joined: Fri Nov 18, 2005 4:52 pm
Location: San Diego, California, USA

Postby James Bott » Sun Dec 23, 2007 10:23 pm

Otto,

Here is a working example using a customer class as a subclass of TData. I have also shown the city field so you can see it is working. This is not very efficient as it requires three disk reads to display each record. I think it could be done using static vars to eliminate the extra disk reads.

Image

Here is the code:

Code: Select all  Expand view  RUN
/*
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)

// end
User avatar
James Bott
 
Posts: 4840
Joined: Fri Nov 18, 2005 4:52 pm
Location: San Diego, California, USA

Postby James Bott » Sun Dec 23, 2007 10:52 pm

I 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
User avatar
James Bott
 
Posts: 4840
Joined: Fri Nov 18, 2005 4:52 pm
Location: San Diego, California, USA

Postby nageswaragunupudi » Mon Dec 24, 2007 1:52 am

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
Regards

G. N. Rao.
Hyderabad, India
User avatar
nageswaragunupudi
 
Posts: 10648
Joined: Sun Nov 19, 2006 5:22 am
Location: India

Postby James Bott » Mon Dec 24, 2007 2:26 am

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
User avatar
James Bott
 
Posts: 4840
Joined: Fri Nov 18, 2005 4:52 pm
Location: San Diego, California, USA

Postby nageswaragunupudi » Mon Dec 24, 2007 2:54 am

Yes, true. But lesser client server traffic. Reading related tables is faster than the program sending requests to read.
Regards

G. N. Rao.
Hyderabad, India
User avatar
nageswaragunupudi
 
Posts: 10648
Joined: Sun Nov 19, 2006 5:22 am
Location: India

Next

Return to FiveWin for Harbour/xHarbour

Who is online

Users browsing this forum: Google [Bot] and 19 guests