The default font of the object inspector is ridiculously small, esp on a high resolution screen.
Is there a way to make it bigger?
Yes there is and it's really easy.
You can alter any window in the IDE by creating a package and installing this in the IDE.
Because the bpl gets loaded into the main process of the Delphi IDE you can alter any IDE window's properties from there.
Code by Mike Fletcher
Create a new package and add the following unit:
unit AdjustOIFont;
interface
uses Vcl.Forms, Vcl.Controls, Vcl.Dialogs, Vcl.StdCtrls;
procedure Register;
implementation
function GetOIForm: TForm;
var
i: Integer;
begin
Result:= nil;
for i:= 0 to Screen.FormCount - 1 do begin
if Screen.Forms[i].Name = 'PropertyInspector' then begin
Result:= Screen.Forms[I];
Exit;
end;
end;
end;
function GetChildControl(AParent: TWinControl; AName: string): TWinControl;
var
i: Integer;
begin
Result:= nil;
for i:= 0 to AParent.ControlCount - 1 do begin
if AParent.Controls[i].Name = AName then begin
Result:= TWinControl(AParent.Controls[i]);
Exit;
end;
end;
end;
function GetOIControl: TCustomListBox;
var
OIForm: TForm;
begin
OIForm:= GetOIForm;
Result:= TCustomListBox(GetChildControl(GetChildControl(OIForm, 'Panel3'), 'PropList'));
end;
procedure Register;
var
OI: TListBox;
OIForm: TForm;
begin
OIForm:= GetOIForm;
OIForm.Font.Size:= 10;
OI:= TListBox(GetOIControl);
OI.Font.Size:= 10;
OI.ItemHeight:= 20;
end;
end.
Build the package and install.
The change will take effect immediately.
Knowing this trick it's also be easy to collect all the enumerated names in a stringlist and copy them to the clipboard.
These names can than be used to expand the code and fix the fonts of other IDE elements as well (e.g. the Structure pane).
Much better.
Works on Seattle and XE7.
One way to achieving this is by modifying registry like it is described in Malcolm Groves article here: http://www.malcolmgroves.com/blog/?p=1804
Another option is to use Delphi IDE Colorizer which is a third party application designed to greatly change appearance of Delphi IDE by changing fonts, colors, etc. You can find it here: https://github.com/RRUZ/Delphi-IDE-Colorizer
And if you perhaps also want to change syntax fonts and syntax highlighting you can also check Delphi IDE Theme Editor which is designed to change the appearance of code highlighting based on your desires. You can find it here: https://github.com/RRUZ/delphi-ide-theme-editor
Related
Is there a good VCL Styles tutorial where we see how to dynamically (in run time) load/change the style ?
This should work with Delphi XE2 and up, since XE2 is the first version with VCL Styles.
I'm adding an answer because local information is often preferred to just links.
Here's the key facts you need to know before you start:
Many VCL controls have color properties, but those properties are going to get ignored when styles are on, and the default "common controls" like Button are going to get drawn by Delphi itself, instead of using the XP or Windows 2000 style that "comes with windows".
Somehow, deep within your application, VCL styles puts hooks in that take over painting your controls. Everything that it can handle, will be drawn using a "skin" on top of the regular controls. Many people call this "skinning the vcl", and prior to VCL styles, you might have found a third party skin system. Now it's built in.
Anything that is not hooked, will still get the regular style. So most third party controls, and some bits of the VCL will not be themed. Don't expect perfect instant results. Also, you might sometimes see some momentary flicker or glitches as a result of skinning, that's to be expected. Add loading of styles at runtime, and the end-quality of your result is anybody's guess. You can't necessarily guarantee that the style which is loaded at runtime, will contain everything you might want it to contain. Nor can you guarantee that with one you statically include in your app, but at least the ones you statically include could be verified by your QA team (which might be you).
And here's the simplest steps to get started: Really only step #2 through #4 are essential.
Click File -> New -> VCL Forms project.
Right click on the project options in the Project manager pane, and click properties. Navigate to Application -> Appearance
Click on a custom style to turn it on. (Amakrits is the first in my list, so I'll click that).
Click on the Default Style combobox and change it to something other than default.
Put something on your form so it's not empty. (A button, a listbox, etc).
Run your app.
Now, advanced stuff: Change your style at runtime:
I use this button click and formcreate to do that:
Add fdefaultStyleName:String; to private section of your form.
Make sure Vcl.Themes is in your uses clause.
procedure TForm1.Button1Click(Sender: TObject);
begin
if Assigned(TStyleManager.ActiveStyle) and (TStyleManager.ActiveStyle.Name<>'Windows') then begin
TStyleManager.TrySetStyle('Windows');
end else begin
TStyleManager.TrySetStyle(fdefaultStyleName); // whatever was in the project settings.
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
if Assigned(TStyleManager.ActiveStyle) then
fdefaultStyleName := TStyleManager.ActiveStyle.Name;
end;
A example (public procedure). Remember uses Vcl.Themes;
procedure TData.AllowSKIN( bSKIN:boolean );
var
sSKIN:string;
begin
sSKIN := 'Aqua Light Slate';
if not bSKIN then sSKIN := 'Windows';
TStyleManager.TrySetStyle( sSKIN );
end;
I have a (template) form that I call in my application to let user set skins. Simply ShowSkinForm to show the form. Also you can call LoadLastSkin during application initialization to have the last skin automatically applied.
UNIT FormSkinsDisk;
{-----------------
2017.02.23
Universal skin loader. Loads skins from disk (vsf file)
To use it:
Application.ShowMainForm:= FALSE;
MainForm.Visible:= FALSE; // Necessary so the form won't flicker during skin loading at startup
LoadLastSkin (during application initialization)
MainForm.Show;
Skins should be present in the 'System\skins' folder
Skins folder:
c:\Users\Public\Documents\Embarcadero\Studio\15.0\Styles\
KNOWN BUG:
TStyleManager.IsValidStyle always fails if Vcl.Styles is not in the USES list!! http://stackoverflow.com/questions/30328644/how-to-check-if-a-style-file-is-already-loaded
-------------------------------------------------------------------------------------------------------------}
INTERFACE {$WARN GARBAGE OFF} {Silence the: 'W1011 Text after final END' warning }
USES
System.SysUtils, Vcl.Controls, Vcl.Forms, Vcl.StdCtrls, System.Classes, System.Types;
TYPE
TfrmSkinsDisk = class(TForm)
lBox: TListBox;
procedure FormCreate (Sender: TObject);
procedure FormDestroy (Sender: TObject);
procedure lBoxClick (Sender: TObject);
procedure FormClose (Sender: TObject; var Action: TCloseAction);
procedure lblTopClick (Sender: TObject);
private
procedure FillLstBox;
public
end;
procedure LoadLastSkin(CONST DefaultSkin: string= ''); { On first run, set the DefaultSkin to an existing file (no path) like: 'Graphite Green.vsf'. Leave it empty if you want the default Windows theme to load }
procedure ShowSkinForm;
IMPLEMENTATION {$R *.dfm}
USES
IOUtils, Vcl.Styles, cIO, vcl.Themes, cINIFile, cINIFileEx, CubicTPU; {VCL.Styles is mandatory here}
VAR
SkinFile: string; { Disk short file name (not full path) for the current loaded skin }
CONST
DefWinTheme= 'Windows default theme';
{-----------------------------------------------------------------------------------------
UTILS
-----------------------------------------------------------------------------------------}
function GetSkinDir: string;
begin
Result:= GetAppSysDir+ 'skins\';
end;
function LoadSkinFromFile(CONST DiskShortName: string): Boolean;
VAR Style : TStyleInfo;
begin
Result:= FileExists(GetSkinDir+ DiskShortName);
if Result then
if TStyleManager.IsValidStyle(GetSkinDir+ DiskShortName, Style)
then
if NOT TStyleManager.TrySetStyle(Style.Name, FALSE)
then
begin
TStyleManager.LoadFromFile(GetSkinDir+ DiskShortName);
TStyleManager.SetStyle(Style.Name);
end
else Result:= FALSE
else
MesajError('Style is not valid: '+ GetSkinDir+ DiskShortName);
end;
procedure LoadLastSkin(CONST DefaultSkin: string= '');
begin
SkinFile:= cINIFile.ReadString('LastDiskSkin', DefaultSkin); { This is a relative path so the skin can still be loaded when the application is moved to a different folder }
if SkinFile = ''
then SkinFile:= DefaultSkin;
if (SkinFile > '')
AND (SkinFile <> DefWinTheme) { DefWinTheme represents the default Windows theme/skin. In other words don't load any skin file. Let Win skin the app }
then LoadSkinFromFile(SkinFile);
end;
procedure ShowSkinForm;
VAR
frmSkins: TfrmSkinsDisk;
begin
frmSkins:= TfrmSkinsDisk.Create(NIL);
frmSkins.ShowModal;
FreeAndNil(frmSkins);
end;
{----------------------------------------------------------------------------------------
CREATE
-----------------------------------------------------------------------------------------}
procedure TfrmSkinsDisk.FormCreate(Sender: TObject);
begin
LoadForm(Self);
FillLstBox; { Populate skins }
end;
procedure TfrmSkinsDisk.FormDestroy(Sender: TObject);
begin
SaveForm(Self);
cINIFile.WriteString ('LastDiskSkin', SkinFile);
end;
procedure TfrmSkinsDisk.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action:= caFree;
end;
{-----------------------------------------------------------------------------------------------------------------------
Populate skins
-----------------------------------------------------------------------------------------------------------------------}
procedure TfrmSkinsDisk.lblTopClick(Sender: TObject);
begin
FillLstBox;
end;
procedure TfrmSkinsDisk.FillLstBox; { Populate skins }
VAR
s, FullFileName: string;
begin
lBox.Items.Clear;
lBox.Items.Add(DefWinTheme); { This corresponds to Windows' default theme }
lblTop.Hint:= GetSkinDir;
if NOT DirectoryExists(GetSkinDir) then
begin
lblTop.Caption:= 'The skin directory could not be located! '+ GetSkinDir+ CRLF+ 'Add skins then click here to refresh the list.';
lblTop.Color:= clRedBright;
lblTop.Transparent:= FALSE;
EXIT;
end;
{ Display all *.vsf files }
for FullFileName in TDirectory.GetFiles(GetSkinDir, '*.vsf') DO
begin
s:= ExtractFileName(FullFileName);
lBox.Items.Add(s);
end;
end;
procedure TfrmSkinsDisk.lBoxClick(Sender: TObject);
begin
if lBox.ItemIndex < 0 then EXIT;
SkinFile:= lBox.Items[lBox.ItemIndex];
if SkinFile= DefWinTheme then
begin
TStyleManager.SetStyle('Windows');
SkinFile:= DefWinTheme;
end
else
if LoadSkinFromFile(SkinFile) then
begin
{ Bug fix } { fix for this bug: http://stackoverflow.com/questions/30328924/form-losses-modal-attribute-after-changing-app-style }
Application.ProcessMessages;
BringToFront;
end;
end;
end.
A word of warning: under current version (Sydney/10.4.2) skins are still terribly bugged. Using caFree on a skinned child form, might close your entire application.
Although I realise that in addition to the included Delphi docking demo there are other and better docking libraries available such as the Developer Express Library and the JVCL Docking Library, but for a specific demonstration project I am restricted to use only the Delphi built-in capability (despite some of the noted flaws).
My question relates to adding persistence to the docking state. I see from examining Controls.pas that TDockTree is the default dock manager and it has Stream I/O routines. Digging around on SO and in various forums though I cant see how anyone has called these routines. I've tried loading and saving to a file from the relevant Create and OnDrop events but I'm stabbing in the dark. I am happy saving and restoring form sizes and states but am struggling with the concepts of what I should be saving. Would any kind person give me a starting place?
I'm using Delphi XE3, so all (?) things are possible!
Many thanks.
I'm using Toolbar 2000 from J. Russels. It is providing panels, toolwindow's and toolbar's.
That one provides functions like TBRegSavePositions and TBRegSavePositions to store the user customization into registry.
Loading a "view" get's easily done by on code line:
TBRegLoadPositions(self, HKEY_CURRENT_USER, c_BaseUserRegKey);
in this case self is my form.
You can load and save your docking configuration with the LoadFromStream and SaveToStream methods by storing the data in a string.
Therefore, the following methods are required:
save the current docking configuration to a string
load the current docking configuration from a string
Here is some code to do this:
function GetDockString(const AManager: IDockManager): AnsiString;
var
LStream: TMemoryStream;
begin
LStream := TMemoryStream.Create();
try
AManager.SaveToStream(LStream);
SetLength(Result, 2 * LStream.Size);
BinToHex(LStream.Memory, PAnsiChar(Result), LStream.Size);
finally
FreeAndNil(LStream);
end;
end;
procedure ReadDockString(const ADockString: AnsiString; const AManager: IDockManager);
var
LStream: TMemoryStream;
begin
LStream := TMemoryStream.Create();
try
LStream.Size := Length(ADockString) div 2;
HexToBin(PAnsiChar(ADockString), LStream.Memory, LStream.Size);
LStream.Position := 0;
AManager.LoadFromStream(LStream);
finally
FreeAndNil(LStream);
end;
end;
I've used such methods in an application to create dockable windows, but the vcl provides only a very basic user experience. You can do something about it, but it is hard to test and debug - I already spent too much time to use and override TCustDockDragObject and TCaptionedTabDockTree, so I would recommend using a docking framework.
Here is a minimal example which creates two forms and reads a docking configuration.
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDblClick(Sender: TObject);
private
FPanel: TPanel;
end;
Implementation:
procedure TForm1.FormCreate(Sender: TObject);
var
LWindow: TForm;
const
LDockExample = '0000080000000000000000000000000000000000000000000000000100000000000000000B0000004368696C6457696E646F77FFFFFFFF';
begin
FPanel := TPanel.Create(Self);
FPanel.Align := alTop;
FPanel.Height := 300;
FPanel.DockSite := true;
FPanel.Parent := Self;
LWindow := TForm.CreateNew(Self);
LWindow.Name := 'ChildWindow';
LWindow.DragKind := dkDock;
LWindow.BoundsRect:=Rect(10, 10, 400, 400);
LWindow.Color := clGreen;
LWindow.Show;
ReadDockString(LDockExample, FPanel.DockManager);
end;
procedure TForm1.FormDblClick(Sender: TObject);
begin
ShowMessage(GetDockString(FPanel.DockManager));
end;
Is there a good VCL Styles tutorial where we see how to dynamically (in run time) load/change the style ?
This should work with Delphi XE2 and up, since XE2 is the first version with VCL Styles.
I'm adding an answer because local information is often preferred to just links.
Here's the key facts you need to know before you start:
Many VCL controls have color properties, but those properties are going to get ignored when styles are on, and the default "common controls" like Button are going to get drawn by Delphi itself, instead of using the XP or Windows 2000 style that "comes with windows".
Somehow, deep within your application, VCL styles puts hooks in that take over painting your controls. Everything that it can handle, will be drawn using a "skin" on top of the regular controls. Many people call this "skinning the vcl", and prior to VCL styles, you might have found a third party skin system. Now it's built in.
Anything that is not hooked, will still get the regular style. So most third party controls, and some bits of the VCL will not be themed. Don't expect perfect instant results. Also, you might sometimes see some momentary flicker or glitches as a result of skinning, that's to be expected. Add loading of styles at runtime, and the end-quality of your result is anybody's guess. You can't necessarily guarantee that the style which is loaded at runtime, will contain everything you might want it to contain. Nor can you guarantee that with one you statically include in your app, but at least the ones you statically include could be verified by your QA team (which might be you).
And here's the simplest steps to get started: Really only step #2 through #4 are essential.
Click File -> New -> VCL Forms project.
Right click on the project options in the Project manager pane, and click properties. Navigate to Application -> Appearance
Click on a custom style to turn it on. (Amakrits is the first in my list, so I'll click that).
Click on the Default Style combobox and change it to something other than default.
Put something on your form so it's not empty. (A button, a listbox, etc).
Run your app.
Now, advanced stuff: Change your style at runtime:
I use this button click and formcreate to do that:
Add fdefaultStyleName:String; to private section of your form.
Make sure Vcl.Themes is in your uses clause.
procedure TForm1.Button1Click(Sender: TObject);
begin
if Assigned(TStyleManager.ActiveStyle) and (TStyleManager.ActiveStyle.Name<>'Windows') then begin
TStyleManager.TrySetStyle('Windows');
end else begin
TStyleManager.TrySetStyle(fdefaultStyleName); // whatever was in the project settings.
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
if Assigned(TStyleManager.ActiveStyle) then
fdefaultStyleName := TStyleManager.ActiveStyle.Name;
end;
A example (public procedure). Remember uses Vcl.Themes;
procedure TData.AllowSKIN( bSKIN:boolean );
var
sSKIN:string;
begin
sSKIN := 'Aqua Light Slate';
if not bSKIN then sSKIN := 'Windows';
TStyleManager.TrySetStyle( sSKIN );
end;
I have a (template) form that I call in my application to let user set skins. Simply ShowSkinForm to show the form. Also you can call LoadLastSkin during application initialization to have the last skin automatically applied.
UNIT FormSkinsDisk;
{-----------------
2017.02.23
Universal skin loader. Loads skins from disk (vsf file)
To use it:
Application.ShowMainForm:= FALSE;
MainForm.Visible:= FALSE; // Necessary so the form won't flicker during skin loading at startup
LoadLastSkin (during application initialization)
MainForm.Show;
Skins should be present in the 'System\skins' folder
Skins folder:
c:\Users\Public\Documents\Embarcadero\Studio\15.0\Styles\
KNOWN BUG:
TStyleManager.IsValidStyle always fails if Vcl.Styles is not in the USES list!! http://stackoverflow.com/questions/30328644/how-to-check-if-a-style-file-is-already-loaded
-------------------------------------------------------------------------------------------------------------}
INTERFACE {$WARN GARBAGE OFF} {Silence the: 'W1011 Text after final END' warning }
USES
System.SysUtils, Vcl.Controls, Vcl.Forms, Vcl.StdCtrls, System.Classes, System.Types;
TYPE
TfrmSkinsDisk = class(TForm)
lBox: TListBox;
procedure FormCreate (Sender: TObject);
procedure FormDestroy (Sender: TObject);
procedure lBoxClick (Sender: TObject);
procedure FormClose (Sender: TObject; var Action: TCloseAction);
procedure lblTopClick (Sender: TObject);
private
procedure FillLstBox;
public
end;
procedure LoadLastSkin(CONST DefaultSkin: string= ''); { On first run, set the DefaultSkin to an existing file (no path) like: 'Graphite Green.vsf'. Leave it empty if you want the default Windows theme to load }
procedure ShowSkinForm;
IMPLEMENTATION {$R *.dfm}
USES
IOUtils, Vcl.Styles, cIO, vcl.Themes, cINIFile, cINIFileEx, CubicTPU; {VCL.Styles is mandatory here}
VAR
SkinFile: string; { Disk short file name (not full path) for the current loaded skin }
CONST
DefWinTheme= 'Windows default theme';
{-----------------------------------------------------------------------------------------
UTILS
-----------------------------------------------------------------------------------------}
function GetSkinDir: string;
begin
Result:= GetAppSysDir+ 'skins\';
end;
function LoadSkinFromFile(CONST DiskShortName: string): Boolean;
VAR Style : TStyleInfo;
begin
Result:= FileExists(GetSkinDir+ DiskShortName);
if Result then
if TStyleManager.IsValidStyle(GetSkinDir+ DiskShortName, Style)
then
if NOT TStyleManager.TrySetStyle(Style.Name, FALSE)
then
begin
TStyleManager.LoadFromFile(GetSkinDir+ DiskShortName);
TStyleManager.SetStyle(Style.Name);
end
else Result:= FALSE
else
MesajError('Style is not valid: '+ GetSkinDir+ DiskShortName);
end;
procedure LoadLastSkin(CONST DefaultSkin: string= '');
begin
SkinFile:= cINIFile.ReadString('LastDiskSkin', DefaultSkin); { This is a relative path so the skin can still be loaded when the application is moved to a different folder }
if SkinFile = ''
then SkinFile:= DefaultSkin;
if (SkinFile > '')
AND (SkinFile <> DefWinTheme) { DefWinTheme represents the default Windows theme/skin. In other words don't load any skin file. Let Win skin the app }
then LoadSkinFromFile(SkinFile);
end;
procedure ShowSkinForm;
VAR
frmSkins: TfrmSkinsDisk;
begin
frmSkins:= TfrmSkinsDisk.Create(NIL);
frmSkins.ShowModal;
FreeAndNil(frmSkins);
end;
{----------------------------------------------------------------------------------------
CREATE
-----------------------------------------------------------------------------------------}
procedure TfrmSkinsDisk.FormCreate(Sender: TObject);
begin
LoadForm(Self);
FillLstBox; { Populate skins }
end;
procedure TfrmSkinsDisk.FormDestroy(Sender: TObject);
begin
SaveForm(Self);
cINIFile.WriteString ('LastDiskSkin', SkinFile);
end;
procedure TfrmSkinsDisk.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action:= caFree;
end;
{-----------------------------------------------------------------------------------------------------------------------
Populate skins
-----------------------------------------------------------------------------------------------------------------------}
procedure TfrmSkinsDisk.lblTopClick(Sender: TObject);
begin
FillLstBox;
end;
procedure TfrmSkinsDisk.FillLstBox; { Populate skins }
VAR
s, FullFileName: string;
begin
lBox.Items.Clear;
lBox.Items.Add(DefWinTheme); { This corresponds to Windows' default theme }
lblTop.Hint:= GetSkinDir;
if NOT DirectoryExists(GetSkinDir) then
begin
lblTop.Caption:= 'The skin directory could not be located! '+ GetSkinDir+ CRLF+ 'Add skins then click here to refresh the list.';
lblTop.Color:= clRedBright;
lblTop.Transparent:= FALSE;
EXIT;
end;
{ Display all *.vsf files }
for FullFileName in TDirectory.GetFiles(GetSkinDir, '*.vsf') DO
begin
s:= ExtractFileName(FullFileName);
lBox.Items.Add(s);
end;
end;
procedure TfrmSkinsDisk.lBoxClick(Sender: TObject);
begin
if lBox.ItemIndex < 0 then EXIT;
SkinFile:= lBox.Items[lBox.ItemIndex];
if SkinFile= DefWinTheme then
begin
TStyleManager.SetStyle('Windows');
SkinFile:= DefWinTheme;
end
else
if LoadSkinFromFile(SkinFile) then
begin
{ Bug fix } { fix for this bug: http://stackoverflow.com/questions/30328924/form-losses-modal-attribute-after-changing-app-style }
Application.ProcessMessages;
BringToFront;
end;
end;
end.
A word of warning: under current version (Sydney/10.4.2) skins are still terribly bugged. Using caFree on a skinned child form, might close your entire application.
Using Delphi XE2, you have the option to embed custom styles (skins) to a VCL project.
Everything works fine. Now I have some forms into a separated dll that I show dynamically.
Of course those are not skinned. How can I rectify that?
I guess I must do some call to TVisualStyle somehow, but no luck.
The host:
procedure TForm1.Button1Click(Sender: TObject);
var
l: THandle;
p: procedure (const h: THandle); stdcall;
begin
l:= LoadLibrary('project1.dll');
if l > 0 then
begin
#p:= GetProcAddress(l,'ShowIt');
p(Application.Handle);
FreeLibrary(l);
end;
end;
The dll:
procedure ShowIt(const h: THandle);stdcall;
var
form: TForm;
b: TButton;
han: THandle;
begin
han:= Application.Handle;
Application.Handle:= h;
form :=Tform.Create(Application);
b:= TButton.Create(form);
b.Parent:= form;
b.Caption:= 'ytes';
b.Left:= 2;
b.Top:= 2;
form.ShowModal;
form.Release;
Application.Handle:= han;
end;
exports ShowIt ;
begin
end.
Pretty standard stuff. Now, what exactly must be done to make the dll form use the host's style theme?
You have two distinct instances of the VCL. You have set the style in the StyleServices instance owned by the executable, but your DLL has no knowledge of that. You could solve this by either:
Passing the style settings to a function in your DLL that applies those settings to the other StyleServices instance.
Use packages so that you only have a single VCL instance.
I had a lot of trouble with this and it was because I was using themes rather than VCL.THEMES and VCL.STYLES.
Delphi was throwing up a customeStyleException saying "style not found" or EcustomStyleException "Feature not supported by this style"
This question already has answers here:
How do I make my GUI behave well when Windows font scaling is greater than 100%
(4 answers)
Closed 8 years ago.
Which do you think are best practices for making a windows dialog compatible both with standard fonts (96 dpi) and "large fonts" setting (120 dpi) so that objects don't overlap or get cut off?
BTW: Just in case it's relevant, I'm interested in doing this for Delphi dialogs.
Thanks in advance!
In general one should use layout managers for this purpose. That what they are designed for.
Delphi (did not work with it for a long time) does not have such managers but is able to handle different dpi ever since. You have to use the autosize propery of the components to ensure that they have the right size for the text they display. To prevent overlapping of components arrange them on the form using the alignment and anchor properties. Eventually you have to group components in containers to achieve a proper layout.
There's a pretty good article in the D2007 help file, under "Considerations When Dynamically Resizing Forms and Controls" (note that the URL is to the help file itself, and not a web page as such).
The same topic, under the same name, can be found in the D2010 help file (same caveat about the URL as above), or on the docwiki.
It also is worthwhile (at least a little bit) to examine TForm.Scaled and TForm.ScaleBy.
This is how I try to deal with Delphi VCL's pixels regardless of Window's font size setting.
unit App.Screen;
interface
uses Controls;
type
TAppScreen = class(TObject)
private
FDefaultPixelsPerInch: integer;
FPixelsPerInch: integer;
function GetPixelsPerInch: integer;
procedure SetPixelsPerInch(const Value: integer);
public
procedure AfterConstruction; override;
function DefaultPixelsPerInch: integer;
function InAcceptableRange(const aPPI: integer): boolean;
procedure ScaleControl(const aControl: TWinControl);
property PixelsPerInch: integer read GetPixelsPerInch write SetPixelsPerInch;
end;
TAppScreenHelper = class helper for TAppScreen
private
class var FInstance: TAppScreen;
class function GetInstance: TAppScreen; static;
public
class procedure Setup;
class procedure TearDown;
class property Instance: TAppScreen read GetInstance;
end;
implementation
uses
TypInfo, Windows, SysUtils, Forms, Graphics;
type
TScreenEx = class(TScreen)
published
property PixelsPerInch;
end;
TScreenHelper = class helper for TScreen
public
procedure SetPixelsPerInch(Value: integer);
end;
procedure TScreenHelper.SetPixelsPerInch(Value: integer);
begin
PInteger(Integer(Self) + (Integer(GetPropInfo(TScreenEx, 'PixelsPerInch').GetProc) and $00FFFFFF))^ := Value;
end;
procedure TAppScreen.AfterConstruction;
begin
inherited;
FDefaultPixelsPerInch := Screen.PixelsPerInch;
FPixelsPerInch := FDefaultPixelsPerInch;
end;
function TAppScreen.DefaultPixelsPerInch: integer;
begin
Result := FDefaultPixelsPerInch;
end;
function TAppScreen.GetPixelsPerInch: integer;
begin
Result := FPixelsPerInch;
end;
function TAppScreen.InAcceptableRange(const aPPI: integer): boolean;
begin
if DefaultPixelsPerInch > aPPI then
Result := DefaultPixelsPerInch * 0.55 < aPPI
else if DefaultPixelsPerInch < aPPI then
Result := DefaultPixelsPerInch * 1.55 > aPPI
else
Result := True;
end;
procedure TAppScreen.ScaleControl(const aControl: TWinControl);
begin
aControl.ScaleBy(PixelsPerInch, DefaultPixelsPerInch);
end;
procedure TAppScreen.SetPixelsPerInch(const Value: integer);
begin
FPixelsPerInch := Value;
Screen.SetPixelsPerInch(FPixelsPerInch);
end;
class function TAppScreenHelper.GetInstance: TAppScreen;
begin
if FInstance = nil then
FInstance := TAppScreen.Create;
Result := FInstance;
end;
class procedure TAppScreenHelper.Setup;
begin
TAppScreen.Instance;
end;
class procedure TAppScreenHelper.TearDown;
begin
FInstance.Free;
FInstance := nil;
end;
initialization
TAppScreen.Setup;
finalization
TAppScreen.TearDown;
end.
Try the following to test the effects of different pixels value:
TAppScreen.Instance.PixelsPerInch := 120;
TAppScreen.Instance.PixelsPerInch := 96;
TAppScreen.Instance.PixelsPerInch := 150;
You should change the PixelsPerInch before instantiate TForm's descendant including Delphi's VCL dialogs.
Never put a control and its describing label side by side, always put the label on top of it.
But apart from that? Maybe:
Leave enough space to the right and bottom of labels so they will not overlap with other controls when large fonts are used.
I have never tried using TLabeledEdit in that scenario, maybe they do that automatically?
There are purported commercial solutions (Developer Express VCL Layout Manager). But I do not trust any of them. I suspect that Embarcadero should address this as a critical weakness in the current UI component set (VCL).
I think that the third-party component set might be your fastest solution right now. It's commercial but not hugely expensive.
http://www.devexpress.com/products/VCL/ExLayoutControl/