×
INTELLIGENT WORK FORUMS
FOR COMPUTER PROFESSIONALS

Log In

Come Join Us!

Are you a
Computer / IT professional?
Join Tek-Tips Forums!
  • Talk With Other Members
  • Be Notified Of Responses
    To Your Posts
  • Keyword Search
  • One-Click Access To Your
    Favorite Forums
  • Automated Signatures
    On Your Posts
  • Best Of All, It's Free!
  • Students Click Here

*Tek-Tips's functionality depends on members receiving e-mail. By joining you are opting in to receive e-mail.

Posting Guidelines

Promoting, selling, recruiting, coursework and thesis posting is forbidden.

Students Click Here

Jobs

Queryunload

Queryunload

Queryunload

(OP)
Good morning,
I have an object reference which I need to release, and would like to have this code fire perhaps in queryunload

However this event does not fire when "Thisform.release" is used in a command button to exit form.

I want to avoid having to go through the application and adding code to every form command button where it is released.
Unload method is too late as the form has already hung trying to release objects.

Is there a way to force the Queryunload event to fire in my form class?

RE: Queryunload

What about putting it in the Destroy event? That fires after QueryUnload but before Unload.

The important point is that, in Destroy, all the form's contained objects, properties, methods, etc. are intact. That's not the case in Unload.

You're right that QueryUnload doesn't fire in response to the form's Release method (nor if you release the form object).

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads

RE: Queryunload

No, in this case, I even guess BINDEVENT isn't possible, as it's having some exceptions and preconditions for some methods and events.

It's a good exercise to have base classes so you can have a central code that acts in all forms.

And the native events have some downsides, as you noticed already. Release() is meant to be very enforced, but indeed most apps will check for unsaved changes before allowing to close, there also is the form.Closeable property and in QueryUnload you want to apply different strategies depending on what is the source of the query for unloading the form.RelaseType

There is a good way to wire all this, and that needs the OOP way of inheriting this from a base class, not a hack with bindevents, I would recommend going the hard route even if bindevent would work.

Both the QueryUnload event and the Release method should call a user-defined method to check whether you want to allow to close the form, in QueryUnload you then would differ from Release if the ReleaseType is 2, which means a shutdown. Then you might disregard unsaved changes and at best would TableREVERT() them, while the Release() method in such a case might focus the save button and ask to save or cancel changes via a messgebox().

Both QueryUnload and RElease can be canceled with NODEFAULT. If closing is allowed, both should set the form.closable=.T.
You will have some common code in Release() and QueryUnload(), but that's OK, you need different overall strategies in the even QueryUnload and the method Release(). Programming code in the Release() will cover all places calling that method for triggering the native base behavior of releasing the form, your new code in that method can stop any call in the one central place.

These are two exits, the common code they call is for determining whether the situation allows closing, you might add another user-defined method to execute at the stage in both QueryUnload and Release, when you're deciding to close the form even forcefully and let that already do part of the cleanup that doesn't work naturally, but there is something wrong, if some references deadlock each other.

Bye, Olaf.

Olaf Doschke Software Engineering
https://www.doschke.name

RE: Queryunload

(OP)
Hi Mike,
I will try putting my code in the form destroy event. However I did try putting the code in the destroy event of the object to be released - but that did not seem to fire, hence did not try it in the form method.
I will try this later when I get back to my dev computer.

Just to clarify, I have a collection in a form that contains object references to a specific class on any of the other open forms. When I call "thisform.release" from an open form, the collection still holds the object reference to that form. I have a piece of code to remove the item in the collection which I would like to run as the form releases.
The forms are classed.
Just checking my approach is in the right direction?

Alastair

RE: Queryunload

(OP)
Hi Mike,

Destroy event in the form class worked perfectly thanks!

Alastair

RE: Queryunload

AlastairP,
I have extended my baseform with following properties: (adapted from and inspired by Andy Kramek)
1) OK2Destroy

CODE --> vfp

Lparameters tcTable

Local lcTable As String, ;
	llRet As Boolean, ;
	lnAnswer As Number, ;
	lnBuffMode As Number, ;
	lnParam As Number, ;
	lnTable As Number, ;
	lnTablesUsed As Number
Local Array aTablesUsed[1]

