Foxpert Software Development & Consulting

Menu

Whitepapers
Downloads
Knowlbits
Guineu

2007-01Jan-25

Egg or Chicken, Properties or Methods?

In Visual FoxPro you can use expressions to initialize properties. These expressions are evaluated only once when you first load a class into memory. The result is stored in the class object. To create an expression for a property, you can either enter the equals sign in the Properties Windows followed by the expression or you press the "fx" button next to the text box on the Properties Window.

I've shown various times that these expressions may include THIS. The reference points to the class object, not the object that you are creating. In fact, THIS doesn't even point to an object of the same class. If you only access properties of the class object, you won't notice a difference.

If you attempt to call a method, though, you need to be aware that properties are evaluated before methods. So what does this mean? Create a new class "Level1" based on Form and store it in Level1.VCX. Add a method "CallMethod" with the following code:

Activate Screen
? "Called ", This.Class

Now add a property nTest and assign the following expression:

=This.CallMethod()*2

In the Command Window enter:

Clear All
Release All
Clear
ox = NewObject("Level1","Level1")
? ox.nTest

You notice two things. The screen is empty and nTest is .F., not 2. The reason for this is that by the time the expression is evaluated, the method code has not yet been attached to the method. CallMethod() exists, but is empty. Now let's drive this one step further. Create a program ShowInfo.prg with the following content

Lparameters toRef
? toRef.Class
toRef.CallMethod()
Return 1

Create a new class Level2 that inherits from Level1. Store the new class in Level2.vcx. Override CallMethod with this code:

Activate Screen
? "Subclass called ", This.Class

Now switch to the Command Window and enter:

Clear All
Release All
Clear
ox = NewObject("Level2","Level2")

This time you can see the following output:

Level2
Called  Level2

ShowInfo is clearly called when the class is loaded. The method call also works, at least kind of. Instead of executing the code in the Level2 class it now executes the code from the Level1 class that hasn't been executed when you instantiated Level1.

What happened is that when you instantiate Level2, Visual FoxPro first creates a class object for Level1. This happens in two steps. First, all properties are evaluated. Then all methods are added. As you requested Level2, Visual FoxPro creates the class object for Level2 next. VFP does so by creating a copy of the Level1 class object including the method code. As before, properties are evaluated first. At this time, CallMethod still contains the code that has been copied over from the class object of Level1.

Only after all properties have been evaluated, Visual FoxPro adds the method code to the Level2 class object. If you create public variable in ShowInfo.prg and store toRef in that variable, you get a reference to the class object. When you execute CallMethod() on this global object after creating an instance, you get indeed the sub-classed code.

So remember: Properties exist before methods do. If you don't want to create these classes yourself, you can download them here.

2007-01Jan-25

TRANSLATE() gotcha

TRANSLATE() is the PL/SQL function that provides the same functionality as CHRTRAN() in Visual FoxPro. The function searches the first string and replaces every character in the second one with the corresponding value from the third string:

SELECT TRANSLATE('12341234','23','XZ') FROM DUAL;

returns

1XZ41XZ4

Like CHRTRAN in Visual FoxPro you can use TRANSLATE() to remove characters from a string. When the third string is shorter than the second one, all characters in the second string that do not have a corresponding position in the third string are removed. There's a major difference to Visual FoxPro, though:

SELECT TRANSLATE('12345','24','') FROM DUAL

You might expect this expression to return '135'. However, Oracle returns NULL. How comes that? Oracle doesn't support empty strings. Empty strings, like the third parameter, automatically become NULL in Oracle. Yet, if any of the parameters for TRANSLATE() is NULL, so is the result. To get around this issue, you can use the same trick we used with IF lines in DOS batch files years ago:

SELECT TRANSLATE('12345','.24','.') FROM DUAL

This expression replaces periods with periods and removes '2' and '4'. By adding an additional character the third parameter doesn't become NULL and you get the result you expect.

2007-01Jan-25

Being fooled by Oracle

PL/SQL is a programming language that is quite close to Visual FoxPro. Many string functions have the same syntax as in Visual FoxPro, such as SUBSTR() or are pretty close like PADR() that becomes RPAD() in Oracle. However, with concatenation there's a huge difference in Oracle. To combine strings you use the "||" operator instead of "+". That would be fine, if the plus operator wouldn't work with strings, too:

SELECT '23'+'45' FROM DUAL;

The result, however, is 68 as a numeric value. When you work with strings and use the plus operator, Oracle converts the string to a number before adding both values. If you pass an expression that Oracle can't interpret as a number, you receive an error message:

SELECT 'A'+'B' FROM DUAL;

