Page 1 of 1
Subclassing in XB++
Posted: Mon Oct 10, 2016 4:58 pm
by LiddycoatA
TL:DR; When I create a subclass, and call the :new() clause. my new class's :init() method appears to interrupt the superclass setting default values to it's members. This breaks further execution of function calls on my new class. How can i continue the default behavior after :init() is called to set these defaults or do I have instantiate them in my subclass definition?
--------------------------------
Detailed explanation:
My company has decided after this years Devcon in Arizona to take the plunge and start converting apps from Foxpro to XB++. We have decided for the moment to forgo using Express while we learn the base language. I loved what I saw at the conference this year in regards to Express and we'll likely start using it once were more comfortable with the root concepts of XB++.
I'm working on a simple program of ours, it only uses one table and is a internal only management system. A great starter project.
XbpBrowse lacks a few things that I want to implement so, I figured to subclass it and create my own class. I'll call it MyBrow
MyBrow has a default method Init(). According to the Xbase documentation Init() executes immediately after :new() This is perfect because all I want to do is start by adding some variable properties to the class that I can easily access and modify at runtime.
Code: Select all
CLASS METHOD MyBrow:Init(oParent, oOwner, aPos, aSize, aPresParam, lVisible)
//init
::aSize := aSize
::aPos := aPos
::sType := "Browser"
RETURN self
So far everything was running swimmingly, until back in my main prg I hit:
: oError:description : Parameter has a wrong data type
I double and triple checked through the debugger and i was successfully passing a XbpColumn object, I tried another method:
Code: Select all
oBrowse:addColumn( FieldBlock(cField),35,cField)
Again I found the same error. It was at this point that I noticed a lot of NIL values in MyBrow's members. I compared the members to a regular XbpBrowse object at runtime and noticed a large number of default values in MyBrow were missing. I was missing values like ColCount, ColPos, ControlState, CursorMode and many many more. My conclusion is that :init() executes between :new() and when these values are set to their defaults.
So my question becomes, how can finish creating the new class, with the default behaviors?
Thanks for any help I can garner.
Re: Subclassing in XB++
Posted: Mon Oct 10, 2016 8:23 pm
by Auge_Ohr
hi,
my Answer is using "pure" Xbase++.
LiddycoatA wrote:
Code: Select all
CLASS METHOD MyBrow:Init(oParent, oOwner, aPos, aSize, aPresParam, lVisible)
//init
::aSize := aSize
::aPos := aPos
::sType := "Browser"
RETURN self
your Class must inherit from XbpBrowse
o:aSize is PROTECT
o:aPos and o:sType does not exist so you have to declare it.
Code: Select all
CLASS MyBrow FROM XbpBrowse
EXPORTED:
// your new iVAR
VAR aPos
VAR sType
INLINE METHOD init( oParent, oOwner, aPos, aSize, aPP, lVisible )
::XbpBrowse:init( oParent, oOwner, aPos, aSize, aPP, lVisible )
* ::aSize := aSize // PROTECT, use other iVAR Name
::aPos := aPos // try other (better) Name
::sType := "Browser"
RETURN self
INLINE METHOD create( oParent, oOwner, aPos, aSize, aPP, lVisible )
::XbpBrowse:create( oParent, oOwner, aPos, aSize, aPP, lVisible )
RETURN self
ENDCLASS
LiddycoatA wrote:So far everything was running swimmingly, until back in my main prg I hit:
: oError:description : Parameter has a wrong data type
a XbpBrowse() have XbpColumn() ... one or many
Method of XbpBrowse() are to "setup" ( Add/Del ... ) XbpColumn() or to navigate in Column.
Question : what do you want to enhance with your new Class ?
most want a new Column Class.
look into
v1.9x -> x:\ALASKA\XPPW32\SOURCE\samples\solution\xbpget\
v2.0x -> x:\USER\xxx\Documents\Xbase++\Source\samples\solution\xbpget\
Re: Subclassing in XB++
Posted: Tue Oct 11, 2016 12:37 am
by skiman
Hi,
I can only give the advice to start with eXPress. This will make it a lot easier.
Re: Subclassing in XB++
Posted: Tue Oct 11, 2016 6:05 am
by rdonnay
Can you give us the full source for your class (and your test program) ?
Re: Subclassing in XB++
Posted: Tue Oct 11, 2016 8:28 am
by LiddycoatA
The full code for my class is very simple.
Code: Select all
CLASS MyBrow FROM XbpBrowse
//declare vairables
PROTECTED: //Visible only in class methods
EXPORTED: //globally visable
CLASS VAR aSize, aPos, sType
CLASS METHOD Init // class methods declaration
ENDCLASS
CLASS METHOD MyBrow:Init(oParent, oOwner, aPos, aSize, aPresParam, lVisible)
//init
::aSize := aSize
::aPos := aPos
::sType := "Browser"
RETURN self
I don't want to post my full code because there's a lot of stuff that doesn't matter. the relevant code is:
Code: Select all
PROCEDURE MAIN
LOCAL nEvent, mp1, mp2, oXbp
LOCAL oBtn_First, oBtn_Prev, oBtn_Next, oBtn_Last
LOCAL oBrowser
PRIVATE oApplication, oScreen //private makes these viewable from any child function. Locals are released at end of declairing function.
// Create Main Application Window
//Object Screen new dialog parent obj, owner, position, size
oApplication := XbpDialog():new( )
oApplication:title := "Mytitle"
oApplication:taskList := .T.
oApplication:usevisualstyle :=.T.
oApplication:minsize := {1150,500}
//do not call create until after settings above are set. create is the final call to build the screen.
oApplication:create( ,,, {1280,720} )
CenterControl( oApplication )
//Add screen objects
//all ojects are added to the application's drawing area.
oScreen := oApplication:DrawingArea
//Add Browser
oBrowser = ScreenBrowser()
// I believe this code is equevilant to READ EVENTS in foxpro
nEvent := 0
DO WHILE nEvent <> xbeP_Close
nEvent := AppEvent( @mp1, @mp2, @oXbp )
oXbp:handleEvent( nEvent, mp1, mp2 )
ENDDO
RETURN
FUNCTION ScreenBrowser()
//Add Table Browse
//NOTE: XB++ calculates position based on a 0,0 bottom left position. By default items are anchored to the bottom of the screen.
// Positions are for the bottom left corner of objects.
LOCAL nBrowseHeight := 250
LOCAL nBrowseWidth := 1100
LOCAL nMarginBuffer := 20
LOCAL aColNames, cnt :=1
LOCAL oBrowse, cField, cField1, i, aColList, aBrowsePos
aAppWindowSize := oApplication:ClientSize
nBrowseLeftPos := aAppWindowSize[1]/2 - nBrowseWidth/2
nBrowseBotPos := aAppWindowSize[2] - nBrowseHeight - nMarginBuffer
aBrowsePos := {nBrowseLeftPos,nBrowseBotPos}
//oBrowse := XbpBrowse():new(oScreen,,aBrowsePos,{nBrowseWidth,nBrowseHeight}) //this works
oBrowse := MyBrow():new(oScreen,,aBrowsePos,{nBrowseWidth,nBrowseHeight}) //this doesn't
oBrowse:layoutalign := XBPLAYOUT_TOP
// Navigation code blocks for the browser
oBrowse:skipBlock := {|n| DbSkipper(n) }
oBrowse:goTopBlock := {| | DbGoTop() }
oBrowse:goBottomBlock := {| | DbGoBottom() }
oBrowse:phyPosBlock := {| | Recno() }
oBrowse:goPhyPosBlock := {|n| DbGoto(n) }
// Navigation code blocks for the vertical scroll bar
oBrowse:posBlock := {| | DbPosition() }
oBrowse:goPosBlock := {|n| DbGoPosition(n) }
oBrowse:lastPosBlock := {| | 100 }
oBrowse:firstPosBlock := {| | 0 }
//This loads ALL fields into browser
FOR i:=1 TO FCount()
cField := FieldName( i )
oBrowse:addColumn( FieldBlock(cField), , cField ) //error happens here when oBrowse is a MyBrow class object.
NEXT
oBrowse:hScroll := .F.
oBrowse:create()
oBrowse:goBottom()
oBrowse:RefreshAll()
//Add index select
IndexBox(aColNames)
ENDIF
RETURN oBrowse
I appreciate your attempt Auge_Ohr, but your code throws further errors. INLINE METHOD has a reserved keyword, and adding the ::XbpBrowse:init( oParent, oOwner, aPos, aSize, aPresParam, lVisible ) line results in an error"Access to instance-variable not allowed within class-object"
For the question about, what I want the class to do, I want it to do exactly what a XbpBrowse does, with a couple additional members I can easily access that declares the objects size and position in the window. But it's much less about what I want it to DO and more about HOW do I manipulate classes in XB++. This is a learning project more than anything before we start to look at taking programs with a hundred PRG files and forms from foxpro to XB++.
Re: Subclassing in XB++
Posted: Tue Oct 11, 2016 9:26 am
by Auge_Ohr
LiddycoatA wrote:The full code for my class is very simple.
Code: Select all
//oBrowse := XbpBrowse():new(oScreen,,aBrowsePos,{nBrowseWidth,nBrowseHeight}) //this works
oBrowse := MyBrow():new(oScreen,,aBrowsePos,{nBrowseWidth,nBrowseHeight}) //this doesn't
oBrowse:layoutalign := XBPLAYOUT_TOP
you need to CREATE() before assign Codeblock for navigation etc.
I appreciate your attempt Auge_Ohr, but your code throws further errors. INLINE METHOD has a reserved keyword, and adding the ::XbpBrowse:init( oParent, oOwner, aPos, aSize, aPresParam, lVisible ) line results in an error"Access to instance-variable not allowed within class-object"
as i say "aSize" is PROTECT -> no Access from "outside"
Re: Subclassing in XB++
Posted: Tue Oct 11, 2016 9:54 am
by rdonnay
You need to make sure that your INIT method calls the INIT method of the SUPER class.
Also, the Init method cannot be a CLASS METHOD in this case. If it is, you will get a runtime error when creating the browse.
Code: Select all
CLASS MyBrow FROM XbpBrowse
//declare vairables
PROTECTED: //Visible only in class methods
EXPORTED: //globally visable
CLASS VAR aSize, aPos, sType
METHOD Init // class methods declaration
ENDCLASS
* --------
METHOD MyBrow:Init(oParent, oOwner, aPos, aSize, aPresParam, lVisible)
//init
::aSize := aSize
::aPos := aPos
::sType := "Browser"
RETURN SUPER:init(oParent,oOwner,aPos,aSize,aPresParam,lVisible)
What kind of new features would you like to add to the XbpBrowse class?
Perhaps I can help you with this.
Re: Subclassing in XB++
Posted: Tue Oct 11, 2016 10:12 am
by LiddycoatA
Fantasitc Roger, that did it. Thanks a ton!
If you don't mind, can you clarify or point me towards an article that explains the difference between calling a CLASS Method Myclass:MyMethod and calling it without the CLASS declaration?
Re: Subclassing in XB++
Posted: Tue Oct 11, 2016 5:06 pm
by rdonnay
Syntax
CLASS METHOD <MethodName,...>
or
CLASS METHOD <MessageName> [IS <MethodName>] [IN <SuperClass>]
Parameters
<MethodName,...>
<MethodName,...> is a comma separated list containing names of class methods of the class object being declared. The name for a method follows the same convention as function and variable names. It must begin with an underscore or a letter and must contain alpha numeric characters. The first 255 characters are significant.
<MessageName>
<MessageName> designates the message which must be sent to a class object so that it executes the class method with the name <MethodName> . <MessageName> is used when several superclasses are available in the class hierarchy which have methods with the same name.
<MethodName>
When the option IS is used, a list of method names cannot be used. In this case only a single method name is permitted in the CLASS METHOD declaration. The method with the name <MethodName> is executed by the class object when it receives the message <MessageName> .
<SuperClass>
When multiple superclasses have the same class method <MethodName> it can be specified explicitly from which superclass a class method is to be called when an object receives the message <MessageName> . Only class methods of a superclass which are visible (which are not declared with the attribute HIDDEN: within the superclass declaration) can be referenced.
Description
CLASS METHOD declares a method as a class method within the class declaration (CLASS...ENDCLASS). Class methods are executed only from the class object. When an instance object receives the message for a class method, it forwards the message on to the class object.
The source code for a class method is generally implemented after the statement ENDCLASS. The code for the class method is prefaced by the declaration CLASS METHOD <MethodName> . If the class method is already implemented in a superclass or is assigned to a specific superclass by the option IN, the class method does not need to be implemented within this class. Instead, an object of this class executes the corresponding method in the superclass. When the class method <MethodName> is implemented in a superclass, it is automatically available for use in a subclass. When a class method of the same name is also declared in the subclass, the object executes the class method within the subclass and not the class method of the same name which exists in a superclass.
Within the source code for the class method the class object is always available via the variable self . This occurs whether the message to call the class method was sent to an instance object or directly to the class object.
When using CLASS METHOD outside the class declaration to preface the method implementation, the name of the class method <MethodName> is always used. When the method is declared as <MessageName> IS <MethodName> , <MessageName> determines the message which must be sent to an object, for the method <MethodName> to be executed.
<MessageName> is a mechanism for providing an alternative name for a class method. It can be used to declare a different name in this class than that used in the superclass. This is especially useful when two or more superclasses contain class methods with the same name. When this occurs, declaring alternative message names (unique within this class) for calling the class method in each of the superclasses is recommended. This clearly indicates which superclass class method will be used. To accomplish this, the declaration is used in the form CLASS METHOD..IS..IN.
The visibility of a class method is determined by the visibility attribute, which is set with one of the keywords EXPORTED:, PROTECTED:, or HIDDEN: (see the sections covering each attribute for additional information).
In general, class methods are only needed for accessing class variables. Class variables are member variables of the class object, and exist only once within a class. The value or contents of a class variable is the same for all instances of a class.
Reserved names: The object model of Xbase++ uses the following identifiers for class methods which are reserved and cannot be used in naming class methods. These are:
Reserved identifiers for class methods
Method Description
:new() Creates instance
:className() Returns name of class
:classObject() Returns class object
:isDerivedFrom() Determines whether or not an object is derived
from a particular class
:isDerivedFrom(<cClassName> | <oClassObject>) --> .T.|.F.
Example
// Declaring a CLASS METHOD
//
// Class declaration with class variables and methods
//
CLASS Cursor
EXPORTED: // globally visible
CLASS VAR nMaxRow, nMaxCol
CLASS METHOD initClass // class methods
CLASS METHOD SetMode, Hide // declaration
VAR nRow, nCol, nShape
METHOD init, Show, UpdatePos
ENDCLASS
CLASS METHOD Cursor:initClass() // class method
::nMaxRow := MaxRow() // implementations
::nMaxCol := MaxCol()
RETURN self
CLASS METHOD Cursor:SetMode( nMaxRow, nMaxCol )
IF SETMODE( nMaxRow, nMaxCol )
::initClass()
ENDIF
RETURN self
CLASS METHOD Cursor:Hide
SetCursor( 0 )
RETURN self