TD 5.x/6.x/7.x miscellaneous
hWndFrame and messages: WM_SYSCOMMAND sample
With TD 5.1 and upwards, the new ProfUis layer has introduced a somewhat different window architecture in TD.
For the "normal" developer this is not seen in the IDE. A defined form window for instance is represented as one item in the outline.
In fact, we see this "template" AS the defined form, both in design time and at runtime.
But internally TD organizes the windows differently in some cases. So at runtime, the one window we expect could be made up of several parent/child window relationships.
In most cases, this is no problem. The internal structure is shielded from us developers when we use "normal" and common functionality.
Looking at a running window using a Spy tool reveals that a form window in TD consists of two actual windows.
A container window and as a child the actual form. These different windows handle different levels of window management.
With this new structure, TD introduced a new system variable, hWndFrame.
So in fact, when referring to your window you have two window handles: the good old hWndForm and the new hWndFrame.
When to use which?
Well that has not a clear answer. It depends on what you needs are of the window in question.
Setting a specific window flag using Windows API or Vis/Sal functions should sometimes be done using hWndForm and sometimes on hWndFrame.
Though having the new system variable gives us access to the properties of the frame window, it is obvious that a lot of (new) developers have issues with the new distinction between hWndForm and hWndFrame.
Sometimes it is just a way of trial and error. When a window flag does not work on hWndForm, a try on hWndFrame succeeds (or visa-versa).
Using a Spy tool actually helps a lot to inspect the window attributes to see if you need the hWndForm level or the hWndFrame level.
Though it is not very clear when to use which, we as developers have a way to access properties with the two system variables.
This is not the case with window messages.
As there is a frame window and a contained form, there are now two places where messages are send to.
Some are only send to the frame, some only to the form and some on both.
The issue in TD is that we only trap messages on hWndForm level. The message actions section is mainly the place where the messages can be trapped on the contained form.
Messages solely send to the hWndFrame level (and not passed to the contained form) are out of reach for us.
Coding any ON message under message actions are not fired when the message is received at hWndFrame level.
This is a real problem, because some of these messages are not only very useful in our apps but sometimes even needed.
In the older TD versions, the messages now send to hWndFrame are send to hWndForm. So old TD apps which are ported to TD 5.1 and upwards could have broken functionality.
An example is the WM_SYSCOMMAND message.
This message informs for example that one of the system buttons is clicked (eg minimize, maximize, restore).
By using the documented feature of this message the click on these buttons can be suppressed.
On older TD versions, you could have coded the WM_SYSCOMMAND message under the message actions section to let your app react on the click of one of these sysbuttons.
When porting this app to the new TD version, suddenly this feature does not work. That is because the WM_SYSCOMMAND message is received now on hWndFrame and not on hWndForm.
And there is no way to code anything under message actions to be informed about the firing of that message on hWndFrame.
This means that a lot of window features which are coded in the previous years could be not possible anymore. At least, not in a standard way in TD-Out-Of-The-Box.
Solution to this problem.
Fortunately there is a way to solve this problem. We can use subclassing !
By subclassing you can redirect messages from a specific window handle to another window handle.
This means that you are able to "steal" the receive of a message on window A and send it instead to Window B.
What we want in this case of WM_SYSCOMMAND: redirect this message from hWndFrame to hWndForm.
So then you are able to code the message under the form message section.
To subclass messages, we can use Subclasser which is created by Christian Schubert.
It consists of a library (apl) and a dll (Subclasser.dll).
To subclass the WM_SYSCOMMAND from hWndFrame and send it instead to hWndForm:
On SAM_Create ! On hWndFrame, the WM_SYSCOMMAND is received and not on the form message level ! So redirect the WM_SYSCOMMAND from hWndFrame (eat it) and send it as a custom message to this form level ! ! Subclass WM_SYSCOMMAND on hWndFrame level and send it as USR_SubclassedWmCommand to the form message level Call RegSubclass( hWndFrame, WM_SYSCOMMAND, hWndForm, USR_SubclassedWmCommand, SUBCLASS_FLAG_INSTEAD ) ! ! Now, any WM_SYSCOMMAND on hWndFrame will be received as USR_SubclassedWmCommand
Then you define the redirected message and code the needed actions there:
On USR_SubclassedWmCommand ! Redirected from hWndFrame by subclasser. Process as usual If BlockActionForm( wParam & 0xFFF0 ) ! Return FALSE to block this action Return FALSE Else ! The action should not be blocked. ! For correct further handling of this message, call the original default window procedure to process WM_SYSCOMMAND which is on hWndFrame Call DefWindowProcW( hWndFrame, WM_SYSCOMMAND, wParam, lParam )
And to cleanup, when the form is destroyed, unregister (unsubclass) the earlier subclassed message:
On SAM_Destroy ! When window is destroyed, the subclassed registration should be unregistered to clean up Call UnregSubclass( hWndFrame, WM_SYSCOMMAND )
So, with Subclasser you are able to redirect any message. The WM_SYSCOMMAND here described is only an example of this.
When there are other messages you need on hWndForm level which are in TD5.x and higher, use the same technique.
You can download the sample for subclassing WM_SYSCOMMAND from here:
To download Subclasser separately:
TD 6.1 SP4 and up: left aligned column checkboxes (CDK tool to change)
Starting from TD 6.1 SP4, table/grid columns having checkbox type show the checkbox left aligned by default.
In previous versions, the attribute "Justify" was present but did not work. The checkbox was always displayed centered,
even when you have set the attribute to "Left" or "Right".
Now this attribute is honored and will actually display the checkbox using the justification setting you provide.
But mostly, it makes no real sense to align left or right. The normal use is you want to display them centered.
Problem is when porting old TD code, it will mostly have the setting "Default", which is in fact "Left".
Columns will look different now, having the checkbox not in the center.
You will have to change all those columns to "Centered" to get the same look as in previous TD versions.
To help, a simple tool is created to automate this. Using the CDK it will load your source files, search for columns having "Check Box" type,
and set the justification to "centered".
BEWARE: DO NOT USE THIS ON YOUR SOURCES DIRECTLY.
MAKE A COPY OF ALL SOURCES IN A SEPARATE FOLDER.
RESPONSIBILITY IS YOURS!
- create a "process folder" and copy all your sources (app,apt,apl) there
- create an output folder (empty one)
- change the Init() function in the source of the tool so it will use these process and output folders
- press Start button to execute
The tool will list all files containing checkbox columns and will show their current setting.
When the setting is not "Centered" it will be changed. The source-files will be saved in the output folder.
Only changed source files will be saved in the output folder.
You can download the CDK tool (TD 6.x source) from here: