Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations derfloh on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

How to interactively change control source of a grid at runtime?

Rifi59

Programmer
Joined
Jul 3, 2025
Messages
5
Hello everyone, I hope i can explain this well, so here it goes.
I have a folder, I've set it to be the default directory while i use fox.
Now I'm building a program that will have form1 as main screen
In that form users will be able to create new directories, in which they will work. (so someone who owns lets say 15 stores would open 15 directories)
I'm currently writting a code that will create tables in those new directories whenever they create a new one.
Form 2 is where they will be able to select other forms depending on what they wanna do (take care of quantity of stuff, accounting etc)
The part i struggle with is how to than set path to that new folder ive created with MKDIR().
So if i start from form 1 and select/create (example) as my directory, i want that to be the directory for the rest of their time using the program and only use tables from that folder, until they turn the program off/or i state in another code that they will open another or close this directory.
Here is the code i have for creating the folders if it means anything :
LOCAL direktorijum, putanja
direktorijum = ALLTRIM(ThisForm.Grid1.Column1.Text1.Value)
putanja = "C:\Trafika\" + direktorijum

* Create the directory if it doesn't exist
IF !DIRECTORY(putanja)
MKDIR (putanja)
ENDIF


ENDIF
DO FORM GLAVNA
And here is the code that i tried putting under this code, in an attempt to set path to that folder

LOCAL putanja1
putanja1 = 'C:\Trafika\' + ALLTRIM(firme.nazivfirme)
SET PATH TO (putanja1)
Currently i do have a code in there that creates 1 table, and it does save it to the path. But when i go further and open the form2
 
Also Its currently set to C:\Trafika but of course that will change to somehow letting them select their own default directory, currently i wanna get this part to work...
 
Last edited:
Is there a control source on the grid? You can set the control source in the LOAD event of the form.

I believe we need more info to understand what you are doing.
 
Hi

first store the full path to your desired directory by :

desireddir = fullpath(curdir()) + desireddirname
desireddir = addbs(desireddir) && adds a backslash to the full path of the directory

then :
set default to (desireddir)

let me know if this works for you

Regards
Ravi
 
SET DEFAULT or CD (change directory) is what you use to set the directory FoxPro first looks into for tables, for example. I see many legacy developers prefer SET DEFAULT, but CD actually does the same and simply resembles what you may know from DOS, just like MKDIR does.