If Empty(m.tcTable)
	lcTable = Alias()
Else
	lcTable = m.tcTable
Endif



With Thisform

	llRet =  .Check4Changes(m.lcTable)

	If m.llRet = .F.  &&found uncommitted changes
		lcMessageText = "Save changements"
		lcMessageTitle = "Take care"
		lnDialogType = getmessagebox(4+16)
		lnAnswer = Messagebox(lcMessageText  , m.lnDialogType,lcMessageTitle ,  0 )  &&  Yes = 6, No = 7, Cancel = 2
		Do Case
			Case m.lnAnswer = 6
				lnBuffMode = CursorGetProp( 'Buffering', m.lcTable )
				If  m.lnBuffMode <> 5
					=CursorSetProp("Buffering",5,m.lcTable)
				Endif
				.AllUpdated = Tableupdate(.T.)
				If Type(' .AllUpdated')='N'
					.AllUpdated = Iif( .AllUpdated=0,.F.,.T.)
				Endif
			Case m.lnAnswer = 7
				*!* Created by Koen Piller
				*!* 26-9-2015 11:04:42
				*!* Purpose : buffferingcheck - to avoid "function requires Buffering - in development
				lnBuffMode = CursorGetProp( 'Buffering', m.lcTable )
				*** If  buffering, just  .T.
				If  m.lnBuffMode = 5
					llRet = .T.
				Else
					=CursorSetProp("Buffering",5,m.lcTable)
				Endif
				*!* end bufferingcheck
				.AllUpdated = Tablerevert(.T.)
				If Type(' .AllUpdated')='N'
					.AllUpdated = Iif( .AllUpdated=0,.F.,.T.)
				Endif
			Case m.lnAnswer = 2
				llRet = .F.
		Endcase

		llRet = .AllUpdated
	Else  &&&&found uncommitted changes
		lnTablesUsed = Aused(aTablesUsed,1)
		If Thisform.Killcursor = .T.
			For lnTable = 1 To m.lnTablesUsed
				lcTable = m.aTablesUsed(m.lnTable,1)
				If iscursor(m.lcTable)
					** Then close cursor
					Try
						Use In (m.lcTable)
					Endtry
				Endif
			Endfor
		Endif
	Endif
Endwith

Return m.llRet 

and
2)Check4Changes

CODE --> vfp

Lparameters tcTable

Local Array aTablesUsed[1]

Local lcFldState As String, ;
	lcTable As String, ;
	llRetVal As Boolean, ;
	lnBuffMode As Number, ;
	lnRec As Number, ;
	lnTable As Number, ;
	lnTablesUsed As Number
 
lnRec = 0

