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

Class reference in a Form 1

Status
Not open for further replies.

SitesMasstec

Programmer
Sep 26, 2010
508
Brasil
Hello colleagues:

I have this class (container), named paxmarit:
ContainerClass_paqvbw.jpg


Then in the second field (txtVrPgto1) of the class, in Procedure (Event): LostFocus, I have:
Code:
IF cboModoPgto1="CR - Cartão de Crédito"
    txtVrParcCC1 = txtVrPgto1
ENDIF

I put this class in a Form.

When I run the Form, I got this error:
ErroClass_e4psqx.jpg

cboModoPgto1 is the first field.

Do I need to use the class name (paxmarit) in the Form?


Thank you,
SitesMasstec
 
If a combobox control named cboXYZ is directly on a form - and important - directly on a form, you would refer to it with THISFORM.cbXYZ
If it's in a container on a form, you can adress it as THISFORM.containername.cboXYZ.

You see the pattern? You never address a contorl just by its name. That will make VFP think you mean a variable of that name, not a control.

Withiun a class, you don't have THISFORM available as the root and using THISFORM.containername in the class code would imply you later can only name the container by a specific name, also the container will maybe not end up on the form, but in a page of a pageframe on the form. So there is another mode of adressing a control rooting in THIS, which will depend on where your code is.

Intellisnse will help you with that, you can find the control a bit like you can find a directory going up with .. from a base directory to a higher level.

So if you have a combobox cboXYZ and a textbox txtABC that are side by side in a container class, you can write
Code:
If THIS.Parent.cboXYZ.value = "CR - Cartão de Crédito"
    THIS.Parent.txtVrParcCC1.Value = This.Value
Endif

Notice the importance of the keyword "THIS", it always refers to the control that has the code you write, so if you write code into an event of the container, THIS is the container, in the click of a button, THIS is the button.

Try it out interactively, don't just copy &paste this code example. If you open up the LostFocus event of the txtVrPgto1 control in the code editor and write just THIS.PARENT that would make you address the container the textbox is in and when you then write another dot, an intellisense dropdown list lists all the elements including txtVrPgto1, so all elements that are also in the container. You can even reach outside the container, if you already know you'll always put the container class on a page, you can get the page caption from This.Parent.Parent.Caption or, if you program in an event or method of the container THIS isw the container and THIS.PARENT is the page.

It's recommended you always program with such relative object names as the relative postions more often is stable, but there are other strategies to actually not need to address controls themselves at all, address the tables records and fields you use for data displayed in the controls. That's what I recommended just today in Mandy_crw's current thread.

If you write code that sets a value of a control you write code that acts as if it was a user seeing and using the form. Think about it, if a txtVrParcCC1 textbox has a controlsource like VrParc.CC1 then to change the value of the textbox you only need to change the value of the CC1 field. Your code has one advantage to a user only seeing and being able to use the form controls, it has all your programmer knowledge "behind the scenes" of what tables and fields you bind to controls. So make use of that knowledge,, set the table field to a new value, not the textbox.value, let the textbox.controlsource update the value.

So above code could be much shorter:
Code:
If sometable.somefield = "CR - Cartão de Crédito"
    REPLACE VrParc.CC1 WITH  This.Value
Endif

That's assuming txtVrParcCC1 is having a field CC1 of a remote view you call VrParc as its controlsource. I can't deduce what your combobox is bound to, maybe you just hardcode a list of values with AddItem(), but if you also use a controlsource/rowsource for it, selecting an item means setting the record pointer to that record and instead of getting the value from the control you can directly read from that current record.

Your code is able to act on the data directly without using the controls. The controls are the interface a user needs to interact, not your code. Anything you want to show to a user usually comes from a record of a table and you can always use that, there are cases where storing a value to a field would be indirection, for example if you want to show a message in a messagebox, a messagebox is not a control with a controlsource, but usually it is more direct to act on a DBF or cursor or view with code than acting on controls. If you make that your rule of thumb, you'll less often address controls at all and that also frees you from moving controls, arranging them differently, without the need to adapt code to those layout changes.

Chriss
 
Oh, Chris, I have never used Parent... so Parent is for the next level (a container)!

I made the changes following your teaching and my form, with the class inside it, works as expected.

Thank you,
SitesMasstec
 
SitesMasstec said:
so Parent is for the next level (a container)!
Yes, it depends of course.

If you design a button in a container, put that container on a page of a pageframe on a form, then program in the button click
THIS is the button
THIS.PARENT is the container
THIS.PARENT.PARENT is the page
THIS.PARENT.PARENT.PARENT is the pageframe
THIS.PARENT.PARENT.PARENT.PARENT is the form
In the latter cases it may make more sense to address from the other direction. If you need to access the form, THISFORM of course always works. If you know there's only one pageframe on the form, it's perhaps better to address it as THISFORM.PAGEFRAME1. There's no equivalent of THISCLASS that makes you access whatever is the root level of the class, no matter how deep a control is buried in the structure.

This mehtod of addressing has its poros and cons. I know from other languages also based on WinForm you can address controls just by their name as you tried first, but then no two controls are allowed to have the same name. And that is a good rule, but makes it hard to add several of the same containers to a form, even. So no solution is ideal for all cases.

You could add a property oClass to every control and in the init of any class you could use THIS.SetAll('oClass',THIS) and then no matter whether a control is directly on the level of a class, or in another container or page, it would be possible to address the class level element, i.e. the container by THIS.oClass. It's a bit cheating, but it can make addressing other things easier. The ideal way to not need anything like that is to address data, though, not controls, neither siblings, children or parents.

Chriss
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top