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!

Bindevents syntax help for Grid Header Click

Status
Not open for further replies.

dantheinfoman

Programmer
May 5, 2015
131
US
Hi All,

I don't need to sort dynamically. I'm trying to make baby steps in a different direction. I have a grid that imports xls files and I want it to (no matter how many columns there happen to be) allow me to click on a header and for it to say wait window 'You clicked on column 1' or 'You clicked on column 5'.

I've read about Bindevent, but i don't get it nor understand the syntax.

Here's the crappy code I've got that isn't working.

Code:
oHandler=NEWOBJECT("myhandler")
BINDEVENT(thisform.pf.p1.grid1.Columns,"Click",oHandler,"myHClickp")
DO "C:\hello\importer\importer\myhclickp.prg"

I figure whatever code I put in, it will have to be in the form's init or more likely in the grid1's init event. I also think that Bindevents is like using dynamicforecolor and all the other 'dynamic' things in that it takes a command from the INIT and rolls with it.

Thanks!

Dan
 
Well, bindevent binds events.

It says whenever the object in parameter 1 has it's method or event param2 called or raised, then in object param3 also do method param4.

You can't bind the Columns collection as event source, the collection is just a collection of column objects, all click events occur in each single column, nothing happens for the columns collection.

What you do right is creating a handler object, which BINDEVENT needs. I assume it has a myHClickp method. What it also needs to work is, that oHandler stays alive. If oHandler is a local or private variable, it'll go out of scope and is released and so the oHandler object is gone even before the first header click occurs, even if you bind the single columns.

One simple object to use as handler is the form itself, as its always there as long as the grid is there, or of course the grid itself, which is even better in regard to encapsulation. A separate handler is a good choice too, to compose different handlers with grids, also to extend native grids, which you can't extend with a method without hacking into your scx or vcx and changing the base class of the grid.

Anyway, you have to create a handler, where it stays. Eg make the handler a container class, which a) can be added to other classes as either property or subcontainer and b) can be extended by adding further objects inside it. You don't want and need this visible, but that'll be the default behaviour of AddObject anyway, it'll create your handler class container object with visible=.f., even if you don't set it .F. in your class definition.

Having a handler based on container you can use AddObject() to add it to objects being able to contain containers, eg the form, or you can use AddProperty() to add the container as property, a property can contain an object reference, even if the object itself would not be allowed as child object and as you don'T need the handler visible that's good enough, too.

Bye, Olaf.
 
I hope these short examples would be useful

1) Show a message when click the form (no bindings)
Code:
PUBLIC ofrm
ofrm = CREATEOBJECT("MyForm")
ofrm.show()

DEFINE CLASS MyForm as Form
	PROCEDURE click
		MESSAGEBOX('This is the native clic code')
	ENDPROC
ENDDEFINE

2) Show another additional message when click the form, before the code form the form.click is executed
Code:
PUBLIC ofrm,oHandler
oHandler = CREATEOBJECT("MyCustom")
ofrm = CREATEOBJECT("MyForm")
ofrm.show()

DEFINE CLASS MyForm as Form
	PROCEDURE Init
		BINDEVENT(ThisForm,"Click",m.oHandler,"MyClick")
	ENDPROC

	PROCEDURE click
		MESSAGEBOX('This is the native clic code')
	ENDPROC
	
	PROCEDURE destroy
		UNBINDEVENTS(This)
		RELEASE oHandler
	ENDPROC
ENDDEFINE

DEFINE CLASS MyCustom as Custom
	PROCEDURE MyClick
		LOCAL laObj[1]
		IF AEVENTS(laObj,0) > 0
			MESSAGEBOX('Before clicked ' + SYS(1272, laObj[1]))
		ENDIF
	ENDPROC
ENDDEF

3) You can bind more delegates to a single source.
Show two additional messages, one before and the second after the code form the form.click is executed
Code:
PUBLIC ofrm,oHandler
oHandler = CREATEOBJECT("MyCustom")
ofrm = CREATEOBJECT("MyForm")
ofrm.show()

DEFINE CLASS MyForm as Form
	PROCEDURE Init
		BINDEVENT(ThisForm,"Click",m.oHandler,"MyClickAfter",1)
		BINDEVENT(ThisForm,"Click",m.oHandler,"MyClick")
	ENDPROC

	PROCEDURE click
		MESSAGEBOX('This is the native clic code')
	ENDPROC
	
	PROCEDURE destroy
		UNBINDEVENTS(This)
		RELEASE oHandler
	ENDPROC
ENDDEFINE

