Prev: Simple email sample
Next: Graphics4VO to vulcan
From: Paul D B on 4 Nov 2009 02:55 Hi Grant, Stephen Quinn wrote: > > Think about GLOBALS like you would about FILTERS > - avoid wherever possible and if you 'think' you must use them > re-think what your trying to do > Agree about the Globals but not about the filters. A filter is just one of the possibilities we have to show only a subset of records (and I'm pretty sure hundreds of people are using them). There is nothing wrong with it but you have to be aware of the limitations and performance problems. So it has to be used only under specific circumstances. The filter is evaluated at the client PC (unless you are using a client:server solution such as ADS). So, when the file is big, that means a lot of network traffic because the entire file will go over the networklink and this again and again each time the file changes. Also, when the filter returns only a few records out of a file with many records, it might take ages (seconds) to filter them out of the file. You will end up with a very sluggish data browser. So: filter = OK, but file has to be small in size, not too many records ( a few thousands at most), and has to return a fair subset of records. Never use a filter to filter 1 record out of ten thousand others! Other possibilities are: - index scopes, which are extremely fast but less flexible than a filter (a filter can be build with a complex expression). A scope can not always "mimic" this, because it is a matter of having the appropriate index keys. - (temporary) index order conditions: I am a fan of this, because it gives you the flexibility of a filter (any complex expression) and the speed of a scope. You could build a temporary index order with a condition, it will take some time to build the file (but often as fast as applying a filter) and then you got yourself a nice little index order that only contains the records that match your condition. - Fast Text Search - SetRelation might in some cases also do what you want So you see there are many approaches available, and you should pick the appropriate one. How do you know this: trial and error <g> , just experiment with it. I have many applications where I started with filters, rewrote them to use index scopes and ended up with temporary index orders conditions. Nothing wrong with that, it is part of learning VO. -- Paul
From: Paul D B on 4 Nov 2009 03:07 Grant wrote: > > I was getting desperate when I asked about Global variables. Thank you > to Geoff for his comments about proper use of the controlevent > parameter. I was able to extract the field from the chosen record of > the Customer data file by rewriting my CellDoubleClick method. Now I > am able to access the field data as a string and was going to use this > to set a filter. If there is abetter way please let me know. Here are > the lines of code first to creat the first browser and then the > CellDoubleClick callback method to get the desired customer field for > the next browser : > > Method bBrowser_Customer class Shell > LOCAL odtwBrowser AS dtwBrowser > LOCAL odbsServer AS DBServer > odbsServer := bDServer{"C:\CompanyFolder\Customer.dbf",,, > GetRdd("C:\CompanyFolder\Customer.dbf")} > odtw := dtwBrowser{SELF} > odtwBrowser:Show() > odtwBrowser:Use(odbsServer) > and > Method CellDoubleClick(oControlEvent) CLASS dtwBrowser > LOCAL oBrowser AS JobWin > LOCAL oCustBrowser as dtwBrowser > LOCAL cFName AS USUAL > LOCAL cFilter AS STRING > LOCAL oControl := oControlEvent:Control > > cFilter := NULL_STRING > oCustBrowser := oControEvent:Control > cFName := oCustBrowser:Server:FieldGet(2) > cFilter := cFName > > oBrowser := JobWin{SELF:Owner,,,{SELF,TRUE}} > Works down to here. If I comment out the next 2 lines I get the second > browser screen but contains all the records. > oBrowser:SetFilter( cFilter ) > Something wrong here > oBrowser:GoTop > oBrowser:Show() > > Does this make sense? Would you not use a filter here? I'm trying to > bend my head around using relationships but I'm a little lost. I want > to pick a customer and then open a browser with a list of jobs done > for that one customer and be able to edit, print, or create new jobs > in this bbrowser screen. > > Thanks for your help > Grant Hi Grant (again) I always make a Class variable for my DB servers. That way I have them at hand everywhere, in each method of my datawindow. They never go out of scope. I will send you an example of a very small databrowser, which allows to open a detailwindow when doubleclicking a record in which the record can be updated and saved. Might give you some inspiration -- Paul
From: Stephen Quinn on 4 Nov 2009 17:47
Grant Your problem is your doing things out of order. You should create DBServer classes (DBEditor) for every DBF you have in your app and use it wherever you want to open the database. Eg CLASS MyDBServer INHERIT DBServer // In this base class you can put your own methods & override the base ones CLASS Customer INHERIT MyDBServer METHOD INIT( /*parameters*/ ) CUSTOMER // Add the relevant details here SELF:cDBFName := 'CUSTOMER.DBF' Method bBrowser_Customer class Shell LOCAL odtwBrowser AS dtwBrowser LOCAL odbsServer AS DBServer odbsServer := CUSTOMER{} // You should pass in the opened Server to your window odtw := dtwBrowser{SELF,, { odbsServer, {Array of Fields to display} } odtwBrowser:Show() // you should assign the server in the postinit of the window METHOD PostInit( p1,p2,p3 ) CLASS dtwBrowse IF IsArray(p3) and ALen(p3) = 2 SELF:Server := p3[1] SELF:oBB:Use( SELF:Server ) aFields := p3[2] FOR i := 1 upto ALen( aFields ) oCol := bDataColumn{ aFields[i], // + the rest } SELF:oBB:AddColumn(oCol ) NEXT ENDIF Method CellDoubleClick(oControlEvent) CLASS dtwBrowser LOCAL oBrowser AS JobWin LOCAL oCustBrowser as dtwBrowser LOCAL cFName AS USUAL LOCAL cFilter AS STRING LOCAL oControl := oControlEvent:Control cFilter := NULL_STRING oCustBrowser := oControEvent:Control // I suggest you use SYMBOL names instead of numeric values for the fields // Less chance of introducing a bug if you ever change the database field order cFName := oCustBrowser:Server:FieldGet( #FNAME ) // cFName := oCustBrowser:Server:FieldGet( 2 ) // cFilter := cFName // I'm assuming the JobWin{} class openes the Jobs database and assigns it to a bBrowser on the window // Pass into the window the Filter criteria and set it in the postinit oBrowser := JobWin{SELF:Owner,,,{SELF,TRUE, cFName } } oBrowser:Show() The way your doing this will make it difficult to set a relationship like I showed in an earlier post. If you open ALL databases you require at the same time setting relationships is a piece of cake. What I usually did is open ALL databases in the basewindow:postint() put them into an array and pass that array around to any other window that needed access to the servers. All relationships/scopes/etc... were done in the base window not by any new window that was opened Eg anArrayOfServers := ArrayNew(5) anArrayOfServers[1] := Customers{} anArrayOfServers[2] := Orders{} anArrayOfServers[3] := Jobs{} anArrayOfServers[4] := Suppliers{} anArrayOfServers[5] := Invoices{} CYA Steve |