I have many applications written using Delphi 6, which I have no desire
to migrate from as it is fast and stable. It occurred to me that an
easier way than modifying the code of each application to make it Vista
ready would be to make a TForm descendant, called say TVistaForm, and
change my main form to use this instead. I have produced the following unit:
unit VistaForms;
interface
uses
Windows, Messages, SysUtils, Classes, Controls, StdCtrls, Forms;
type
TVistaForm = class(TForm)
protected
procedure CreateParams(var Params: TCreateParams); override;
procedure WMSyscommand(var Message: TWmSysCommand);
message WM_SYSCOMMAND;
public
constructor Create(AOwner: TComponent); override;
end;
implementation
procedure TVistaForm.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
Params.ExStyle := Params.ExStyle and not WS_EX_TOOLWINDOW or
WS_EX_APPWINDOW;
end;
procedure TVistaForm.WMSyscommand(var Message: TWmSysCommand);
begin
case (Message.CmdType and $FFF0) of
SC_MINIMIZE:
begin
ShowWindow(Handle, SW_MINIMIZE);
Message.Result := 0;
end;
SC_RESTORE:
begin
ShowWindow(Handle, SW_RESTORE);
Message.Result := 0;
end;
else
inherited;
end;
end;
constructor TVistaForm.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
ShowWindow(Application.Handle, SW_HIDE);
SetWindowLong(Application.Handle, GWL_EXSTYLE,
GetWindowLong(Application.Handle, GWL_EXSTYLE) and not
WS_EX_APPWINDOW or WS_EX_TOOLWINDOW);
ShowWindow(Application.Handle, SW_SHOW);
end;
end.
which seems to work. Just change the main form declaration from TForm to
TVistaForm. But what I want to add to this is some code to iterate
through all the controls on the form changing the font name of all those
that are set to MS Sans Serif to the system font, because as you know,
if you change any font attribute (by making it bold, say) ParentFont
becomes false, and that control does not automatically use the system
font, and it's a real pain finding all of these and changing them manually.
Does anyone know a good way to do this? My first attempt failed,
presumably because some controls are on tabs or status bars and are
therefore not in the Controls array of the form itself. So I also need
to iterate the controls of the controls that can have child controls,
and I can see the code becoming horribly messy.
I haven't looked yet at how to deal with the problem of modal dialogs
disappearing behind the main form, as mentioned in the article. Using
Delphi 6, I don't have the form property PopupParent to play with.
Again, I'd like to create a TForm descendant with the necessary
functionality so that I can just change each form's class instead of
inserting code each time it is called.
Hopefully, this approach may result in something that many people would
find useful, and not only those using Delphi 6, so I'm hoping that
others out there are willing to contribute solutions to these problems,
because I'm far from being an expert at this kind of programming.
--
Julian Moss
Tech-Pro Limited
http://www.tech-pro.net
> But what I want to add to this is some code to iterate through all the
> controls on the form changing the font name of all those that are set to
> MS Sans Serif to the system font, because as you know, if you change
> any font attribute (by making it bold, say) ParentFont becomes false,
> and that control does not automatically use the system font, and it's a
> real pain finding all of these and changing them manually.
Why not just set the Font for the form itself, and then set ParentFont to
true for all of the relavant child controls? Why update their Fonts
individually?
> Does anyone know a good way to do this? My first attempt failed,
> presumably because some controls are on tabs or status bars and are
> therefore not in the Controls array of the form itself.
Simply iterate through the controls recursively. Every windowed control has
its own Controls list.
> So I also need to iterate the controls of the controls that can have
> child controls, and I can see the code becoming horribly messy.
Not really. Recursion is very simple to implement, ie:
procedure TVistaForm.UpdateControls(AParent: TWinControl);
var
I: Integer;
Ctrl: TControl;
begin
for I := 0 to AParent.ControlCount-1 do
begin
Ctrl := AParent.Controls[I];
// do something with Ctrl ...
if Ctrl is TWinControl then
UpdateControls(Ctrl as TWinControl);
end;
end;
procedure TVistaForm.DoSomething;
begin
UpdateControls(Self);
end;
Gambit
As OP mentioned, if you make any changes to the font properties
of a component, say changing the size or style of a label, ParentFont
becomes false and a change to the Forms's font name will not flow
through to those components.
I've seen this before, and it has a small problem: ShowWindow() simply
changes the window state, effectively preventing the associated system
sounds from playing (where enabled). I'd rather not have UI
inconsistencies, so I devised this solution:
procedure TTopLevelForm.wm_SysCommand(var M: TMessage);
begin
case (M.WParam and $FFF0) of
SC_MINIMIZE, SC_RESTORE, SC_MAXIMIZE:
M.Result := DefWindowProc(self.Handle, M.Msg, M.WParam, M.LParam);
else
inherited;
end;
end;
Which seems to work flawlessly.
> constructor TVistaForm.Create(AOwner: TComponent);
> begin
> inherited Create(AOwner);
> ShowWindow(Application.Handle, SW_HIDE);
> SetWindowLong(Application.Handle, GWL_EXSTYLE,
> GetWindowLong(Application.Handle, GWL_EXSTYLE) and not
> WS_EX_APPWINDOW or WS_EX_TOOLWINDOW);
> ShowWindow(Application.Handle, SW_SHOW);
> end;
You shouldn't be doing this here, IMO.
Plus, you're hiding and showing the app window, which I don't quite
understand. If I were you I'd remove the second ShowWindow() and replace
it with this:
Application.Title := '';
That is because the app window will still show in the Task Manager,
allowing the user to interact with it. With an empty Title, it will
be hidden. You don't need the app window anywhere, since your main form
will already appear on the Taskbar and Task list. Effectively, replacing
it.
> I haven't looked yet at how to deal with the problem of modal dialogs
> disappearing behind the main form, as mentioned in the article. Using
> Delphi 6, I don't have the form property PopupParent to play with.
> Again, I'd like to create a TForm descendant with the necessary
> functionality so that I can just change each form's class instead of
> inserting code each time it is called.
Maybe intercept window activation and pass the message to DefWindowProc.
Haven't tried it though.
> As OP mentioned, if you make any changes to the font properties
> of a component, say changing the size or style of a label, ParentFont
> becomes false and a change to the Forms's font name will not flow
> through to those components.
It will if the Form iterates through its children when its own font is
updated, setting their ParentFont properties back to True.
Gambit
But, unless I am mistaken, then all the customizations to the
other font properties will be reset to those of the ParentFont.
This would likely make a mess if significant changes to font
size, etc. were used enhance the layout and design of a form.
> But, unless I am mistaken, then all the customizations to the
> other font properties will be reset to those of the ParentFont.
> This would likely make a mess if significant changes to font
> size, etc. were used enhance the layout and design of a form.
Simply keep track of the Form's original Font settings. Whenever the
CM_FONTCHANGED message is received, loop through the children updating any
Fonts that match the Form's old Font, then update the tracking to be ready
for a future change.
Gambit
> Simply keep track of the Form's original Font settings. Whenever the
> CM_FONTCHANGED message is received, loop through the children updating any
> Fonts that match the Form's old Font, then update the tracking to be ready
> for a future change.
It is quite tricky. The standard TForm font is MS Sans Serif, size 8. If
you set DesktopFont := true the form font will become Segoe UI, size 9.
Any control where you changed the font color, or made it bold or
underlined, will have lost the ParentFont setting, so both the font name
and the size will need to be changed. Anything that is MS Sans Serif but
a different size, will just need the font name changed. There is no
setting that will do this automatically, you have got to iterate through
the children. Your earlier code fragment has given me a clearer idea of
how to do it, and I hadn't even thought of using CM_FONTCHANGED. Thanks!
> I've seen this before, and it has a small problem: ShowWindow() simply
> changes the window state, effectively preventing the associated system
> sounds from playing (where enabled).
Thanks for the suggested improvement. I'll try that. You're quite right:
since the point of this is to maintain the standard UI functionality
under Vista, we don't want to break some other functionality in the process.
>> constructor TVistaForm.Create(AOwner: TComponent);
>
> You shouldn't be doing this here, IMO.
I'm doing that there because the original article I referred to puts
this code in the Form.Create. I don't confess to understand how it
works, I just tried the suggestions in the article, and wanted to come
up with a way to make the changes that involved less editing.
Thanks for your other comments. Time for some experimentation, clearly.
Very simple, it just modifies the app window's characteristics so it
doesn't get listed on the Taskbar.
Then you have the CreateParams hack in the form, to change the form's
characteristics so it WILL get listed on the Taskbar.
This is so that the main form can take the place of the app window (thus
allowing Vista's Taskbar thumbnails feature to work).
<rant>
Personally, I'd rather CG get rid of the app window completely. It's a
relic of the 16-bit Windows days. There was no Taskbar then, so having
all forms belong to the app window made sense as far as a clean desktop
goes (windows minimized into icons on the desktop - more windows, more
icons; Delphi apps just needed one), but now (read: since Windows 95)
it's just restrictive. And of course with Vista, it actually interferes
with the Taskbar.
</rant>
> Not really. Recursion is very simple to implement, ie:
>
> procedure TVistaForm.UpdateControls(AParent: TWinControl);
> var
> I: Integer;
> Ctrl: TControl;
> begin
> for I := 0 to AParent.ControlCount-1 do
> begin
> Ctrl := AParent.Controls[I];
> // do something with Ctrl ...
> if Ctrl is TWinControl then
> UpdateControls(Ctrl as TWinControl);
> end;
> end;
"Do something with Ctrl" is harder than it looks. I don't see a common
class of all controls that have a Font property. Is there an easier way
to programmatically change the Font property of all the possible types
of control that be in the array, other than:
if Ctrl is TLabel then
TLabel(Ctrl).Font.Name := ...
else if Ctrl is TButton then
TButton(Ctrl).Font.Name L= ...
else if Ctrl is TEdit then
...
Julian Moss