ORA-00900: invalid SQL statement

In a real world application, though, you won't use such values hard coded in a SELECT statement. Rather, you have a complex PL/SQL stored procedure and the error message comes up in a huge combined INSERT FROM SELECT statement. If you are used to a language like Visual FoxPro that uses plus for strings, too, you might not immediately spot this error.

2007-01Jan-13

The scope of CREATEOBJECT

Sometimes one gains insights that are absolutely useless. One such piece is the scope of CREATEOBJECT(). The first time you instantiate an object from a class, Visual FoxPro creates a template object with all the properties values that the class defines. If the class definition contains an expression for one of the properties, Visual FoxPro evaluates it at this time once. For any further instance, Visual FoxPro uses the pre-evaluated expression from the class object.

Now the useless part of this article: All class properties are evaluated in the scope of the procedure or method that instantiates the object. This means, that in the class property expressions you have access to every local variable in that procedure:

Local lcVar, loRef
lcVar = "Hi"
loRef = CreateObject("Test")
? loRef.cTest

Define Class Test as Custom
  cTest = m.lcVar
EndDefine
2007-01Jan-12

Tagged

So much for keeping this strictly technical... Craig Berntson and Ted Roche tagged me in their blogs. Which means I'm now participating in a cocktail party game initiated by Jeff Pulver and I don't even know him except for five things. So, here are the five things about me you didn't (want to) know before:

  1. I collect Walt Disney comics. From the money I made with my first FoxPro program I bought the first comic in my collection. Today I've got a little less than 1,000 issues.
  2. In school I was the last boy in my class that got a computer.
  3. I got my driver's license in 2004, but still prefer trains over cars.
  4. I love cooking. It's hard to understand why people buy food where you need a PhD in chemistry to understand the list of ingredients.
  5. Unlike many other programmers I never really learned to play an instrument.

As I live in the real world rather than a blogosphere I don't have anyone to tag.

2007-01Jan-05

Binding to the Valid event

As with so many things in Visual FoxPro, binding to the Valid event isn't as straight forward as one may wish to. When binding to a control in a grid, Visual FoxPro calls the delegate object only when the grid control contains user defined code in the Valid event at some point in the class hierarchy. This code doesn't have to do anything and can be a plain comment. Visual FoxPro needs the user-defined code as a binding place, so its mere existence is sufficient.

If you try to bind to a textbox's Valid event using the default object Text1 or a regular textbox object without any special code in the Valid event, your code is never called. This behavior is actually documented (well, hidden) in the Remarks section of BindEvent: "Certain events such as When and Valid require code in the event for it to occur."

The following sample demonstrates this behavior. No matter how you navigate in the grid or what values you change, the only Valid event you ever see is the one from column 2:

*==========================================================
* Binding to the Valid event of a grid control is only
* possible if you use a subclassed control with code in
* the valid event.
*==========================================================

  Public goForm
  Create Cursor Test (cField1 C(10), cField2 C(10))
  Insert into Test Values ("one","two")
  Insert into Test Values ("three","four")
  Go top
  goForm = CreateObject("Form")
  goForm.AddObject("grd","MyGrid")
  goForm.grd.Visible = .T.
  goForm.Left = 200
  goForm.Show()

Define Class MyGrid as Grid
  ColumnCount = 2
  Procedure Init
    This.Columns(2).RemoveObject("Text1")
    This.Columns(2).AddObject("Text1","MyTextbox")
    This.Columns(2).Text1.Visible = .T.
    BindEvent( ;
      This.Columns(1).Text1, "Valid", ;
      This, "ColumnValid" ;
    )
    BindEvent( ;
      This.Columns(2).Text1, "Valid", ;
      This, "ColumnValid" ;
    )
  EndProc
  Procedure ColumnValid
    Local laSource[1]
    AEvents(laSource,0)
    Activate Screen
    ? Sys(1272,laSource[1])
  EndProc
EndDefine

Define Class MyTextbox as TextBox
  Procedure Valid
    *
  EndProc
EndDefine

Previous KnowlBits

RSS

August 2008 (1)

July 2008 (2)

May 2008 (1)

April 2008 (2)

January 2008 (2)

December 2007 (2)

November 2007 (2)

October 2007 (1)

September 2007 (1)

August 2007 (5)

July 2007 (4)

May 2007 (6)

March 2007 (3)

February 2007 (7)

January 2007 (6)

November 2006 (1)

October 2006 (3)

September 2006 (10)

June 2006 (2)

May 2006 (6)

April 2006 (1)

Southwest Fox 2008, Mesa, AZ, October 16 - 19, 2008

Impressum Kontakt Contact