How add the own class Header in Grid |
> I receive a code of the form containing GRID with
several fields.
> I create a class and receive PRG-file, generated by Class Browser.
> On compiling receive syntactical error on line (GRID.COLUMN1.HEADER1.. as
HEADER )
> ... аnd no anybody to help me. One hope on you.
Let's try to understand, by decomposing this problem on the following items:
Try to change a code on the similar following:
*///////// any.prg for VFP 6.0+SP3 or later RELEASE oFrm PUBLIC oFrm oFrm = CreateObject('form1') IF VARTYPE(oFrm) # 'O' RELEASE oFrm RETURN .F. ENDIF oFrm.Show() DEFINE CLASS form1 AS form Top = 0 Left = 0 Height = 212 Width = 218 DoCreate = .T. Caption = "Form1" Name = "Form1" DataSession = 2 ADD OBJECT grdsetup AS grid WITH ; ColumnCount = 2, ; Height = 200, ; Left = 12, ; RecordSource = "setup", ; RecordSourceType = 1, ; Top = 0, ; Width = 192, ; Name = "grdSetup", ; Column1.ControlSource = "setup.key_name", ; Column1.Name = "Column1", ; Column2.ControlSource = "setup.value", ; Column2.Name = "Column2" PROCEDURE Load ThisForm.AddObject('oSE', 'SessionDE', 'grdwithcolDE') IF TYPE('ThisForm.oSE') # 'O' OR ISNULL(ThisForm.oSE) RETURN .F. ENDIF ENDPROC PROCEDURE Init() WITH ThisForm.grdsetup.Column1.header1 .Caption = "key_name" ENDWITH WITH ThisForm.grdsetup.Column2.header1 .Caption = "value" ENDWITH ENDPROC ENDDEFINE DEFINE CLASS grdwithcolDE AS DataEnvironment name = "Dataenvironment" ADD OBJECT Cursor1 AS Cursor WITH ; name = "Cursor1", ; alias = "setup", ; cursorsource = "setup", ; database = HOME(2)+"tastrade\data\tastrade.dbc" ENDDEFINE DEFINE CLASS SessionDE As Session DataSession = 2 Name = "SessionDE" oDE = NULL PROTECTED FUNCTION CreateDE(tcClassNameDE) IF VARTYPE(tcClassNameDE) # 'C' OR EMPTY(tcClassNameDE) RETURN .F. ENDIF WITH This IF .DataSession <> 1 SET DATASESSION TO .DataSessionId SET DELETED ON SET MULTILOCKS ON SET EXCLUSIVE OFF ENDIF .oDE = NewObject(tcClassNameDE) IF VARTYPE(.oDE) = 'O' .oDE.OpenTables() ELSE RETURN .F. ENDIF ENDWITH ENDFUNC PROCEDURE Init(tcClassNameDE) RETURN This.CreateDE(tcClassNameDE) ENDPROC PROCEDURE Destroy() This.oDE = NULL ENDPROC ENDDEFINE *///////// end of any.prg
i.e. simply transfer initialization of headers/columns to an event MyForm.Init()
Similar question frequently asked because there is a desire to reprogram its an event Click(). And it's true that this problem exist. As the class Header can not be added in vcx-library, at least officially :-) Let's consider this problem in two aspects:
However, from a code - there are no problems! ...And to be not unfounded I shall bring a short example that as it can be made.
*//////////////////////// any.prg for VFP 6.0+SP3 or later RELEASE oFrm PUBLIC oFrm oFrm = CreateObject('form1') IF VARTYPE(oFrm) # 'O' RELEASE oFrm RETURN .F. ENDIF oFrm.Show() DEFINE CLASS form1 AS form Top = 0 Left = 0 Height = 212 Width = 218 DoCreate = .T. Caption = "Form1" Name = "Form1" DataSession = 2 PROCEDURE Load * *-- SessionDE ThisForm.AddObject('oSE', 'SessionDE', 'GridDE') IF TYPE('ThisForm.oSE') # 'O' OR ISNULL(ThisForm.oSE) RETURN .F. ENDIF * *-- Grid ThisForm.AddObject('oGrid', 'mygrid') IF TYPE('ThisForm.oGrid') # 'O' OR ISNULL(ThisForm.oGrid) RETURN .F. ENDIF * *-- Columns LOCAL lnCol, lcCurObj, lcAlias, lcSetComp lcSetComp = SET('COMPATIBLE') IF INLIST(lcSetComp, 'ON', 'DB4') SET COMPATIBLE OFF ENDIF lcAlias = PROPER(ThisForm.oSE.oDE.Cursor1.alias) WITH ThisForm.oGrid .RecordSource = lcAlias FOR lnCol = 1 TO FCOUNT(lcAlias) lcCurObj = 'Column'+LTRIM(STR(lnCol)) .AddObject(lcCurObj, 'MyColumn') IF TYPE('ThisForm.oGrid.'+lcCurObj) # 'O' RETURN .F. ENDIF lcCaption = PROPER(FIELD(lnCol, lcAlias)) lcField = lcAlias+'.'+lcCaption WITH .Columns(lnCol) .Header1.Caption = lcCaption .ControlSource = lcField .Width = FSIZE(lcCaption, lcAlias)*7 .Text1.Visible = .T. .Visible = .T. ENDWITH ENDFOR .Visible = .T. ENDWITH IF !(lcSetComp == SET('COMPATIBLE')) SET COMPATIBLE &lcSetComp ENDIF ENDPROC PROCEDURE Init WITH This .Width = 2*SYSMETRIC(21)/3 .Height = 2*SYSMETRIC(22)/3 .Resize() ENDWITH ENDPROC PROCEDURE Resize WITH ThisForm.oGrid LOCAL lnWs, lnHs lnWs = .Left+.Left lnHs = .Top+.Top IF ThisForm.Width > lnWs .Width = ThisForm.Width - lnWs ENDIF IF ThisForm.Height > lnHs .Height = ThisForm.Height - lnHs ENDIF ENDWITH ENDPROC ENDDEFINE DEFINE CLASS MyHeader AS Header PROCEDURE Click() WITH This.Parent.Parent .ChangeOrder(.ActiveColumn) ENDWITH ENDPROC ENDDEFINE DEFINE CLASS MyColumn AS Column Width = 30 ADD OBJECT Header1 AS MyHeader ENDDEFINE DEFINE CLASS MyGrid AS grid ColumnCount = 0 DeleteMark = .F. Height = 200 RecordSourceType = 1 Width = 192 Name = "MyGrid" PROCEDURE ChangeOrder LPARAMETERS tnCol IF EMPTY(tnCol) RETURN .F. ENDIF LOCAL lcAlias, lcCdx, lnRecNo, lcSource; ,lcField, lnPos, lnTag, lbFound WITH This * *-- Check CDX-file lcAlias = .RecordSource lcCdx = CDX(1, lcAlias) IF EMPTY(lcCdx) WAIT WINDOW 'CDX file for '+lcAlias; +' not found.' NOWAIT RETURN .F. ENDIF IF EMPTY(TAG(lcCdx, 1, lcAlias)) RETURN .F. ENDIF * *-- Find index for column lcSource = .Columns(tnCol).ControlSource lnPos = ATC('.', lcSource) lcField = UPPER(SUBSTR(lcSource, lnPos+1)) IF LENC(lcField) > 10 lcField = LEFTC(lcField, 10) ENDIF FOR lnTag = 1 TO TAGCOUNT(lcCdx, lcAlias) IF TAG(lcCdx, lnTag, lcAlias) = lcField lbFound = .T. EXIT ENDIF ENDFOR IF !lbFound WAIT WINDOW 'Tag: '; +lcField+' not found.' NOWAIT RETURN .F. ENDIF * *-- Change order lnRecNo = IIF(!EOF(), RECNO(), 0) SET ORDER TO TAG (lcField) ; IN (lcAlias) .ActivateCell(1, tnCol) IF lnRecNo # 0 GO (lnRecNo) ELSE GO TOP ENDIF .Refresh() ENDWITH ENDPROC ENDDEFINE DEFINE CLASS GridDE AS DataEnvironment name = "Dataenvironment" ADD OBJECT Cursor1 AS Cursor WITH ; name = "Cursor1", ; alias = "customer", ; cursorsource = "customer", ; database = HOME(2); +"tastrade\data\tastrade.dbc" ENDDEFINE DEFINE CLASS SessionDE As Session DataSession = 2 Name = "SessionDE" oDE = NULL PROTECTED FUNCTION CreateDE(tcClassNameDE) IF VARTYPE(tcClassNameDE) # 'C' ; OR EMPTY(tcClassNameDE) RETURN .F. ENDIF WITH This IF .DataSession <> 1 SET DATASESSION TO .DataSessionId SET DELETED ON SET MULTILOCKS ON SET EXCLUSIVE OFF ENDIF .oDE = NewObject(tcClassNameDE) IF VARTYPE(.oDE) = 'O' .oDE.OpenTables() ELSE RETURN .F. ENDIF ENDWITH ENDFUNC PROCEDURE Init(tcClassNameDE) RETURN This.CreateDE(tcClassNameDE) ENDPROC PROCEDURE Destroy() This.oDE = NULL ENDPROC ENDDEFINE *//////////////////////// end of any.prg
As you can see all is so simple, that practically there is nothing to make comments. : -) Unless only it is necessary to tell, that I have taken advantage of the GenDE utility (see on page Examples) to receive source code of the class GridDE :-))...
As was told above, that "the class Header can not be added in vcx-library, at least officially". Let's discuss now this problem, i.e. we shall look, as it can be made that is to say informally
Allocation classes MyColumn, MyHeader, SessionDE in vcx-library is not typical from the point of view of Microsoft, so they have written VFP that the use only from a code (see definitions of these classes in HeaderInGrid_p.prg), however on my sight it's rather severe restriction, ... and I have located these classes in library that is to say by "handles", the apart from is clean of aesthetic reasons, there is also a certain problem under discussion below.
The main thing of a problem that a method AnyObj.AddObject() does not allow to create instances of classes from prg-files immediately (only from vcx-libraries together with SET CLASSLIB TO...), while only just the method AddObject() correctly works with Private Data Session of the form, as against CreateObject()/NewObject() [see theme "I have VFP 6.0+SP3 VS6 and now class Session has appeared. Not whether will show an example of its use?" in FAQ on http://vfpdmur.narod.ru/faq/de_e.html]
The possible way to overcome of the indicated difficulty consists in locating a class SessionDE (derivative from Session) in vcx-library. However here again, to make it immediately it is not possible, because there is an error: Cannot visually modify a class of this type (Error 1978) (it have place as well for classes: MyColumn, MyHeader :-(). Nevertheless, there is a small trick to realize it. Pitches below are explained how to make it, on an example Class SessionDE from library GridLib.vcx
If you accidentally have tried to edit a not edited class Session (Column, Header) i.e. have received Error 1978, it is better to close VFP environment and to open it again, as such attempt of editing results to exclusive-grab of the file of your library and any forces to be closed of such grab is not possible :-( The reliable indication of a possibility of editing of a class SessionDE (MyColumn, MyHeader) in Class Designer is the availability is the icon of a class Custom on a tab Classes in the project. And on the contrary, do not try to open on editing in Class Designer a class, which does not have icon (i.e. derivative from Session), it inevitably will reduce Error 1978.
I would be not up to an extremity honest man in relation to you, if has not uncovered one piquant detail :-) There is a certain complexity in creation of a class, derivative from Column. The problem consists on definition the container including the class Header. Thus, if we not contrive to include the class (MyHeader) in a class created as derivative from Column, i.e. exactly that makes a code:
DEFINE CLASS MyColumn AS Column ADD OBJECT Header1 AS MyHeader ENDDEFINE
that we shall be do, somewhere in an event MyColumn.Init() in runtime, to make the following:
WITH This IF TYPE('This.Header1') = 'O' .RemoveObject('Header1') ENDIF .AddObject('Header1', 'MyHeader') ENDWITH
Deleting a Header1 class, inserted by default, and adding instead of it the own class MyHeader :-( ...Just so the class MyColumn from GridLib.vcx is written. It certainly not a masterpiece, however are no any problems with editing it, but an elemination that are indicated in the warning above.
Now, when as I hope a problem be clear, it is possible to create and rigorous class - container, as derivative from Column. For this purpose it is possible to act as follows:
At last, that it was easily possible to change a used class with MyColumn on NewColumn and to inspect with what from classes I am used, has located in an event MyForm.Load() following code:
* *-- What class column we are used now? #DEFINE UDCHEADER .F. #IF UDCHEADER #DEFINE CLS_COLUMN 'NewColumn' &&- includes the user defined class Header #ELSE #DEFINE CLS_COLUMN 'MyColumn' &&- includes the default class Header #ENDIF WAIT WINDOW 'Used: '+CLS_COLUMN NOWAIT
In the conclusion I want to advise: before to begin something to do with library GridLib.vcx, make backup copy for it :-)
It's necessary from a code/vcx to create the own class MyPageFrame with Pages... and already it and to add. :-)
To check up said above you can on the following example containing files:
HeaderInGrid_p.prg | - "direct" code, i.e. all definitions of classes in the prg-file as well as executable example |
GridLib.vcx | - is created on a just technique above for classes from HeaderInGrid_p.prg |
HeaderInGrid_l.prg | - example using of classes from library GridLib.vcx |
Download Hdrgrd.zip (17,7KB) (see also note on top p. Examples, about slow loading :-()
Download grdhdrcl.zip (12,0KB) - On an example of the class, derivative from Header, the opportunity of change of the order of sorting on click on heading of a column in Grid is shown.