Welcome to the SRP Forum! Please refer to the SRP Forum FAQ post if you have any questions regarding how the forum works.
Panel Close on Form Close
I have a form (with a ribbon, but not sure that's relevant), and one of the views on the form causes a panel form to show.
All works well so far.
If I close the main form, the panel seems to close immediately.
My issue that I have some code in the Close event, to prompt for save changes.
At this point however the form is no longer there, so I cant get the data, and if they choose cancel I cant leave things as they were.
Maybe there is something SRP can do, or maybe its the way I coded it.
Is this an issue, or is there some best practice coding technique that I can use to avoid this.
Colin
All works well so far.
If I close the main form, the panel seems to close immediately.
My issue that I have some code in the Close event, to prompt for save changes.
At this point however the form is no longer there, so I cant get the data, and if they choose cancel I cant leave things as they were.
Maybe there is something SRP can do, or maybe its the way I coded it.
Is this an issue, or is there some best practice coding technique that I can use to avoid this.
Colin
Comments
Just so I am clear on your configuration, you used the SetForm method to add an OI form onto an SRP Panel control? That seems clear enough but I wanted to confirm.
Unless there is some underlying connection I am unaware of between the Panel's form and your primary form such that a CLOSE event gets issued down to the secondary form, I would be highly inclined to believe that this is all based on how you coded it.
Can you offer some additional information on how the CLOSE event is handled? What is triggering the CLOSE event in the first place? Is this the user clicking on the X button in the caption bar or through some form-based control (button, menu, etc.)?
Where does your code that prompts for save changes live? Is it in a Script event handler or some promoted event that fires before the System Event Handler? Besides prompting for save changes, what else does it do?
The code in my CLOSE event is below.
This is in the Close event script itself.
Close is being don't by the red X in the form top right, ie Windows is closing, as opposed to by some other button on the form.
The code that prompts for the save is in the Omnievent call below.
If I put a debug as the first line of code, I can see that the panel is clear, before the rest of the event code happens.
This is also confirmed by viewing in debugger, you can see all the forms/controls in place, and the panel form is not present.
Colin
EES System Setup - Close Events CJR - June 2014 $Insert ICEMATE_COMMON $Insert SYSTEM_SETUP_COMMON ReturnState=1 CALL SEND_EVENT(@WINDOW,"OMNIEVENT","CLOSE",ReturnState) IF COUNT(IndexBuild,"ELEMENTS") THEN Call Set_Property(OLE_SHORTCUTS,"OLE.GroupEnabled[All]",1) Call Set_Property(OLE_SHORTCUTS,"ENABLED",1) Call Set_Property(OLE_INDEXTABLE,"ENABLED",1) CALL ELEMENTS_SUBROUTINES("BUILD_INDEX","","","") CALL ELEMENTS_SUBROUTINES("BUILD_TREE","","","") END IF COUNT(IndexBuild,"PROJECT_INDEX") THEN CALL EES_SUBROUTINES("PROJECT_INDEX","A") FOR I=1 TO DCOUNT(SECURITY_GROUPS<1>,@VM) GROUP=SECURITY_GROUPS<1,I> CALL EES_SUBROUTINES("PROJECT_INDEX",GROUP) NEXT I END RETURN ReturnState
I will have to test this to see it happening and then figure out how to proceed. Your code has me curious. What is the purpose of the ReturnState variable? I don't see anywhere where this gets changed.
Okay, this was easy to replicate and I concur that the pre-system CLOSE event handler does close the embedded window within the SRP Panel control. I am curious if this also affects forms that are embedded with SRP ShortcutBar controls as well.
I will ask Kevin to look into this but it may be out of our hands. My slightly unqualified guess is that clicking on the "X" caption button immediately sends a WM_CLOSE message to the parent window which also transitions down to all child windows. The controls themselves are direct children of the main form so they won't close themselves down. Your embedded form, however, doesn't have the same strict connection so it is very willing to respond to the WM_CLOSE message on its own.
You might very well be better off controlling this on your own. I would probably add a CLOSE event handler to the embedded form and have it check for a flag of some kind before it allows it self to be closed. During your CREATE event of the main form you could set a UDP on the embedded form. Then, when the main form is ready for the embedded form to close, it can reset the value of the UDP which will now allow it to close without interference.
Even the RemoveForm Send Message does not call Close.
Maybe another event is needed on the Panel, such as OnClose.
The issue is that the Panel has to force-destroy the embedded forms when the parent form is closing. This was necessary because embedded forms wouldn't close when closing the parent. Instead, clicking the X button would close embedded forms first, so users would have to click multiple times to close the main form. If you recall, we had the same problem with the Ribbon and Backstage forms once upon a time.
The side effect is that embedded forms simply cannot respond to CLOSE events. To even allow it would be to go back to multiple X clicks to close forms.
We are entering into the twilight zone, so to speak. We have stand-alone data-bound forms made to be children of a control, but they still want to act like stand-alone data-bound forms. It's a cart-before-the-horse kind of thing.
I suppose I could add a property that disables my code to force-close embedded forms. This would go back to requiring two or three X clicks to close a parent, at which point you'd be totally responsible for compensating for that anomaly.
In what way are you closing the embedded forms? Are you simply sending a CLOSE event, a WM_CLOSE message, or are you destroying the object? I am asking for two reasons:
I am using the DestroyWindow API. The reason is because I have do get rid of the window after the user clicks the X button but before the parent form knows it's closing. Otherwise, the WM_CLOSE message is sent to the embedded form instead. If I only send WM_CLOSE (instead of DestroyWindow), then it will stay alive long enough for the original problem of multiple clicks on X to manifest. DestroyWindow is the only solution.
Now that I think about it, it's not even possible to make a property disabling my logic. The original problem was always that clicking the X button closes the wrong the form. So, even if my logic is disabled, the main window will never know it's closed, so the developer can't respond to that either.
This is one of those cases where I created a tool as a solution to a small problem (wanting to place some controls in a scrollable pane), and the tool is being applied to new frontiers that just aren't panning out. The panel control is volatile in the sense that we are doing crazy things that defy the assumptions of OI's presentation server. There are some behaviors that are simply unavoidable as a result. I agree that, as we discover these limitations, they need to be documented.
Now, all of this has to do with placing stand-alone forms in a panel.
Colin,
What is the user case that has led to you placing databound forms in the Panel control? Did you need it for real estate? Is it part of some kind of dynamic form framework (something like an SDI)? Is it a popup? Perhaps knowing the problem can lead us to seeing how else the Panel can be used/modified to create a solution.
Thank you for the clear explanation. It helps to understand the nature of the beast.
I don't think Colin's embedded form was databound. His main issue is that the main form (which is either databound or simply employs a Save warning) is causing the embedded form to close automatically.
I do think the use case should be re-examined in light of present design dilemma. I know embedded forms are useful if only because they have use their own event handlers. So they can act as pseudo-objects.
There might be some value in creating a utility that reads the SYSREPOSWINS record and dynamically creates the controls and puts them in the SRP Panel control. Event handling would still be an issue.
I am not using a data bound form.
In fact I never use them, hate them with a passion.
I have a save warning on the embedded form.
I think there could be a simple solution, if the main form close just sent a message to the panel children.
If the main form close has to close regardless, its not a major problem to have the Save Yes/No option, rather than a Yes/No/Cancel, where cancel causes the Close to abort. If we just lose the cancel, no problem, it saves or it doesn't.
Colin
I have a main screen, with a nice ribbon.
The left shortcuts allow the user to select the view to update.
The right area is either a table for editing, or a panel form, depending on the selections made.
Some are just simple lists, so an SRP edit table suffices.
Some are complex so have a screen, but I then use the ribbon buttons to control, so it all looks seamless.
The form has a changed event to trigger a common variable if any changes are made.
The form has code on Close which prompts for save, if you run the form standalone (and cheat, as no buttons) it displays the dialog for save.
It seems that the main form is not triggering the Close event on the panel form, which is the issue here.
If there could be some event triggered, such as Close (obviously) or if not then even just sending an message to OMNIEVENT on the panel could be something we can pick up.
Yes, that much we can expect based on the implementation Kevin was required to use (DestroyWindow). This is just a variation of the original idea I proposed but in light of the prevailing issues this is not feasible.
Kevin,
As I recall, it is possible to trap the clicking of the non-client area (WM_NCHITTEST?). Would this offer Colin any means of trapping the click of the Close button before all of the automatic stuff kicks in?
Thanks, understood.
I seem to recall seeing a Rev post somewhere on how to hide or disable the Close button on the form (red X).
I just tried using the Close option from the ribbon System/Home menu, which is my own Close, and this calls the Close event and prompts to save happily.
So, if we just disable the Windows red X, the user has to close via the ribbon, and all would be OK.
A bit risky perhaps, but one option, especially if we just disable the red X if the panel exists, or it a change is made.
I will see about implementing and if this works and will advise.
Colin
X is disabled
User is forced to close via System menu.
Not that elegant, but removes the issue for now.
Disable Close
MF_BYPOSITION$=1024 hwnd=Get_Property( @window, 'HANDLE') system_menu_hwnd = GetSystemMenu ( hwnd, 0) rv=RemoveMenu (system_menu_hwnd, 6, MF_BYPOSITION$)
A new event called OnParentClosing has been added. This fires only when the user tries to close using Alt+F4 or the X button. It fires before the parent form even knows it's supposed to close. If you qualify it synchronously (setting field <4> to 2), then you can use the new Cancel property to Cancel the closing of the parent form.
Qualifying the event looks like this:
// Qualify parent closing event event Qualify = Yes$ Qualify<4> = 2 Send_Message(@Window:".OLE_PANEL", "QUALIFY_EVENT", "OLE.OnParentClosing", Qualify)
During the event, you cancel the closing of the window like so:Set_Property(@Window:".OLE_PANEL", "OLE.Cancel", 1)
Let me know if you have any questions or issues.Worked perfectly.
I added the qualify event, which triggered.
Then this sends a message to the child form to prompt to save, with YNC options.
Cancel triggers the OLE.Cancel, which aborts the close, and Y causes the save, and then carries on closing.
Some small changes were added.
Instead of calling the CLOSE event on the child form, which would be the normal place to check for changes on close, I called my OMNI for a separate event (my own) CHECK_SAVE which prompts, does the save as needed etc and exits, no close, as the close is already being dealt with.
There is another tricky one here (maybe just OI 8) but there is a bug in OI whereby Dialog levels over 2 fail to respond with messages. So as the parent contains the child, then the child calls MSG (as its own dialog) the MSG fails to return the responses from the buttons. To resolve this, the child form just needs to send the Parent window name to the MSG rather than the normal @Window, and the results are then properly returned.
Colin