DEFINE CLASS MyCustom as Custom
	PROCEDURE MyClick
		LOCAL laObj[1]
		IF AEVENTS(laObj,0) > 0
			MESSAGEBOX('Before clicked ' + SYS(1272, laObj[1]))
		ENDIF
	ENDPROC
	PROCEDURE MyClickAfter
		LOCAL laObj[1]
		IF AEVENTS(laObj,0) > 0
			MESSAGEBOX('Code executed after clicked ' + SYS(1272, laObj[1]))
		ENDIF
	ENDPROC
ENDDEF


Respectfully,
Vilhelm-Ion Praisach
Resita, Romania
 
Here is and example for column headers. The event handler is the form itself, while the sources are each column header
Code:
PUBLIC ofrm
ofrm = CREATEOBJECT("MyForm")
ofrm.show()

DEFINE CLASS MyForm as Form
	ADD OBJECT grd as grid
	PROCEDURE load
		CREATE CURSOR cc (nI I,cC C(20))
		INSERT INTO cc VALUES (3,'Spock')
		INSERT INTO cc VALUES (4,'Uhuru')
		INSERT INTO cc VALUES (6,'Uhura')
		INSERT INTO cc VALUES (1,'Kirk')
		INSERT INTO cc VALUES (2,'Picard')
		INSERT INTO cc VALUES (5,'Data')
		GO TOP
	ENDPROC
	PROCEDURE Init
		This.grd.RecordSource = 'cc'
		This.DoBinds()
	ENDPROC
	
	PROCEDURE DoBinds
		LOCAL loCol,loObj
		FOR EACH loCol IN This.grd.Columns
			FOR EACH loObj IN m.loCol.Objects
				IF m.loObj.Baseclass == 'Header'
					UNBINDEVENTS(loObj)
					BINDEVENT(loObj,"Click",This,"MyClick")
					EXIT
				ENDIF
			NEXT
		NEXT
	ENDPROC
	
	PROCEDURE MyClick
		LOCAL laObj[1],loH,lcOrder
		IF AEVENTS(laObj,0) > 0
			loH = laObj[1]
			IF m.loH.Baseclass == 'Header'
				MESSAGEBOX('Clicked ' + SYS(1272, loH))
			ENDIF
		ENDIF
	ENDPROC
ENDDEFINE

Respectfully,
Vilhelm-Ion Praisach
Resita, Romania
 
I intentionally didn't posted code, as Dan should be able to do this himself. You shouod comprehend, otherwise you're still not able to maintain and etend this. Anyway, code samples are not wrong to illustrate t´some things.

Let me put focus on some important lines in Vilhelm-Ion's code:

First I said you can't bind to the columns collection, you have to bind each single column. More precise each single header of each single column.

That's done with this loop:
Code:
FOR EACH loCol IN This.grd.Columns

The inner code looks for Header objects and the unbind is unnecessary, as long as you init this all just once. In the normal case you can also assume the header object is called Header1 and a direct child of the column object, so you could bind:
Code:
BINDEVENTS(loCol.Header1,"Click",oHandler,"handleClick")

This is making an assumption which could become wrong, if you'd use grid column member classes, but you can stay with te name header1 in your own column classes, too. It's extra cautious to iterate the Objects to find headers. I don't see a way the header can be somewhere else but in Objects[1], so you could reference this without just feeling lucky
Code:
BINDEVENTS(loCol.Objects[1],"Click",oHandler,"handleClick")

This binds all header click events to a single handler and you get one specific problem, you don't know which header was clicked in the MyClick or handleClick event, here AEVENTS comes to the rescue:
Code:
IF AEVENTS(laObj,0) > 0
This creates or fills the laObj array. The parameter 0 tells it to fill in the current event source object. Checking for >0 means checking, whether the event handling code really was triggered by the event and not called, so you don't react to some direct call of oHandler.handleClick() only react to the calls coming from the event binding. You can also see how you got triggered in laObj[3] and could react different. 0=System means it was a real event, eg the user clicked. 1=RAISEEVENT() means some code used RAISEVENT() to raise the header.click event and 2=Method Call even means some code simply called Header.Click(). In the normal case you want to handle each of these equal, you can't punish developers directly calling header1.click() just because it's bad practice.

The object reference in laObj[1] is enabling you to know which header was clicked, you can look at the caption, or look up at the column object via laObj{1].Parent and see ColumnOrder, the Name of the column and even the Column.Controlsource to make further decisions on what the click should do. You may add own properties to the header or column for the handler to evaluate eg which index should be used for sorting.

Well, overall you see there is a lot involved and that's why I suggested an easier way of offering a few sort options via a combobox to titoneon earlier.
This is still not the whole nine yards.

Bye, Olaf.







 
Thank you all! I've gotten a lot further on this project than I imagined I could. I will post back later with samples of functional codes.

Thanks

Dan
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top