SET PATH is like setting the DOS %PATH% environment variable it sets a directory (or indeed multiple directories when used with ADDITIVE, also accepts a list of directores. That's the wrong instrument to set the default or current directory.
 
Regarding your general idea of having a directory per store. Well, it won't solve the question you have per title, changing directory the grid won't change to the same table name in that other directory, just because you changed directory. Next time you start a form that does USE some.dbf after you CD it will open the table of that directory, so far so good.

If that's all you need, fine. But once a grid recordsource is set it's not depndending on the default path and doesn't switch with changing directory. The grid is bound to the alias and that workareas DBF is fixed until you close and reopen another DBF with same alias again, to which an already running grid won't react well, though.

The simplest solution, therefore, is to actually restart the form after a switch to another directory aka another store, so that store's (aka that directories) data will be used.

Also don't make use of the data environment for that strategy, as that almost fixes the path even though you can make it look in the default directory too, but only after it doesn't find a DBC or DBF that's stored in dataenvironment items in their database and/or cursorsource properties.

So a dataenvironment item will not first look into default directory but what it has been set to during development. Only after not finding them there refer to the default directory and then directories you set with SET PATH like the DOS %PATH% environment variable. And: Also important: If the paths of data environment items exists at runtime for the EXE (i.e. on your development PC, but not only there, necessarily) changing directory actually won't change what DBFs the data environment opens, as the given paths have first priority.

That way a dataenvironemt item differs from the simple usage of the USE command, which will first look into the default directory when you only specify the stem DBF name. So while a dataenvironment item is mainly like a visually designable USE command (with even more bells and whistles like also setting relations), it has other priorities of file opening. In short a DE is nice to be used when you develop locally and for yourself only, it's not well desgned for multi user and even less so multi tenant systems.
 
Last edited:
In my application framework, I define a property under the _VFP variable:

Code:
ADDPROPERTY(_VFP, "DataPath", "")

I set this to the path that contains the database that I am going to be working with. This allows me at design time to build the screens with the database tables referenced in the form's data environment (DE). In the DE's BeforeOpenTables() event I have the following code:

Code:
SetDBCPath(thisform.Dataenvironment, _VFP.DataPath, "Membership")

Where "Membership" is the name of the database. In the code for SetDBCPath() I have the following code:

Code:
FUNCTION SetDBCPath
LPARAMETERS toFormDE, tcDBCPath, tcDBCName
LOCAL lcDBCName, lcFilePath, lnNumCursors, lnNdx
lcDBCName  = FORCEEXT(tcDBCName, "DBC")
lcFilePath = ADDBS(tcDBCPath) + lcDBCName
lnNumCursors = AMEMBERS(lcProperties, toFormDE, 2)
FOR lnNdx=1 TO lnNumCursors
    IF UPPER(LEFT(lcProperties[lnNdx], 6)) = "CURSOR"
        IF ATC(lcDBCName, toFormDE.&lcProperties[lnNdx]..Database) > 0
            toFormDE.&lcProperties[lnNdx]..Database = lcFilePath
        ENDIF
    ENDIF
ENDFOR
ENDFUNC

This will change the database path to the run-time path. So, you would just set the property value _VFP.DataPath to the directory that you want to have opened. You should not have to change the form controls to point to a different location; this is taken care of for you by the DE event BeforeOpenTables().
 
Hi,

In my baseform class I have a method called SetDataBase. Code like below

Code:
Local cObjClass, cObjName, cOldDatabase, cNewDatabase, oReference
Local Array aCursors[1]


If Not (Empty(gcDrive) Or Empty(gcPath))
    = AMEMBERS(aCursors, THISFORM.Dataenvironment, 1)
    For i = 1 to ALEN(aCursors,1)
        If aCursors(i,2) = "Object"
            cObjClass = "THISFORM.DATAENVIRONMENT." + aCursors(i,1) + ".Class"

            If EVAL(cObjClass) = "Cursor"
                cObjName = "THISFORM.DATAENVIRONMENT." + aCursors(i,1) + ".DATABASE"
                cOldDatabase = EVAL(cObjName)
                cNewDatabase = AllTrim(gcDrive) + AllTrim(gcPath) + AllTrim(Substr(cOldDatabase, RAT("\",cOldDatabase) + 1))
                oReference = EVAL("THISFORM.DATAENVIRONMENT." + aCursors(i,1))
                oReference.Database = cNewDatabase
              
            Endif
        Endif
    EndFor
  
_Screen.Caption = gcCaption + " - " + gcDrive + gcPath + " - " + IIF(VARTYPE(oApp.cUser) = "C", oApp.cUser, "")
Endif

Then in each form's dataenvironment "BeforeOpenTables" event I call "ThisForm.SetDataBase".

In my menu I have a pad called "Change database" which calls

Code:
cNewPath = GetDir()

    If Empty(cNewPath)
        = MessageBox("Banque de Données n'a pas été changée!", 48, "Changer Banque de Données")
       
    Else
        If File(cNewPath + gcFile)
            = MessageBox("Le nouveau chemin est " + cNewPath, 48, "Changer Banque de Données")
            gcDrive = Substr(cNewPath,1,2)
            gcPath = Substr(cNewPath,3)
        Else
            = MessageBox("Banque de Données n'a pas été changée!", 48, "Changer Banque de Données")

        Endif
    Endif

The four variables gcCaption, gcDrive, gcFile and gcPath have of course to be defined up the hill

Et voilà!

hth

MarK
 
Last edited:
This is going slightly off topic now about the DE. Sorry, Rif59.

To get back to the grid control:
1. When at runtime an alias/workarea of a tablename the grid is set to is changed by the code
Code:
CD newdir
SELECT tablename && select the workarea still using the table of the previous directory
USE && close that
USE tablename && open the table of the new directory
The grid will go blank when the first USE that closes the gridalias runs and the grid doesn't go for the reestablished alias.
Even if make this in one move and close the old directory table simply by opening the new directory table in the gridalias workarea by just doing
Code:
CD newdir
SELECT tablename
USE tabename && using the table from the new directory and therefore swap it out with the dbf of the previous directory
Even in that case, the grid will go blank.

The help at some point tells a way to change the recordsource of a grid is to make it empty first, then make whatever changes in the workareas and then set the grid.recordsource back. That still does not help with all grid columns and their properties and code you designed in the form designer.

And, by the way, that's also a lesson to learn for swapping out workareas, independent from whether it's for the grid ar just for sake of switching to other data: The USE command works for the current workarea and therefore does not only open what you specify, it also closes what's open in the workarea. Unless you use the IN clause of the USE command and explicitly tell it to USE a table in some other workarea than the currently selected. Typically USE something IN 0 is used to open in any unused new workarea. It also explains why simply USE without any further tablename or other clause added to it closes the currently selected workarea and there is no CLOSE command for that: The pure USE just specifies open nothing in the current workarea, and when that's done it means whatever is currently open in the workarea is closed and replaced by nothing. But therefore, if you want to open something to replace a previously open table, then just skip the close step and do it implictly by just opening the new dbf where the old is open.

But since the grid is reacting that sensitive to a workarea change, even one where you swap out a table with another of the exact same structure, the best way to deal with the directory change, after you do it with CD or SET DEFAULT instead of SET PATH, is to restart the form. And all the stuff about the data environment of forms only will play a role when you actually use the data environment.

The other general recommendable solution is to not bind the grid and even in general nothing to DBF files, but to cursors or views or cursoradapters. A cursor can stay as is and just be populated with data from one or the other directory as you can always stick to it and let the grid stick to it, empty it with ZAP and refill it with INSERT INTO cursor SELECT * from other.dbf or with APPEND FROM other.dbf.
 
Last edited:
May I question the whole app logic?
I would never create 1 to x subdirectories for each store.
Keep all data in one main database, and add a store identifier ("nStore", together with an index) to the relevant tables.
Then just do a SET FILTER TO nStore = 5 and you'll see only that store in your forms.
You could easily open several instances of the same form with just different nStore filters.

Thus no fiddling with pathes and wrongly opened tables, no fiddling with controlsources.
And as added benefit you then can also get sums over all or a group of stores just by appropriate filtering.
 
Last edited:
May I question the whole app logic?
I would never create 1 to x subdirectories for each store.
Keep all data in one main database, and add a store identifier ("nStore", together with an index) to the relevant tables.
One way and doable, of course, but for speed and safety I'd say a lot of folks would like to have the data completely separate.
 
Hello everyone, i wasn't there for a bit but got lots of answers, let me address something for @wOOdy-Soft. In my agency we work in a fox program for accounting of everything you can think off, Its been used for years and I'm just currently trying to learn it bit by bit, so right now i am trying to recreate it myself. Its not just a store, it will have literally 1000+ tables and a lot more stuff than just accounting stuff for a single store, i need to allow the user to create a new directory, and have a code that creates all of the necceseary tables for them to work in, and then they choose what part of accounting they wanna do for that store/business etc...

Im currently just trying to get the program to do what it needs to, its simple after, its just the fact that i need the directory part to work, i saw @Chris Miller give some suggestions so im gonna go off that and see what everyone else told me as well...
 
@Chris Miller i think that there is a bit of a misunderstanding, The form in which the grid is ( the on that i have to change the controlsource ) is only gonna be open after they already have set the default directory that they created, they will never have the opportunity to change it mid-use. Just wanted to say that much, ill now see what else you've said.
 
Hey everyone I'm running into a bunch of other errors now, if any of you could connect via anydesk or discord it would be easier to show you on share screen, I literally have no clue how to explain what i wanna do because of my lack of english, nor how to fix current errors.
 
You have to hope for someone else do make an online appointment.
The form in which the grid is ( the on that i have to change the controlsource ) is only gonna be open after they already have set the default directory that they created, they will never have the opportunity to change it mid-use.
That just makes things easier, so you don't have the problem of changing a running grid - your question title literally suggests that's what you want. So you don't need to, fine, still SET PATH is wrong and CD is the lesson to take out, mainly.

If this is a thing that's typically even only a one time choice, then you could configure it in an INI file, for example, configuration options in XML or a DBF, registry, etc. and it even is questionable you'd have a directory per store, they install a software locally and use it locally, data is not necessary to be all in one place for multiple stores and even if it is, it doesn't need a directory per store. That you project you'll have 1000s of tables doesn't change the necessity or non-necessity for directories.

Otherwise, for changing into a directory the main takeaway is that you use CD or SET DEFAULT to set a directory. You don't have to, to open a DBF or DBC, though, not even that is necessary, you can make USE or OPEN DATABASE open data anywhere without first making it the current/default directory. Typically, you read a location from configuration data and then OPEN DATABASE (m.lcDatabasefile), where m.lcDatabasefile is coming from configuration data. Where to read thata from, you may ask: Well, an INI file will typically reside directly next to an EXE, the setup can define this to be read and writable for users, you could also store such paths in the reigstry and set a default in a setup of your application, too. It can be a path into system directories meant to be used for application data, where you typically add your application name to the root system application data directory for a single or multi users. It can be a database in the network on a network share and it could be some remote database server in the internet (cloud) and you can have multiple of those, also within one directory or one share or one cloud/domain/server. But specifically a software installed locally can have a local database and always in the same directory, as it's local - it doesn't collide with other stores/affiliates/locations. It then can become a job to collect all data centrally, but that's not a configuration problem. Database servers can even store Petabytes of data in one database, you have far less limitations than with DBFs.

Overall you're a bit too fixated on what you make requirements, and in that I second wOOdy, you just show you don't have the experience to realize what options you have and therefore are that fixated. You're giving the answer to change directory anyway, so you're free to stick to your idea. But you could be happy for getting alternative ideas proposed.
 
Last edited:

Part and Inventory Search

Sponsor

Back
Top