lcTable = Iif(Vartype(m.tcTable) # 'C' Or Empty(m.tcTable), Alias(), Alltrim(Upper(m.tcTable)))
If Empty( m.lcTable) Or ! Used(Juststem( m.lcTable))
	lcTable = Thisform.Masteralias
Endif
lnBuffMode = CursorGetProp('Buffering', m.lcTable)

If lnBuffMode = 1
	Return .F.
Endif

lnTablesUsed = Aused(m.aTablesUsed)
For m.lnTable = 1 To m.lnTablesUsed

	If CursorGetProp('sourcetype',aTablesUsed[m.lnTable,1]) = 3	Or CursorGetProp('sourcetype',aTablesUsed[m.lnTable,1])>100	&&skip for views
		CursorSetProp('Buffering',m.lnBuffMode,aTablesUsed[m.lnTable,1])	&&optimistic table buffering
	Endif

Endfor

If Inlist( m.lnBuffMode, 2,3)
	lcFldState = Nvl( Getfldstate( -1, m.lcTable), '')
	llRetVal = !Empty( Chrtran( m.lcFldState , '1', ''))
Else
	lnRec = Getnextmodified(0,m.lcTable)
	llRetVal = !(Getnextmodified(0,m.lcTable) # 0)
Endif

Return m.llRetVal 

I have than in my Release method:

CODE --> vfp

Local llRet As Boolean
llRet = .F.
If Not Thisform.OK2Destroy(m.llRet)
	Nodefault
Endif 
and in QueryUnload:

CODE --> vfp

Thisform.Check4changes()
If Not Thisform.OK2Destroy()
	Nodefault
Endif 

It works fine (for me) - if anyone has some observations/improvements please donot hesitate

Regards,

Koen

RE: Queryunload

Hi Koen,

I've got something roughly similar in my base DataForm class. A DataForm is any form that allows the user to view / edit / add / delete data from one or more tables. The QueryUnload calls a method called OKToClose, which works similarly to yours.

One minor improvement I have is in the wording of the user message. I have a form property called cFriendlyName, which always contains the name of the entity being edited, as known to the user. For example, if the form edits data from the Customer table, the cFriendlyName woudl simply be "customer" (in lower case, in the singular). My user message would then be:

CODE -->

lnReply = MESSAGEBOX("Do you want to save your changes to this " + thisform.cFriendlyName + "?", ;
	3+48, gcShortTitle) 

As I said, it's a very minor point, and does not affect the overall logic. But I do believe in paying close attention to the wording of all user-visible messages, and anything you can do to make the messages even a tiny bit more informative is always worthwhile.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads

RE: Queryunload

Quote (AlastairP)

When I call "thisform.release" from an open form, the collection still holds the object reference to that form.
That comes unexpected, as collection have the fine feature of automatically removing items referencing objects, the moment they are released:

Quote (VFP help on the form.release method)

When you have an object reference to a form in a collection, and you call the form's Release method, the object is removed from the collection without having to release or remove the form from the collection first.

And also vice versa, releasing the collection releases the objects referenced in it:

Quote (VFP help on the collection class)

When Visual FoxPro releases a collection, which contains object references, it also releases any objects in the collection if they are not referenced elsewhere. Make sure to release objects and any references to those objects from a collection before releasing the collection itself from memory.

And it is the way the help describes this, undoubted and used in this away by me many times.

So what do you actually store in the collection? The form reference or just its name or some other related value? You might make it harder on yourself than it actually is. This also is no subject of any bug, so this works since the collection object was added to the VFP base classes in (I think) VFP 8. What's your code to generate the collection? Did you subclass it and changed methods/events? Do you really use a collection object or an array? And last not least, do you store references elsewhere, too? That might hinder the release, if you overcomplicate things. An object only releases once all references to it (outside collections) are removed. References in collections are good to keep forms from automatically releasing.

This all works on the basis of reference counts and what you might be doing wrong is first creating a form variable reference in a private or public variable you don't release after storing the reference to the collection. In the simplest manner each form can add itself to a general forms collection in its load via goFormCollection.Add(THISFORM,THISFORM.Class)

You might use another key, eg TRANSFORM(THISFORM.HWND) will be something unique for each form, but won't let you easiyl find a form of some class, you will need a unique key, if you want to allow multiple forms of the same class, especially many baseclass forms. You might delegate this task to add the form to the collection to a form handler creating the form instead of letting each form register itself, there are always tons of ways. Another thing that might interfere with teh form release is starting a form LINKED to a varname, which is the other variant of varname=createobject(Formclass). Anyway, you will have problems when you mix strategies and partly use DO FORM, partly CREATEOBJECT(), partly even DEFINE WINDOW. Consolidate on one method of handling forms and then all this will work out nicer, most likely.

Bye, Olaf.

Olaf Doschke Software Engineering
https://www.doschke.name

RE: Queryunload

Mike,
Good point, will adjust.
Regards,
Koen

RE: Queryunload

(OP)
Hi Olaf,
Thanks for the tips.
I have used collections before successfully and never had trouble releasing object references.
I will look into your suggestions and see what I can find.

Alastair

Red Flag This Post

Please let us know here why this post is inappropriate. Reasons such as off-topic, duplicates, flames, illegal, vulgar, or students posting their homework.

Red Flag Submitted

Thank you for helping keep Tek-Tips Forums free from inappropriate posts.
The Tek-Tips staff will check this out and take appropriate action.

Reply To This Thread

Posting in the Tek-Tips forums is a member-only feature.

Click Here to join Tek-Tips and talk with other members!

Close Box

Join Tek-Tips® Today!

Join your peers on the Internet's largest technical computer professional community.
It's easy to join and it's free.

Here's Why Members Love Tek-Tips Forums:

Register now while it's still free!

Already a member? Close this window and log in.

Join Us             Close