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 Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

command buttons not refreshing form

Status
Not open for further replies.

T17Rax

Technical User
Jun 11, 2015
39
GB
I've a form that's being called via parameters. (I'm wondering if this is the problem).

I've two command buttons; forward & backward. The code works - They skip through the records up and down as they should but the form isn't updating the form.

Now, is it because I'm missing something silly which should tell the form to update itself after a skip command has been called? I'd have thought that would have been the "ThisForm.Refresh" method? But even that's not doing the job.

On a standalone form, you can select the control source but this is a form called with parameters so the labels and textboxes are already occupied by variables. Is this why? But the public variables should have registered what it had been assigned and keep it's values throughout the entire application.

Hmm...
 
> But the public variables should have registered what it had been assigned and keep it's values throughout the entire application.

That's where you have a much too complex idea of what setting a variable means.
var= alias.field is not binding to the field, it's just copying over the current records field value as the var value.
Even if VFP variables would be objects, like everything is an object in C#, Ruby or other languages, that wouldn't make setting the variable binding it to something else by reference.

If you bind controls to a variable, only variable changes change the controls display value. A change of current record does nothing, even if that is where the variable came from. If thinks would work the way you think, how could anything change independant of other things, then sooner or later everything would just be a mesh of references.

Bye, Olaf.
 
I'm not sure where your public variables are coming into the picture. Given that "The code works - They skip through the records up and down as they should", that implies the controls are bound to fields in a table (as you would expect).

If that's so, then you should be able to execute commands like [tt]SKIP[/tt] and [tt]SKIP -1[/tt] in your buttons, followed by [tt]THISFORM.Refresh[/tt], and you should see the values of the fields in the relevant record. If that's not what you are seeing, there must be some other code or setting that is preventing it. Without actually seeing your code, it's hard to know exactly what is going wrong.

Another common mistake with navigation buttons is that they don't necessarily operate on the correct alias. A [tt]SKIP[/tt] won't necessarily skip in the table to which the controls are bound, but rather in whatever work area is selected at the moment the user clicks the button. Since you cannot be sure what the user was doing immediately before clicking the button, you should always explicitly specify the alias, either by [tt]SELECT[/tt]ing first, or using the [tt]IN[/tt] clause.

Mike


__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Mike,

T17Rax didn't tell what controls are really bound to and how he sees change in the records. He might check in browsing the table.

T17Rax said:
this is a form called with parameters so the labels and textboxes are already occupied by variables
This is what I interpret as hint on the control being bound to public variables. Maybe even not that, maybe the controls values were just set to passed in values.

But what can be said from the description of the form not updating after skip +/- 1 and calling Thisform.refresh, is that the controls are NOT bound to table fields. That's almost certain.

Bye, Olaf.
 
T17Raw, just a hint to see whether your controls are bound the way you'd expect and do it, look into the controlsource property. If that's empty a control will constantly display what is put into its value initially and you can edit that, but it's not in any way connected to a table.

And as you seem to want to set a value to a passed in value and still have a binding, cauious! If you both bind and set value you programmatically edit the record you bind to.

A way to display a certain record in a form at start would be passing in an Id or the RECNO() to let the form locate the record at init and show it.

Bye, Olaf.
 
Reading what Olaf initially had written, I decided to go back to square one and start logically.
From basic understanding, binding a textbox to a ControlSource (which would be alias.field) would show an individual record which is highlighted in the table.

So, the command button codes are as follows:
cmdforward
Code:
skip 
if eof()
go top 
endif
thisform.refresh

cmdbackward
Code:
skip -1
if bof()
go bottom
endif
thisform.refresh

My form is being called with a few parameters to carry the variables through. (This is where I noticed what I was doing wrong) As I call the form, I also send the required data to a cursor:
Code:
pinNumber = ALLTRIM(THISFORM.Text10.Value)

SELECT audit1
SET FILTER TO dat = DATE() 
LOCATE FOR Idno = VAL(pinNumber)

IF NOT FOUND()
  MESSAGEBOX("No Such Account")
ELSE
store substr(coins,1,2) to tenPence
store substr(coins,4,2) to twentyPence
store substr(coins,7,2) to fiftyPence
store substr(coins,10,2) to onePound
store substr(coins,13,2) to twoPound
store substr(coins,16,2) to fivePound
store substr(coins,19,2) to tenPound
store substr(coins,22,2) to twentyPound
store substr(coins,25,2) to fiftyPound
STORE balprior TO cashBefore
STORE cash TO totalCoins

SELECT * FROM audit1 WHERE dat = DATE() AND idno = VAL(pinNumber) INTO CURSOR revalquery
CALCULATE SUM(cash) FOR Idno = VAL(pinNumber) TO valueadded

revalquery = SYS(3)

DO FORM c:\code\revalcounter\specificaccount WITH tenPence, twentyPence, fiftyPence, onePound, twoPound, fivePound, tenPound, twentyPound, fiftyPound, totalCoins, valueadded, cashBefore
ENDIf

In the called form, it shows the data from the parameters but I noticed that even though the called form it selects the cursor via SELECT revalquery, the buttons were focusing on the audit1 table rather than the cursor. So even though the record pointer was navigating through the cursor created, the form objects would change the values, if the audit1 table was selected. This was only tested on a textbox to show the full cell. The substr() functions doesn't appear to agree on control binding.

With this issue in mind, if it's possible, how do you bind the textbox object to a cursor/query?
 
how do you bind the textbox object to a cursor/query?

I think Olaf's already answered that. You set the textbox's ControlSource property to the field within the cursor.

So, if your cursor is named Revalquery, and the field you want to bind to is named TenPence, then you set the ControlSource to Revalquery.TenPence.

But, in that case, you wouldn't need to pass the parameters. Your form would pick up the values from the cursor.

In fact, you could improve the entire architecture by creating the cursor in the Load event of the form (the one containing the bound textboxes), and not pass any parameters at all.

One other point: By binding the controls to fields in the cursor in this way, you will see the correct values as you skip through the table, but you won't be able to edit the values. That's because the cursor will be read-only. You can change that by adding a READWRITE clause to the SELECT that creates the cursor, but you would still need to save the edited values to an actual table.

Mike



__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Another point: Read what I wrote earlier regarding the code in your buttons. As it stands, you cannot be sure which table or cursor you are navigating. You should either [tt]SELECT[/tt] the appropriate work area before you do the [tt]SKIP[/tt], etc. Or, use the [tt]IN[/tt] clause to specify the work area (and also pass the alias as a parameter to BOF() and EOF().

Mike



__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
The cursor is named revalquery, so you bind to "revalquery.field"

If you query before calling a form, the form has to work in the same datasession, if the form has a private datasession you have no access to the query result. Therefore the norm is doing a query in form init or even in the dataenvironment code, not in the caller form. The aspect of a form being self contained, working on its own, plays a role here. What the form needs as input then only is the pinNumber.

T17Rax said:
The substr() functions doesn't appear to agree on control binding.
You still don't gripe what you're doing here. You copy a field substring into a variable. You don't store the expression to the variable, you store the result of the expression to the variable.

This causes no binding whatsover. Binding is done via controlsource only. Nothing in these lines has a controlsource. Variables are nothing but the name for a value you store in them, in this case you do this very literally via the STORE command. The expressions used take a substring of a field. Just because the field is the source of a value also doesn't cause any binding. A field also is not a control having a controlsource, that's exclusive to controls. If you want to forward the expressions you would need to store "substr(coins,1,2)" to tenPence for example, to set the tenPence variable to the expression. Passing that in also does nothing at first, you then would need to set some controls controlsource property to tenPence (and not to "tenPence", that would bind to the variable and show the expression in the textbox, not its value).

You don't show what happens to the parameters passed in, so it's not clear what happens and even passing in the expression instead of its value could still not cause the binding you want. I'm quite sure it wouldn't work, because assume the current situation would result in a string like "10", setting a controls controlsource to "10" wouldn't work, it would error. In the best case you display 10, as eval("10") is 10. A controlsource has to be an expression, typically a fieldname or tablename.fieldname to be twoway, more complex expressions need to be put into brackets to work or cause "... is not a variable" error.

So besides passing in the wrong thing, even if you mend that and mend what you do in init it's a waste, as this could be done at design time once and for all. You only would need to pass in the expressions to which controls should bind to, if your form would be very general purpose for many different data sources. Dynamic forms like that are a thing I would rather avoid, this is not elegant, this is bound to be halfway dynamic enough to do a few things and then get stuck if one time you need further controls and other times not. Form design goes hand in hand with data design and table changes lead to form changes and vice versa, as the database is the persistence of data and the form is its representation and human interface to data. Don't try to cover generic cases here you can use to display any data just by passing expressions in. That may sound like a universally working thing, but you lose control of it- no pun intended here.

Last not least, even when going that route, expressions with functions don't allow two way binding, your controls can only display the expression results, you can't bind one textbox to the leftmost character of one field and the next control to the next two chars. The simple reason is while you can [tt]STORE field TO control.value[/tt] and VFP can do the inverse [tt]REPLACE field WITH control.value[/tt] VFP couldn't apply the same simplistic ionversion [tt]REPLACE substring(field,1) WITH control.value[/tt]. What would be needed to write back the changed single character is [tt]REPLACE field WITH control.value+substr(field,2)[/tt]. There are other REPLACE command that would do that, but surely not the simple swap of value and controlsource expression. Surely there is no simple logic turning your thinking into reality on a sub field level. You expect too much intelligence of the mapping here.

To be clear, the replace statements are not what VFP executes behind the scenes, but you could imagine that happening when a control reads a value for display and write back its value in the valid event. The controlsource can only work in both read and write directiony for controlsource expressions that can be put into both the [tt]STORE exression To control.value[/tt] and [tt]REPLACE expression WITh control.value[/tt] commands. The REPLACE is the limiting facotr, as it can only address fields. It's not the full truth as you can bind to variables, you can bind to "tenPence" and that would mean the write back is done by [tt]STORE control.value TO tenpence[/tt].

And then finally, if you bind to a cursor, that's readonly binding anyway, even if it wouldn't be binding to an expression it's still one way. If you want to save back edited changes you need to bind to something updatable, and that should either be the DBF itself or an updatable cursor, which writed back to the DBF. There are half solutions only enabling the control to write but not write back to the DBF, eg querying INTO CURSOR somecursor READWRITE.

You're far off from what you want in my interpretation, unless you only want to display something.

Bye, Olaf.
 
While waiting for the response, I had found out how to bind controls to objects from cursors - which was to have a cursor in the datasession to begin with!
I don't think I've explained my question very well and I apologise for that.

I understand the purpose of controlsource. It can be one field from one table per object. I was wondering how the command buttons were able to pick up on the cursor unless I physically selected audit1, the form would not show the results.

I agree with mike in that perhaps it would have been a better idea to identify which table the commandbutton should work on.

The general aim of my application would be readonly - gather a few records where the filter applies from the select query, place them into a temporary cursor and release the cursor when finished browsing.
The table audit1 was written by a former developer who used to work for the company I am with now. In one of the fields, it's got 20+ characters which count the coins which look like so:
"00,00,00,00,00,00,00,00,00" - most left being 10p, most right being £50.
The substr() method was applied to my form to segregate the individual coinage characters - understanding that controlsource meant one field per table, I had hoped that there was a possibility to have a substrings of that field into a cursor, and then displayed via controlsource. So where I had written "store substr(coins,1,2) to tenPence", I knew that "00,00,00,00,00,00,00,00"(in bold) was stored as the variable "tenPence".
I guess what I could do is store the tenPence result into another table as a separate field, i.e, 10p, do the same with the rest and call those new fields in the controlsource.

I do tend to over-complicate things!
 
If you only want to display substr(coins,1,2), then you can set controlsource = "(substr(coins,1,2))", that works. It's just readonly. And it'll always work, you don't need to pass this in, it'll always be that for the tenPence textbox.

As already said you should change the form you call to have LPARAMETERS tnPinNunber and then do the query inside that form:
Code:
SELECT * FROM audit1 WHERE dat = DATE() AND idno = tnPinNumber INTO CURSOR revalquery

You may display the "No Such Account"message, if _TALLY=0 after the query, or if RECCOUNT("revalquery")=0.

I don't have any idea, what you intend to do with revalquery = SYS(3). If you think you direct the cursor to that file name you're misunderstanding at least two things again. You create a new variable named revalquery, which is not influencing the revalquery cursor, revalquery is an alias name, a name of a workarea, it's neither a file name nor an object name nor a variable name. If revalquery would be a variable name holding the cursor in your imagination or understanding of things, what should setting it to SYS(3) do? If you set a variable to "1,2,3" (representing a series of values) and then to Sys(3) what happens? Do you think "1,2,3" will be stored in the filename Sys(3) creates?

It would be really interesting to know what you have done in your life before programming. What have you done so far?

You can have several different things with the same name, eg an alias name, field name and variable name and property name can be the same. Also control names can occur many times in the same form, if they are each in different containers. It doesn't make things easier to understand. What is the intention of code? Good code has comments, best code is self explanatory, but that's the saying of quite pretentious experts expecting every developer to understand their code as their code is perfect. Every person thinks different and even very obvious code should be commented. Comments are often the first thing I write, then the code doing what the comments say are inserted in new lines between comment lines and that way of writing code means first putting down your path of the solution in natural language without looking up references in every single line you write. You can wite down the intention of the code you want to write very easily and then you don't get distracted and carried away, you first write down a full thought and recipe, then do it.

By the way: The reason your buttons worked without SELECT audit1 or SELECT revalquery is quite simply because the query result is selected and if you don't select anything else you work on this single workarea. But the advice surely is good for the future, when your form uses two or more datasession workareas, tables, cursors - aliases and you switch.

I now think I know who you are. You still have a lot to learn in regard to understanding VFP and why the code you inherited may not be as bad as you judge it to be, if you know how to handle it.

Bye, Olaf.
 
Olaf,

That is some really good advice there!
The parameters was something I have touched upon but as you've mentioned, in general, and I agree, I have much more to learn. Once I called the parameters and saw the data being passed to the called forms, for me I saw it working and that was a good enough result to keep going.

"revalquery = sys(3)" - this was something I read somewhere about calling a cursor via parameter. It didn't work but I had forgotten to remove it.

OlafDoschke said:
It would be really interesting to know what you have done in your life before programming. What have you done so far?
Not very much I can tell you that! I'm still young, in my first IT job. Studied IT in college on the hardware side of things and went on to university to learn network engineering/systems administration - with these qualifications I got an entry-level job in IT support. While I was at college, I refused to learn anything to do with programming because I couldn't grasp the logic and it was frustrating - boy did I regret that... 6 months later in my job I was asked if I wanted the software development/programming job, providing I taught myself about VFP. (Could be a while off yet though, lol). That request was due to changing circumstances within the company. In my spare time I try to learn it where I can and give myself little tasks. It's enjoyable! But I knew nothing about programming before I got my job.
So by browsing the web, looking at old source code I have access to and trying it all out myself, I've been able to teach myself what I know now. It may not be the best way of structuring code, but there's always room for improvement somewhere.

Comments - that was a good point to make! It's something I seem to forget. Just because I know what I'm doing doesn't mean somebody else will. As you say, it may be good use to write down the objective, that way the task is always on focus.

My name used to be VibrantSeeker but I had decided to change it. I worried that the name sounded too immature to be asking requests of such a mature topic.

Thank you.
 
This is a bit of an aside, but I would probably have handeld the extraction of the coinage counts in a different way.

Given that in each record you have a field that contains a string in the form "00,00,00,00,00,00,00,00,00", it would be easy to copy the counts into an array. If audit1.Coins is the field containing the counts, you could do this:

[tt]ALINES(laCoins, audit1.Coins, 0, ",")[/tt]

That will generate an array, laCoins, whose first element contains the count of tenPence, the second element contains the count of twentyPence, and so on. You would generate the array after you navigate to the next / previous record, and use the array elements as the control sources.

I am not suggesting that you should make this change now, as you have already put some effort into getting your existing form working. I am mentioning it more for your own information, and for future reference.

A couple of other point:

I had found out how to bind controls to objects from cursors - which was to have a cursor in the datasession to begin with

That is essential. If the cursor is not in the form's data session, the form (and therefore the controls within the form), do not know that the cursor exists, and so cannot bind to its fields. But, if that was the case, you would have seen error message, rather than simply not seeing the fresh values.

My name used to be VibrantSeeker but I had decided to change it. I worried that the name sounded too immature to be asking requests of such a mature topic.

Well, of course it's entirely up to you which handle you use for the forum, but personally I would think VibrantSeeker is much more meaningful than T17Rax, which is completely impersonal. I might be in a minority, but I feel a bit silly addressing someone as T17Rax. I think a conversation is more personal when you can address the other person as Olaf or Griff or Dan or Vilhelm or whatever. It would be good if you felt able to include your real forename in your signature. But of course is it entirely up to you, and you are perfectly free to remain anonymous if you really want to.

Mike







__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
I've now been able to get the form working as I'd hoped it to, following the advice and being able to see how it works. As it is usually the case, it was the last bit that was needed finalize it all and finish it off.

I must thank you both for your help and advice and I hope that I will be able to put it all to use at some point in the near future.

Thank you,
Anthony.

P.s. If I get that programming job, I'll be sure to let you know. Maybe then I could be on the contributing end of this website rather than requesting lol.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top