Forms & Dialogs

From Team Developer SqlWindows Wiki
Revision as of 12:07, 29 September 2017 by DaveRabelink (Talk | contribs)

Jump to: navigation, search

Form Window & DialogBox


Pointer2.png How to drag a window without a caption Pointer.png


MediaWikiLink.png How to drag a window without a caption

Pointer2.png How to force a window on the taskbar Pointer.png

When a top-level window is created without a specific parent (hWndNULL) the window will be visible on the Windows taskbar.
Windows created using a parent, will not be shown.

To be able to use a parent while creating a window AND having it placed on the taskbar, use the following:
On creation of the window, set the GWL_EXSTYLE to WS_EX_APPWINDOW.

Beware, a top level window with accessories enabled has a different internal window hierarchy compared to
a window without accessories.
For top level windows having accessories, hWndForm should be changed to the parent of hWndForm.
You can not use SalParentWindow, that function will return the owner of the window.
You need to call GetParent from WinApi as declared above.

So, declare these external functions and system constants:

Library name: USER32.DLL
   Function: GetWindowLongA
      Export Ordinal: 0
         Number: LONG
         Window Handle: HWND
         Number: INT

   Function: SetWindowLongA
      Export Ordinal: 0
         Number: LONG
         Window Handle: HWND
         Number: INT
         Number: LONG

   Function: GetParent
      Export Ordinal: 0
         Window Handle: HWND
         Window Handle: HWND

      Number: GWL_EXSTYLE	= -20
      Number: WS_EX_APPWINDOW	= 0x00040000

For top level windows without accessories enabled, do this:
set the WS_EX_APPWINDOW style to the window (hWndForm) at creation:

On SAM_Create
   Call SetWindowLongA( hWndForm, GWL_EXSTYLE, GetWindowLongA( hWndForm, GWL_EXSTYLE ) | WS_EX_APPWINDOW )

For top level windows WITH accessories enabled, do this:
set the WS_EX_APPWINDOW style to the parent window of hWndForm at creation:

On SAM_Create
   Call SetWindowLongA( GetParent( hWndForm ), GWL_EXSTYLE, GetWindowLongA( GetParent( hWndForm ), GWL_EXSTYLE ) | WS_EX_APPWINDOW )

The window will then be shown on the taskbar.

Here you can download a sample:


Pointer2.png Simulating a 'modal' form window Pointer.png

For dialogs we have flavors 'Modal' and 'Modeless'.
A modal dialog is created using SalModalDialog. The execution will continue after the dialog has been closed.
The modeless dialog is created using SalCreateWindow which will continue execution after the dialog is created.

We do not have a 'modal' form window type, they are for default always modeless. So your application will not wait until the form is closed.

But why not use a modal dialog when we need a window to "halt" your execution until it is closed?
Well, there are some differences between dialogs and form windows. One of them is that a dialog does not have a menu strip and does not have
a system menu (minimize, maximize).
So when using a modal dialog, you will have to take those "limitations".

Using this simple trick, we can "simulate" a modal form window:

First create a modal dialog and hide it. On the SAM_CreateComplete of that modal dialog, you create the needed form window.
Because the modal dialog is still open, the execution will wait until the hidden modal dialog is closed.
Now, when closing the form window, notify the hidden dialog that the form has been closed. Then the dialog will end itself.
After the hidden dialog has closed, the execution will continue.

This may come in handy when you have a form window which is used in different workflows. For instance, a form window is used to display images.
In one workflow you need to create a floating window in your application, while the user is executing other tasks. In this case you need a "normal" form window.
In the other workflow, you want to display the image using the same form, but now the application should wait until the form is closed.
By using this trick you can achieve this, so reusing the same form as "modeless" and "modal" flavors.

Here you can download a sample:


Pointer2.png Prevent window disable when modal dialog is created Pointer.png

When a modal dialog is created, all top level windows currently open (forms/dialogs) are disabled until the modal dialog is closed.
You might want specific windows to remain accessible, even when a modal dialog is created.
For instance, if you have a floating window having functions which should always be accessible by the user, independent of modal dialogs.
Or you have a log/trace window which should never be disabled by the application workflow.

With a fairly simple trick you are able to prevent windows being disabled on creation of modal dialogs.

On creation of a modal dialog, every top level window which is currently open will be disabled and will receive the Windows API message : WM_ENABLE. This message is to inform the window that it has been disabled or enabled.

The trick is just to enable the window when it receives WM_ENABLE on indication that the window was disabled.
First define the Windows message constant:

      Number: WM_ENABLE = 0x000A

Then code under message actions for the window which should never be disabled:

   If wParam = FALSE
      Call SalEnableWindow( hWndForm )

(The wParam contains TRUE when the window was enabled and FALSE when it was disabled).

This is enough to prevent the top level window ever being disabled.
But we have to take messageboxes into account.
When a SalMessageBox is created, it normally disables all open windows until the messagebox is closed.
When it is needed to retain this feature, so a window will be disabled when a messagebox is displayed, we need to code something extra.

We create a wrapper function for SalMessageBox, called PALMessageBox. This custom function will set a global variable to TRUE just before it calls SalMessageBox.
So when the messagebox is opened and WM_ENABLE will be processed by all other open windows, we can query the global boolean to see if a messagebox was the reason for the disable.
When this is the case, we do not enable the window to keep it disabled as expected:

   If wParam = FALSE AND NOT gbMessageBoxOpen
      Call SalEnableWindow( hWndForm )

Unfortunately we have another issue to consider: when a window has a frame (hWndFrame).
On TD 5.1 and up, forms and dialogs having accessories enabled (toolbar, statusbar) will have a frame window around the normal window.
And in this case, the WM_ENABLE is send to the frame window instead of the window itself.
That means, we are unable to code the WM_ENABLE message under message actions, as this will only process messages on hWndForm and not hWndFrame.

To solve this, we need to subclass WM_ENABLE on hWndFrame and send it to hWndForm using subclasser.
When a window is created and it has a frame, do this to subclass the message:

On SAM_Create
   Call RegSubclass( hWndFrame, WM_ENABLE, hWndForm, PAM_WMENABLE_SubClassed, SUBCLASS_FLAG_AFTER )

This will relay the WM_ENABLE message to hWndForm and will be relayed as the custom user message PAM_WMENABLE_SubClassed.
We code this message then under message actions, just as WM_ENABLE.
Then a framed window will react like expected.

Here you can download a sample (ANSI and UNICODE):